Skip to content

Commit 7edd13b

Browse files
authored
Cleanup test command (#51883)
1 parent eee7296 commit 7edd13b

16 files changed

+314
-329
lines changed

src/Cli/dotnet/Commands/Test/MTP/MSBuildUtility.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ private static bool BuildOrRestoreProjectOrSolution(string filePath, BuildOption
153153
msbuildArgs.Add($"-verbosity:quiet");
154154
}
155155

156-
var parsedMSBuildArgs = MSBuildArgs.AnalyzeMSBuildArguments(msbuildArgs, CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, TestCommandDefinition.MTPTargetOption, TestCommandDefinition.VerbosityOption, CommonOptions.NoLogoOption());
156+
var parsedMSBuildArgs = MSBuildArgs.AnalyzeMSBuildArguments(msbuildArgs, CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, MicrosoftTestingPlatformOptions.MTPTargetOption, TestCommandDefinition.VerbosityOption, CommonOptions.NoLogoOption());
157157

158158
int result = new RestoringCommand(parsedMSBuildArgs, buildOptions.HasNoRestore).Execute();
159159

src/Cli/dotnet/Commands/Test/MTP/MicrosoftTestingPlatformOptions.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,6 @@ internal static class MicrosoftTestingPlatformOptions
6666
HelpName = CliCommandStrings.CmdNumberName
6767
};
6868

69-
public static readonly Option<string?> ConfigurationOption = CommonOptions.ConfigurationOption(CliCommandStrings.TestConfigurationOptionDescription);
70-
71-
public static readonly Option<string> FrameworkOption = CommonOptions.FrameworkOption(CliCommandStrings.TestFrameworkOptionDescription);
72-
7369
public static readonly Option<bool> NoBuildOption = new("--no-build")
7470
{
7571
Description = CliCommandStrings.CmdNoBuildDescription
@@ -109,6 +105,8 @@ internal static class MicrosoftTestingPlatformOptions
109105
Description = CliCommandStrings.CmdListTestsDescription,
110106
Arity = ArgumentArity.Zero
111107
};
108+
109+
public static readonly Option<string[]> MTPTargetOption = CommonOptions.RequiredMSBuildTargetOption(CliConstants.MTPTarget);
112110
}
113111

114112
internal enum OutputOptions

src/Cli/dotnet/Commands/Test/MTP/ValidationUtility.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ static void ValidateOptionsIrrelevantToModulesFilter(ParseResult parseResult)
4040
}
4141

