Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
228 changes: 224 additions & 4 deletions src/Cli/dotnet/commands/dotnet-test/VSTestArgumentConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Microsoft.DotNet.Cli
using System;
using System.Collections.Generic;
using System.Linq;
using static BlameArgs;

/// <summary>
/// Converts the given arguments to vstest parsable arguments
Expand Down Expand Up @@ -62,6 +63,8 @@ public List<string> Convert(string[] args, out List<string> ignoredArgs)
ignoredArgs = new List<string>();

string activeArgument = null;
BlameArgs blame = new BlameArgs();

foreach (var arg in args)
{
if (arg == "--")
Expand All @@ -77,6 +80,10 @@ public List<string> Convert(string[] args, out List<string> ignoredArgs)
{
ignoredArgs.Add(activeArgument);
}
else if (blame.IsBlameArg(activeArgument, null))
{
// do nothing, we process remaining arguments ourselves
}
else
{
newArgList.Add(activeArgument);
Expand All @@ -101,6 +108,12 @@ public List<string> Convert(string[] args, out List<string> ignoredArgs)
continue;
}

if (blame.IsBlameArg(argValues[0], argValues[1]))
{
blame.UpdateBlame(argValues[0], argValues[1]);
continue;
}

// Check if the argument is shortname
if (ArgumentMapping.TryGetValue(argValues[0].ToLower(), out var longName))
{
Expand All @@ -111,10 +124,18 @@ public List<string> Convert(string[] args, out List<string> ignoredArgs)
}
else
{
activeArgument = arg.ToLower();
if (ArgumentMapping.TryGetValue(activeArgument, out var value))
if (blame.IsBlameSwitch(arg))
{
activeArgument = value;
blame.UpdateBlame(arg, null);
activeArgument = arg;
}
else
{
activeArgument = arg.ToLower();
if (ArgumentMapping.TryGetValue(activeArgument, out var value))
{
activeArgument = value;
}
}
}
}
Expand All @@ -129,6 +150,10 @@ public List<string> Convert(string[] args, out List<string> ignoredArgs)
ignoredArgs.Add(activeArgument);
ignoredArgs.Add(arg);
}
else if (blame.IsBlameArg(activeArgument, arg))
{
blame.UpdateBlame(activeArgument, arg);
}
else
{
newArgList.Add(string.Concat(activeArgument, ":", arg));
Expand All @@ -138,7 +163,14 @@ public List<string> Convert(string[] args, out List<string> ignoredArgs)
}
else
{
newArgList.Add(arg);
if (blame.IsBlameArg(arg, null))
{
blame.UpdateBlame(arg, null);
}
else
{
newArgList.Add(arg);
}
}
}

Expand All @@ -148,12 +180,20 @@ public List<string> Convert(string[] args, out List<string> ignoredArgs)
{
ignoredArgs.Add(activeArgument);
}
else if( blame.IsBlameArg(activeArgument, null)) {
// do nothing, we process remaining arguments ourselves
}
else
{
newArgList.Add(activeArgument);
}
}

if (blame.Blame)
{
blame.AddCombinedBlameArgs(newArgList);
}

return newArgList;
}

Expand All @@ -172,4 +212,184 @@ private void UpdateVerbosity(string verbosity, List<string> newArgList)
newArgList.Add(verbosityString + verbosity);
}
}

class BlameArgs
{
public bool Blame = false;
public string LegacyBlame = null;

public bool CollectCrashDump = false;
public string CollectCrashDumpType = null;
public bool CollectCrashDumpAlways = false;

public bool CollectHangDump = false;
public string CollectHangDumpType = null;
public string CollectHangDumpTimeout = null;

public static string BlameParam = "--blame";
public static string BlameCrashParam = "--blame-crash";
public static string BlameCrashDumpTypeParam = "--blame-crash-dump-type";
public static string BlameCrashCollectAlwaysParam = "--blame-crash-collect-always";
public static string BlameHangParam = "--blame-hang";
public static string BlameHangDumpTypeParam = "--blame-hang-dump-type";
public static string BlameHangTimeoutParam = "--blame-hang-timeout";

// parameters that expect arguments
private readonly string[] _blameArgList = new string[]{
BlameCrashDumpTypeParam,

BlameHangDumpTypeParam,
BlameHangTimeoutParam
};

// parameters that don't expect any arguments
private readonly string[] _blameSwitchList = new string[]{
BlameParam,

BlameCrashParam,
BlameCrashCollectAlwaysParam,

BlameHangParam,
};


internal bool IsBlameArg(string parameter, string value)
{
return _blameArgList.Any(p => Eq(p, parameter)) || _blameSwitchList.Any(p => Eq(p, parameter));
}

private bool IsLegacyBlame(string parameter, string value)
{
// when provided --blame <value>, we do not want to process it any further
// most likely a legacy call, and the param is already in the format that vstest.console expects
return Eq(BlameParam, parameter) && !string.IsNullOrWhiteSpace(value);
}

internal bool IsBlame(string parameter)
{
return Eq(BlameParam, parameter);

}

internal bool IsBlameSwitch(string parameter)
{
return _blameSwitchList.Any(p => Eq(p, parameter));

}

internal void UpdateBlame(string parameter, string argument)
{
if (IsLegacyBlame(parameter, argument))
{
Blame = true;
LegacyBlame = argument;
}

if (Eq(parameter, BlameParam))
{
Blame = true;
}

// Any blame-crash param implies that we collect crash dump
if (Eq(parameter, BlameCrashParam))
{
Blame = true;
CollectCrashDump = true;
}

if (Eq(parameter, BlameCrashCollectAlwaysParam))
{
Blame = true;
CollectCrashDump = true;
CollectCrashDumpAlways = true;
}

if (Eq(parameter, BlameCrashDumpTypeParam))
{
Blame = true;
CollectCrashDump = true;
CollectCrashDumpType = argument;
}

// Any Blame-hang param implies that we collect hang dump
if (Eq(parameter, BlameHangParam))
{
Blame = true;
CollectHangDump = true;
}

if (Eq(parameter, BlameHangDumpTypeParam))
{
Blame = true;
CollectHangDump = true;
CollectHangDumpType = argument;
}

if (Eq(parameter, BlameHangTimeoutParam))
{
Blame = true;
CollectHangDump = true;
CollectHangDumpTimeout = argument;
}
}

private bool Eq(string left, string right)
{
return string.Equals(left, right, StringComparison.OrdinalIgnoreCase);
}

internal void AddCombinedBlameArgs(List<string> newArgList)
{
if (!Blame)
return;

if (!string.IsNullOrWhiteSpace(LegacyBlame))
{
// when legacy call is detected don't process
// any more parameters
newArgList.Add($"--blame:{LegacyBlame}");
return;
}

string crashDumpArgs = null;
string hangDumpArgs = null;

if (CollectCrashDump)
{
crashDumpArgs = "CollectDump";
if (CollectCrashDumpAlways)
{
crashDumpArgs += ";CollectAlways=true";
}

if (!string.IsNullOrWhiteSpace(CollectCrashDumpType))
{
crashDumpArgs += $";DumpType={CollectCrashDumpType}";
}
}

if (CollectHangDump)
{
hangDumpArgs = "CollectHangDump";
if (!string.IsNullOrWhiteSpace(CollectHangDumpType))
{
hangDumpArgs += $";DumpType={CollectHangDumpType}";
}

if (!string.IsNullOrWhiteSpace(CollectHangDumpTimeout))
{
hangDumpArgs += $";TestTimeout={CollectHangDumpTimeout}";
}
}

if (CollectCrashDump || CollectHangDump)
{
newArgList.Add($@"--blame:""{string.Join(";", crashDumpArgs, hangDumpArgs).Trim(';')}""");
}
else
{
newArgList.Add("--blame");
}
}
}
}
42 changes: 29 additions & 13 deletions src/Tests/dotnet.Tests/ParserTests/VSTestArgumentConverterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public void ConvertArgsShouldConvertValidArgsIntoVSTestParsableArgs(string input
// Act
var convertedArgs = new VSTestArgumentConverter().Convert(args, out var ignoredArgs);

convertedArgs.Should().BeEquivalentTo(convertedArgs);
convertedArgs.Should().BeEquivalentTo(expectedArgs);
ignoredArgs.Should().BeEmpty();
}

Expand All @@ -35,7 +35,7 @@ public void ConvertArgshouldConvertsVerbosityArgsIntoVSTestParsableArgs(string i
// Act
var convertedArgs = new VSTestArgumentConverter().Convert(args, out var ignoredArgs);

convertedArgs.Should().BeEquivalentTo(convertedArgs);
convertedArgs.Should().BeEquivalentTo(expectedArgs);
ignoredArgs.Should().BeEmpty();
}

Expand All @@ -50,7 +50,7 @@ public void ConvertArgsShouldIgnoreKnownArgsWhileConvertingArgsIntoVSTestParsabl
// Act
var convertedArgs = new VSTestArgumentConverter().Convert(args, out var ignoredArgs);

convertedArgs.Should().BeEquivalentTo(convertedArgs);
convertedArgs.Should().BeEquivalentTo(expectedArgs);
Assert.Equal(expIgnoredArgs, ignoredArgs);
}