4242
if (parseResult.HasOption(CommonOptions.ArchitectureOption) ||
43-
parseResult.HasOption(MicrosoftTestingPlatformOptions.ConfigurationOption) ||
44-
parseResult.HasOption(MicrosoftTestingPlatformOptions.FrameworkOption) ||
43+
parseResult.HasOption(TestCommandDefinition.ConfigurationOption) ||
44+
parseResult.HasOption(TestCommandDefinition.FrameworkOption) ||
4545
parseResult.HasOption(CommonOptions.OperatingSystemOption) ||
4646
parseResult.HasOption(CommonOptions.RuntimeOptionName))
4747
{

src/Cli/dotnet/Commands/Test/TestCommandDefinition.cs

Lines changed: 25 additions & 243 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using System.Text.Json;
66
using System.Text.Json.Serialization;
77
using Microsoft.DotNet.Cli.CommandLine;
8-
using Microsoft.DotNet.Cli.Extensions;
98
using Command = System.CommandLine.Command;
109

1110
namespace Microsoft.DotNet.Cli.Commands.Test;
@@ -33,145 +32,11 @@ private sealed class GlobalJsonTestNode
3332
public const string Name = "test";
3433
public static readonly string DocsLink = "https://aka.ms/dotnet-test";
3534

36-
public static readonly Option<string> SettingsOption = new Option<string>("--settings", "-s")
37-
{
38-
Description = CliCommandStrings.CmdSettingsDescription,
39-
HelpName = CliCommandStrings.CmdSettingsFile
40-
}.ForwardAsSingle(o => $"-property:VSTestSetting={SurroundWithDoubleQuotes(CommandDirectoryContext.GetFullPath(o))}");
41-
42-
public static readonly Option<bool> ListTestsOption = new Option<bool>("--list-tests", "-t")
43-
{
44-
Description = CliCommandStrings.CmdListTestsDescription,
45-
Arity = ArgumentArity.Zero
46-
}.ForwardAs("-property:VSTestListTests=true");
47-
48-
public static readonly Option<string> FilterOption = new Option<string>("--filter")
49-
{
50-
Description = CliCommandStrings.CmdTestCaseFilterDescription,
51-
HelpName = CliCommandStrings.CmdTestCaseFilterExpression
52-
}.ForwardAsSingle(o => $"-property:VSTestTestCaseFilter={SurroundWithDoubleQuotes(o!)}");
53-
54-
public static readonly Option<IEnumerable<string>> AdapterOption = new Option<IEnumerable<string>>("--test-adapter-path")
55-
{
56-
Description = CliCommandStrings.CmdTestAdapterPathDescription,
57-
HelpName = CliCommandStrings.CmdTestAdapterPath
58-
}.ForwardAsSingle(o => $"-property:VSTestTestAdapterPath={SurroundWithDoubleQuotes(string.Join(";", o!.Select(CommandDirectoryContext.GetFullPath)))}")
59-
.AllowSingleArgPerToken();
60-
61-
public static readonly Option<IEnumerable<string>> LoggerOption = new Option<IEnumerable<string>>("--logger", "-l")
62-
{
63-
Description = CliCommandStrings.CmdLoggerDescription,
64-
HelpName = CliCommandStrings.CmdLoggerOption
65-
}.ForwardAsSingle(o =>
66-
{
67-
var loggersString = string.Join(";", GetSemiColonEscapedArgs(o!));
68-
69-
return $"-property:VSTestLogger={SurroundWithDoubleQuotes(loggersString)}";
70-
})
71-
.AllowSingleArgPerToken();
72-
73-
public static readonly Option<string> OutputOption = new Option<string>("--output", "-o")
74-
{
75-
Description = CliCommandStrings.CmdOutputDescription,
76-
HelpName = CliCommandStrings.TestCmdOutputDir
77-
}
78-
.ForwardAsOutputPath("OutputPath", true);
79-
80-
public static readonly Option<string> DiagOption = new Option<string>("--diag", "-d")
81-
{
82-
Description = CliCommandStrings.CmdPathTologFileDescription,
83-
HelpName = CliCommandStrings.CmdPathToLogFile
84-
}
85-
.ForwardAsSingle(o => $"-property:VSTestDiag={SurroundWithDoubleQuotes(CommandDirectoryContext.GetFullPath(o))}");
86-
87-
public static readonly Option<bool> NoBuildOption = new Option<bool>("--no-build")
88-
{
89-
Description = CliCommandStrings.CmdNoBuildDescription,
90-
Arity = ArgumentArity.Zero
91-
}.ForwardAs("-property:VSTestNoBuild=true");
92-
93-
public static readonly Option<string> ResultsOption = new Option<string>("--results-directory")
94-
{
95-
Description = CliCommandStrings.CmdResultsDirectoryDescription,
96-
HelpName = CliCommandStrings.CmdPathToResultsDirectory
97-
}.ForwardAsSingle(o => $"-property:VSTestResultsDirectory={SurroundWithDoubleQuotes(CommandDirectoryContext.GetFullPath(o))}");
98-
99-
public static readonly Option<IEnumerable<string>> CollectOption = new Option<IEnumerable<string>>("--collect")
100-
{
101-
Description = CliCommandStrings.cmdCollectDescription,
102-
HelpName = CliCommandStrings.cmdCollectFriendlyName
103-
}.ForwardAsSingle(o => $"-property:VSTestCollect=\"{string.Join(";", GetSemiColonEscapedArgs(o!))}\"")
104-
.AllowSingleArgPerToken();
105-
106-
public static readonly Option<bool> BlameOption = new Option<bool>("--blame")
107-
{
108-
Description = CliCommandStrings.CmdBlameDescription,
109-
Arity = ArgumentArity.Zero
110-
}.ForwardIfEnabled("-property:VSTestBlame=true");
111-
112-
public static readonly Option<bool> BlameCrashOption = new Option<bool>("--blame-crash")
113-
{
114-
Description = CliCommandStrings.CmdBlameCrashDescription,
115-
Arity = ArgumentArity.Zero
116-
}.ForwardIfEnabled("-property:VSTestBlameCrash=true");
117-
118-
public static readonly Option<string> BlameCrashDumpOption = CreateBlameCrashDumpOption();
119-
120-
private static Option<string> CreateBlameCrashDumpOption()
121-
{
122-
Option<string> result = new Option<string>("--blame-crash-dump-type")
123-
{
124-
Description = CliCommandStrings.CmdBlameCrashDumpTypeDescription,
125-
HelpName = CliCommandStrings.CrashDumpTypeArgumentName,
126-
}
127-
.ForwardAsMany(o => ["-property:VSTestBlameCrash=true", $"-property:VSTestBlameCrashDumpType={o}"]);
128-
result.AcceptOnlyFromAmong(["full", "mini"]);
129-
return result;
130-
}
131-
132-
public static readonly Option<bool> BlameCrashAlwaysOption = new Option<bool>("--blame-crash-collect-always")
133-
{
134-
Description = CliCommandStrings.CmdBlameCrashCollectAlwaysDescription,
135-
Arity = ArgumentArity.Zero
136-
}.ForwardIfEnabled(["-property:VSTestBlameCrash=true", "-property:VSTestBlameCrashCollectAlways=true"]);
137-
138-
public static readonly Option<bool> BlameHangOption = new Option<bool>("--blame-hang")
139-
{
140-
Description = CliCommandStrings.CmdBlameHangDescription,
141-
Arity = ArgumentArity.Zero
142-
}.ForwardAs("-property:VSTestBlameHang=true");
143-
144-
public static readonly Option<string> BlameHangDumpOption = CreateBlameHangDumpOption();
145-
146-
private static Option<string> CreateBlameHangDumpOption()
147-
{
148-
Option<string> result = new Option<string>("--blame-hang-dump-type")
149-
{
150-
Description = CliCommandStrings.CmdBlameHangDumpTypeDescription,
151-
HelpName = CliCommandStrings.HangDumpTypeArgumentName
152-
}
153-
.ForwardAsMany(o => ["-property:VSTestBlameHang=true", $"-property:VSTestBlameHangDumpType={o}"]);
154-
result.AcceptOnlyFromAmong(["full", "mini", "none"]);
155-
return result;
156-
}
157-
158-
public static readonly Option<string> BlameHangTimeoutOption = new Option<string>("--blame-hang-timeout")
159-
{
160-
Description = CliCommandStrings.CmdBlameHangTimeoutDescription,
161-
HelpName = CliCommandStrings.HangTimeoutArgumentName
162-
}.ForwardAsMany(o => ["-property:VSTestBlameHang=true", $"-property:VSTestBlameHangTimeout={o}"]);
163-
164-
public static readonly Option<bool> NoLogoOption = CommonOptions.NoLogoOption(forwardAs: "--property:VSTestNoLogo=true", description: CliCommandStrings.TestCmdNoLogo);
165-
166-
public static readonly Option<bool> NoRestoreOption = CommonOptions.NoRestoreOption;
167-
16835
public static readonly Option<string> FrameworkOption = CommonOptions.FrameworkOption(CliCommandStrings.TestFrameworkOptionDescription);
16936

170-
public static readonly Option ConfigurationOption = CommonOptions.ConfigurationOption(CliCommandStrings.TestConfigurationOptionDescription);
37+
public static readonly Option<string?> ConfigurationOption = CommonOptions.ConfigurationOption(CliCommandStrings.TestConfigurationOptionDescription);
17138

17239
public static readonly Option<Utils.VerbosityOptions?> VerbosityOption = CommonOptions.VerbosityOption();
173-
public static readonly Option<string[]> VsTestTargetOption = CommonOptions.RequiredMSBuildTargetOption("VSTest");
174-
public static readonly Option<string[]> MTPTargetOption = CommonOptions.RequiredMSBuildTargetOption(CliConstants.MTPTarget);
17540

17641
public static TestRunner GetTestRunner()
17742
{
@@ -223,28 +88,7 @@ public static TestRunner GetTestRunner()
22388
return null;
22489
}
22590

226-
public static Command Create()
227-
{
228-
var command = new Command(Name);
229-
230-
switch (GetTestRunner())
231-
{
232-
case TestRunner.VSTest:
233-
ConfigureVSTestCommand(command);
234-
break;
235-
236-
case TestRunner.MicrosoftTestingPlatform:
237-
ConfigureTestingPlatformCommand(command);
238-
break;
239-
240-
default:
241-
throw new InvalidOperationException();
242-
};
243-
244-
return command;
245-
}
246-
247-
public static void ConfigureTestingPlatformCommand(Command command)
91+
public static void ConfigureMicrosoftTestingPlatformCommand(Command command)
24892
{
24993
command.Description = CliCommandStrings.DotnetTestCommandMTPDescription;
25094
command.Options.Add(MicrosoftTestingPlatformOptions.ProjectOrSolutionOption);
@@ -259,8 +103,8 @@ public static void ConfigureTestingPlatformCommand(Command command)
259103
command.Options.Add(CommonOptions.ArchitectureOption);
260104
command.Options.Add(CommonOptions.EnvOption);
261105
command.Options.Add(CommonOptions.PropertiesOption);
262-
command.Options.Add(MicrosoftTestingPlatformOptions.ConfigurationOption);
263-
command.Options.Add(MicrosoftTestingPlatformOptions.FrameworkOption);
106+
command.Options.Add(ConfigurationOption);
107+
command.Options.Add(FrameworkOption);
264108
command.Options.Add(CommonOptions.OperatingSystemOption);
265109
command.Options.Add(CommonOptions.RuntimeOption(CliCommandStrings.TestRuntimeOptionDescription));
266110
command.Options.Add(VerbosityOption);
@@ -272,7 +116,7 @@ public static void ConfigureTestingPlatformCommand(Command command)
272116
command.Options.Add(MicrosoftTestingPlatformOptions.ListTestsOption);
273117
command.Options.Add(MicrosoftTestingPlatformOptions.NoLaunchProfileOption);
274118
command.Options.Add(MicrosoftTestingPlatformOptions.NoLaunchProfileArgumentsOption);
275-
command.Options.Add(MTPTargetOption);
119+
command.Options.Add(MicrosoftTestingPlatformOptions.MTPTargetOption);
276120
}
277121

278122
public static void ConfigureVSTestCommand(Command command)
@@ -284,98 +128,36 @@ public static void ConfigureVSTestCommand(Command command)
284128
// We are on purpose not capturing the solution, project or directory here. We want to pass it to the
285129
// MSBuild command so we are letting it flow.
286130

287-
command.Options.Add(SettingsOption);
288-
command.Options.Add(ListTestsOption);
131+
command.Options.Add(VSTestOptions.SettingsOption);
132+
command.Options.Add(VSTestOptions.ListTestsOption);
289133
command.Options.Add(CommonOptions.TestEnvOption);
290-
command.Options.Add(FilterOption);
291-
command.Options.Add(AdapterOption);
292-
command.Options.Add(LoggerOption);
293-
command.Options.Add(OutputOption);
134+
command.Options.Add(VSTestOptions.FilterOption);
135+
command.Options.Add(VSTestOptions.AdapterOption);
136+
command.Options.Add(VSTestOptions.LoggerOption);
137+
command.Options.Add(VSTestOptions.OutputOption);
294138
command.Options.Add(CommonOptions.ArtifactsPathOption);
295-
command.Options.Add(DiagOption);
296-
command.Options.Add(NoBuildOption);
297-
command.Options.Add(ResultsOption);
298-
command.Options.Add(CollectOption);
299-
command.Options.Add(BlameOption);
300-
command.Options.Add(BlameCrashOption);
301-
command.Options.Add(BlameCrashDumpOption);
302-
command.Options.Add(BlameCrashAlwaysOption);
303-
command.Options.Add(BlameHangOption);
304-
command.Options.Add(BlameHangDumpOption);
305-
command.Options.Add(BlameHangTimeoutOption);
306-
command.Options.Add(NoLogoOption);
139+
command.Options.Add(VSTestOptions.DiagOption);
140+
command.Options.Add(VSTestOptions.NoBuildOption);
141+
command.Options.Add(VSTestOptions.ResultsOption);
142+
command.Options.Add(VSTestOptions.CollectOption);
143+
command.Options.Add(VSTestOptions.BlameOption);
144+
command.Options.Add(VSTestOptions.BlameCrashOption);
145+
command.Options.Add(VSTestOptions.BlameCrashDumpOption);
146+
command.Options.Add(VSTestOptions.BlameCrashAlwaysOption);
147+
command.Options.Add(VSTestOptions.BlameHangOption);
148+
command.Options.Add(VSTestOptions.BlameHangDumpOption);
149+
command.Options.Add(VSTestOptions.BlameHangTimeoutOption);
150+
command.Options.Add(VSTestOptions.NoLogoOption);
307151
command.Options.Add(ConfigurationOption);
308152
command.Options.Add(FrameworkOption);
309153
command.Options.Add(CommonOptions.RuntimeOption(CliCommandStrings.TestRuntimeOptionDescription));
310-
command.Options.Add(NoRestoreOption);
154+
command.Options.Add(CommonOptions.NoRestoreOption);
311155
command.Options.Add(CommonOptions.InteractiveMsBuildForwardOption);
312156
command.Options.Add(VerbosityOption);
313157
command.Options.Add(CommonOptions.ArchitectureOption);
314158
command.Options.Add(CommonOptions.OperatingSystemOption);
315159
command.Options.Add(CommonOptions.PropertiesOption);
316160
command.Options.Add(CommonOptions.DisableBuildServersOption);
317-
command.Options.Add(VsTestTargetOption);
318-
}
319-
320-
private static string GetSemiColonEscapedstring(string arg)
321-
{
322-
if (arg.IndexOf(";") != -1)
323-
{
324-
return arg.Replace(";", "%3b");
325-
}
326-
327-
return arg;
328-
}
329-
330-
private static string[] GetSemiColonEscapedArgs(IEnumerable<string> args)
331-
{
332-
int counter = 0;
333-
string[] array = new string[args.Count()];
334-
335-
foreach (string arg in args)
336-
{
337-
array[counter++] = GetSemiColonEscapedstring(arg);
338-
}
339-
340-
return array;
341-
}
342-
343-
/// <summary>
344-
/// Adding double quotes around the property helps MSBuild arguments parser and avoid incorrect splits on ',' or ';'.
345-
/// </summary>
346-
internal /* for testing purposes */ static string SurroundWithDoubleQuotes(string input)
347-
{
348-
if (input is null)
349-
{
350-
throw new ArgumentNullException(nameof(input));
351-
}
352-
353-
// If already escaped by double quotes then return original string.
354-
if (input.StartsWith("\"", StringComparison.Ordinal)
355-
&& input.EndsWith("\"", StringComparison.Ordinal))
356-
{
357-
return input;
358-
}
359-
360-
// We want to count the number of trailing backslashes to ensure
361-
// we will have an even number before adding the final double quote.
362-
// Otherwise the last \" will be interpreted as escaping the double
363-
// quote rather than a backslash and a double quote.
364-
var trailingBackslashesCount = 0;
365-
for (int i = input.Length - 1; i >= 0; i--)
366-
{
367-
if (input[i] == '\\')
368-
{
369-
trailingBackslashesCount++;
370-
}
371-
else
372-
{
373-
break;
374-
}
375-
}
376-
377-
return trailingBackslashesCount % 2 == 0
378-
? string.Concat("\"", input, "\"")
379-
: string.Concat("\"", input, "\\\"");
161+
command.Options.Add(VSTestOptions.VsTestTargetOption);
380162
}
381163
}

src/Cli/dotnet/Commands/Test/TestCommandParser.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ private static Command CreateCommand()
2727
private static Command CreateTestingPlatformCommand()
2828
{
2929
var command = new MicrosoftTestingPlatformTestCommand(TestCommandDefinition.Name);
30-
TestCommandDefinition.ConfigureTestingPlatformCommand(command);
30+
TestCommandDefinition.ConfigureMicrosoftTestingPlatformCommand(command);
3131
command.SetAction(parseResult => command.Run(parseResult));
3232
return command;
3333
}

0 commit comments

Comments
 (0)