Expand All @@ -69,20 +69,36 @@ public static class DataSource
{
public static IEnumerable<object[]> ArgTestCases { get; } = new List<object[]>
{
new object[] { "-h", "--help" },
new object[] { "sometest.dll -s test.settings", "sometest.dll --settings:test.settings" },
new object[] { "sometest.dll -t", "sometest.dll --listtests" },
new object[] { "sometest.dll --list-tests", "sometest.dll --listtests" },
new object[] { "sometest.dll --filter", "sometest.dll --testcasefilter" },
new object[] { "sometest.dll -l trx", "sometest.dll --logger:trx" },
new object[] { "sometest.dll -a c:\adapterpath\temp", "sometest.dll --testadapterpath:c:\adapterpath\temp" },
new object[] { "sometest.dll --test-adapter-path c:\adapterpath\temp", "sometest.dll --testadapterpath:c:\adapterpath\temp" },
new object[] { "sometest.dll -f net451", "sometest.dll --framework:net451" },
new object[] { @"-h", "--help" },
new object[] { @"sometest.dll -s test.settings", @"sometest.dll --settings:test.settings" },
new object[] { @"sometest.dll -t", @"sometest.dll --listtests" },
new object[] { @"sometest.dll --list-tests", @"sometest.dll --listtests" },
new object[] { @"sometest.dll --filter", @"sometest.dll --testcasefilter" },
new object[] { @"sometest.dll -l trx", @"sometest.dll --logger:trx" },
new object[] { @"sometest.dll -a c:\adapterpath\temp", @"sometest.dll --testadapterpath:c:\adapterpath\temp" },
new object[] { @"sometest.dll --test-adapter-path c:\adapterpath\temp", @"sometest.dll --testadapterpath:c:\adapterpath\temp" },
new object[] { @"sometest.dll -f net451", @"sometest.dll --framework:net451" },
new object[] { @"sometest.dll -d c:\temp\log.txt", @"sometest.dll --diag:c:\temp\log.txt" },
new object[] { @"sometest.dll --results-directory c:\temp\", @"sometest.dll --resultsdirectory:c:\temp\" },
new object[] { @"sometest.dll -s testsettings -t -a c:\path -f net451 -d log.txt --results-directory c:\temp\", @"sometest.dll --settings:testsettings --listtests --testadapterpath:c:\path --framework:net451 --diag:log.txt --resultsdirectory:c:\temp\" },
new object[] { @"sometest.dll -s:testsettings -t -a:c:\path -f:net451 -d:log.txt --results-directory:c:\temp\", @"sometest.dll --settings:testsettings --listtests --testadapterpath:c:\path --framework:net451 --diag:log.txt --resultsdirectory:c:\temp\" },
new object[] { @"sometest.dll --settings testsettings -t --test-adapter-path c:\path --framework net451 --diag log.txt --results-directory c:\temp\", @"sometest.dll --settings:testsettings --listtests --testadapterpath:c:\path --framework:net451 --diag:log.txt --resultsdirectory:c:\temp\" }
new object[] { @"sometest.dll --settings testsettings -t --test-adapter-path c:\path --framework net451 --diag log.txt --results-directory c:\temp\", @"sometest.dll --settings:testsettings --listtests --testadapterpath:c:\path --framework:net451 --diag:log.txt --resultsdirectory:c:\temp\" },
new object[] { @"sometest.dll --blame", @"sometest.dll --blame" },
new object[] { @"sometest.dll --blame-crash", @"sometest.dll --blame:""CollectDump""" },
new object[] { @"sometest.dll --blame-crash-dump-type full", @"sometest.dll --blame:""CollectDump;DumpType=full""" },
new object[] { @"sometest.dll --blame-crash-collect-always", @"sometest.dll --blame:""CollectDump;CollectAlways=true""" },
new object[] { @"sometest.dll --blame --blame-crash-dump-type full --blame-crash-collect-always", @"sometest.dll --blame:""CollectDump;CollectAlways=true;DumpType=full""" },
new object[] { @"sometest.dll --blame-hang", @"sometest.dll --blame:""CollectHangDump""" },
new object[] { @"sometest.dll --blame-hang-dump-type full", @"sometest.dll --blame:""CollectHangDump;DumpType=full""" },
new object[] { @"sometest.dll --blame-hang-timeout 10min", @"sometest.dll --blame:""CollectHangDump;TestTimeout=10min""" },
new object[] { @"sometest.dll --blame --blame-hang-dump-type full --blame-hang-timeout 10min", @"sometest.dll --blame:""CollectHangDump;DumpType=full;TestTimeout=10min""" },
new object[] { @"sometest.dll --blame --blame-hang-dump-type full --blame-hang-timeout 10min --blame-crash-dump-type mini --blame-crash-collect-always", @"sometest.dll --blame:""CollectDump;CollectAlways=true;DumpType=mini;CollectHangDump;DumpType=full;TestTimeout=10min""" },
// using the legacy --blame syntax when we provide the parameter that are already in vstest.console format still works
new object[] { @"sometest.dll --blame ""CollectDump;DumpType=full""", @"sometest.dll --blame:""CollectDump;DumpType=full""" },
new object[] { @"sometest.dll --blame:""CollectDump;DumpType=full""", @"sometest.dll --blame:""CollectDump;DumpType=full""" },



};

public static IEnumerable<object[]> VerbosityTestCases { get; } = new List<object[]>
Expand Down