Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -66,38 +66,38 @@ public static BuildOptions GetBuildOptions(ParseResult parseResult, int degreeOf
var msbuildArgs = parseResult.OptionValuesToBeForwarded(TestCommandParser.GetCommand())
.Concat(binLogArgs);

string? resultsDirectory = parseResult.GetValue(TestingPlatformOptions.ResultsDirectoryOption);
string? resultsDirectory = parseResult.GetValue(MicrosoftTestingPlatformOptions.ResultsDirectoryOption);
if (resultsDirectory is not null)
{
resultsDirectory = Path.GetFullPath(resultsDirectory);
}

string? configFile = parseResult.GetValue(TestingPlatformOptions.ConfigFileOption);
string? configFile = parseResult.GetValue(MicrosoftTestingPlatformOptions.ConfigFileOption);
if (configFile is not null)
{
configFile = Path.GetFullPath(configFile);
}

string? diagnosticOutputDirectory = parseResult.GetValue(TestingPlatformOptions.DiagnosticOutputDirectoryOption);
string? diagnosticOutputDirectory = parseResult.GetValue(MicrosoftTestingPlatformOptions.DiagnosticOutputDirectoryOption);
if (diagnosticOutputDirectory is not null)
{
diagnosticOutputDirectory = Path.GetFullPath(diagnosticOutputDirectory);
}

PathOptions pathOptions = new(
parseResult.GetValue(TestingPlatformOptions.ProjectOption),
parseResult.GetValue(TestingPlatformOptions.SolutionOption),
parseResult.GetValue(MicrosoftTestingPlatformOptions.ProjectOption),
parseResult.GetValue(MicrosoftTestingPlatformOptions.SolutionOption),
resultsDirectory,
configFile,
diagnosticOutputDirectory);

return new BuildOptions(
pathOptions,
parseResult.GetValue(CommonOptions.NoRestoreOption),
parseResult.GetValue(TestingPlatformOptions.NoBuildOption),
parseResult.GetValue(MicrosoftTestingPlatformOptions.NoBuildOption),
parseResult.HasOption(TestCommandParser.VerbosityOption) ? parseResult.GetValue(TestCommandParser.VerbosityOption) : null,
parseResult.GetValue(TestingPlatformOptions.NoLaunchProfileOption),
parseResult.GetValue(TestingPlatformOptions.NoLaunchProfileArgumentsOption),
parseResult.GetValue(MicrosoftTestingPlatformOptions.NoLaunchProfileOption),
parseResult.GetValue(MicrosoftTestingPlatformOptions.NoLaunchProfileArgumentsOption),
degreeOfParallelism,
otherArgs,
msbuildArgs);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Microsoft.DotNet.Cli.Commands.Test;

internal static class TestingPlatformOptions
internal static class MicrosoftTestingPlatformOptions
{
public static readonly Option<string> ProjectOption = new("--project")
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

namespace Microsoft.DotNet.Cli.Commands.Test;

internal partial class TestingPlatformCommand
internal partial class MicrosoftTestingPlatformTestCommand
{
private readonly ConcurrentDictionary<string, CommandLineOption> _commandLineOptionNameToModuleNames = [];
private readonly ConcurrentDictionary<bool, List<(string, string[])>> _moduleNamesToCommandLineOptions = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,18 @@

namespace Microsoft.DotNet.Cli.Commands.Test;

internal partial class TestingPlatformCommand : Command, ICustomHelp
internal partial class MicrosoftTestingPlatformTestCommand : Command, ICustomHelp, ICommandDocument
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The additional interface is for #50638

{
private MSBuildHandler _msBuildHandler;
private TerminalTestReporter _output;
private TestApplicationActionQueue _actionQueue;

private byte _cancelled;
private bool _isDiscovery;
private bool _isRetry;

public TestingPlatformCommand(string name, string description = null) : base(name, description)
public MicrosoftTestingPlatformTestCommand(string name, string description = null) : base(name, description)
{
TreatUnmatchedTokensAsErrors = false;
}

public string DocsLink => "https://aka.ms/dotnet-test";

public int Run(ParseResult parseResult)
{
int? exitCode = null;
Expand All @@ -44,44 +41,48 @@ private int RunInternal(ParseResult parseResult)
ValidationUtility.ValidateMutuallyExclusiveOptions(parseResult);
ValidationUtility.ValidateSolutionOrProjectOrDirectoryOrModulesArePassedCorrectly(parseResult);

PrepareEnvironment(parseResult, out TestOptions testOptions, out int degreeOfParallelism);
int degreeOfParallelism = GetDegreeOfParallelism(parseResult);
bool filterModeEnabled = parseResult.HasOption(MicrosoftTestingPlatformOptions.TestModulesFilterOption);
var testOptions = new TestOptions(filterModeEnabled, IsHelp: parseResult.HasOption(MicrosoftTestingPlatformOptions.HelpOption));

InitializeOutput(degreeOfParallelism, parseResult, testOptions.IsHelp);

SetupCancelKeyPressHandler();

BuildOptions buildOptions = MSBuildUtility.GetBuildOptions(parseResult, degreeOfParallelism);

InitializeActionQueue(degreeOfParallelism, testOptions, buildOptions);
var actionQueue = InitializeActionQueue(degreeOfParallelism, testOptions, buildOptions);

_msBuildHandler = new(buildOptions, _actionQueue, _output);
TestModulesFilterHandler testModulesFilterHandler = new(_actionQueue, _output);
var msBuildHandler = new MSBuildHandler(buildOptions, actionQueue, _output);

if (testOptions.HasFilterMode)
{
var testModulesFilterHandler = new TestModulesFilterHandler(actionQueue, _output);
if (!testModulesFilterHandler.RunWithTestModulesFilter(parseResult))
{
return ExitCode.GenericFailure;
}
}
else
{
if (!_msBuildHandler.RunMSBuild())
if (!msBuildHandler.RunMSBuild())
{
return ExitCode.GenericFailure;
}

if (!_msBuildHandler.EnqueueTestApplications())
if (!msBuildHandler.EnqueueTestApplications())
{
return ExitCode.GenericFailure;
}
}

_actionQueue.EnqueueCompleted();
actionQueue.EnqueueCompleted();
// Don't inline exitCode variable. We want to always call WaitAllActions first.
var exitCode = _actionQueue.WaitAllActions();
var exitCode = actionQueue.WaitAllActions();
exitCode = _output.HasHandshakeFailure ? ExitCode.GenericFailure : exitCode;
if (exitCode == ExitCode.Success &&
parseResult.HasOption(TestingPlatformOptions.MinimumExpectedTestsOption) &&
parseResult.GetValue(TestingPlatformOptions.MinimumExpectedTestsOption) is { } minimumExpectedTests &&
parseResult.HasOption(MicrosoftTestingPlatformOptions.MinimumExpectedTestsOption) &&
parseResult.GetValue(MicrosoftTestingPlatformOptions.MinimumExpectedTestsOption) is { } minimumExpectedTests &&
_output.TotalTests < minimumExpectedTests)
{
exitCode = ExitCode.MinimumExpectedTestsPolicyViolation;
Expand All @@ -90,26 +91,9 @@ private int RunInternal(ParseResult parseResult)
return exitCode;
}

private void PrepareEnvironment(ParseResult parseResult, out TestOptions testOptions, out int degreeOfParallelism)
private TestApplicationActionQueue InitializeActionQueue(int degreeOfParallelism, TestOptions testOptions, BuildOptions buildOptions)
{
SetupCancelKeyPressHandler();

degreeOfParallelism = GetDegreeOfParallelism(parseResult);

bool filterModeEnabled = parseResult.HasOption(TestingPlatformOptions.TestModulesFilterOption);

var arguments = parseResult.GetArguments();
testOptions = GetTestOptions(filterModeEnabled, isHelp: ContainsHelpOption(arguments));

_isDiscovery = ContainsListTestsOption(arguments);

// This is ugly, and we need to replace it by passing out some info from testing platform to inform us that some process level retry plugin is active.
_isRetry = arguments.Contains("--retry-failed-tests");
}

private void InitializeActionQueue(int degreeOfParallelism, TestOptions testOptions, BuildOptions buildOptions)
{
_actionQueue = new TestApplicationActionQueue(degreeOfParallelism, buildOptions, testOptions, _output, async (TestApplication testApp) =>
return new TestApplicationActionQueue(degreeOfParallelism, buildOptions, testOptions, _output, async (TestApplication testApp) =>
{
testApp.HelpRequested += OnHelpRequested;
return await testApp.RunAsync();
Expand All @@ -129,9 +113,9 @@ private void SetupCancelKeyPressHandler()
private void InitializeOutput(int degreeOfParallelism, ParseResult parseResult, bool isHelp)
{
var console = new SystemConsole();
var showPassedTests = parseResult.GetValue(TestingPlatformOptions.OutputOption) == OutputOptions.Detailed;
var noProgress = parseResult.HasOption(TestingPlatformOptions.NoProgressOption);
var noAnsi = parseResult.HasOption(TestingPlatformOptions.NoAnsiOption);
var showPassedTests = parseResult.GetValue(MicrosoftTestingPlatformOptions.OutputOption) == OutputOptions.Detailed;
var noProgress = parseResult.HasOption(MicrosoftTestingPlatformOptions.NoProgressOption);
var noAnsi = parseResult.HasOption(MicrosoftTestingPlatformOptions.NoAnsiOption);

// TODO: Replace this with proper CI detection that we already have in telemetry. https://github.com/microsoft/testfx/issues/5533#issuecomment-2838893327
bool inCI = string.Equals(Environment.GetEnvironmentVariable("TF_BUILD"), "true", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("GITHUB_ACTIONS"), "true", StringComparison.OrdinalIgnoreCase);
Expand All @@ -144,33 +128,24 @@ private void InitializeOutput(int degreeOfParallelism, ParseResult parseResult,
UseCIAnsi = inCI,
ShowAssembly = true,
ShowAssemblyStartAndComplete = true,
MinimumExpectedTests = parseResult.GetValue(TestingPlatformOptions.MinimumExpectedTestsOption),
MinimumExpectedTests = parseResult.GetValue(MicrosoftTestingPlatformOptions.MinimumExpectedTestsOption),
});

_output.TestExecutionStarted(DateTimeOffset.Now, degreeOfParallelism, _isDiscovery, isHelp, _isRetry);
var isDiscovery = parseResult.HasOption(MicrosoftTestingPlatformOptions.ListTestsOption);
// This is ugly, and we need to replace it by passing out some info from testing platform to inform us that some process level retry plugin is active.
var isRetry = parseResult.GetArguments().Contains("--retry-failed-tests");

_output.TestExecutionStarted(DateTimeOffset.Now, degreeOfParallelism, isDiscovery, isHelp, isRetry);
}

private static int GetDegreeOfParallelism(ParseResult parseResult)
{
var degreeOfParallelism = parseResult.GetValue(TestingPlatformOptions.MaxParallelTestModulesOption);
var degreeOfParallelism = parseResult.GetValue(MicrosoftTestingPlatformOptions.MaxParallelTestModulesOption);
if (degreeOfParallelism <= 0)
degreeOfParallelism = Environment.ProcessorCount;
return degreeOfParallelism;
}

private static TestOptions GetTestOptions(bool hasFilterMode, bool isHelp) =>
new(hasFilterMode, isHelp);

private static bool ContainsHelpOption(IEnumerable<string> args)
{
return args.Contains(TestingPlatformOptions.HelpOption.Name) || TestingPlatformOptions.HelpOption.Aliases.Any(alias => args.Contains(alias));
}

private static bool ContainsListTestsOption(IEnumerable<string> args)
{
return args.Contains(TestingPlatformOptions.ListTestsOption.Name);
}

private void CompleteRun(int? exitCode)
{
if (Interlocked.CompareExchange(ref _cancelled, 1, 0) == 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,22 +112,22 @@ private string GetArguments()

if (TestOptions.IsHelp)
{
builder.Append($" {TestingPlatformOptions.HelpOption.Name}");
builder.Append($" {MicrosoftTestingPlatformOptions.HelpOption.Name}");
}

if (_buildOptions.PathOptions.ResultsDirectoryPath is { } resultsDirectoryPath)
{
builder.Append($" {TestingPlatformOptions.ResultsDirectoryOption.Name} {ArgumentEscaper.EscapeSingleArg(resultsDirectoryPath)}");
builder.Append($" {MicrosoftTestingPlatformOptions.ResultsDirectoryOption.Name} {ArgumentEscaper.EscapeSingleArg(resultsDirectoryPath)}");
}

if (_buildOptions.PathOptions.ConfigFilePath is { } configFilePath)
{
builder.Append($" {TestingPlatformOptions.ConfigFileOption.Name} {ArgumentEscaper.EscapeSingleArg(configFilePath)}");
builder.Append($" {MicrosoftTestingPlatformOptions.ConfigFileOption.Name} {ArgumentEscaper.EscapeSingleArg(configFilePath)}");
}

if (_buildOptions.PathOptions.DiagnosticOutputDirectoryPath is { } diagnosticOutputDirectoryPath)
{
builder.Append($" {TestingPlatformOptions.DiagnosticOutputDirectoryOption.Name} {ArgumentEscaper.EscapeSingleArg(diagnosticOutputDirectoryPath)}");
builder.Append($" {MicrosoftTestingPlatformOptions.DiagnosticOutputDirectoryOption.Name} {ArgumentEscaper.EscapeSingleArg(diagnosticOutputDirectoryPath)}");
}

foreach (var arg in _buildOptions.UnmatchedTokens)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ internal sealed class TestModulesFilterHandler(TestApplicationActionQueue action
public bool RunWithTestModulesFilter(ParseResult parseResult)
{
// If the module path pattern(s) was provided, we will use that to filter the test modules
string testModules = parseResult.GetValue(TestingPlatformOptions.TestModulesFilterOption);
string testModules = parseResult.GetValue(MicrosoftTestingPlatformOptions.TestModulesFilterOption);

// If the root directory was provided, we will use that to search for the test modules
// Otherwise, we will use the current directory
string rootDirectory = Directory.GetCurrentDirectory();
if (parseResult.HasOption(TestingPlatformOptions.TestModulesRootDirectoryOption))
if (parseResult.HasOption(MicrosoftTestingPlatformOptions.TestModulesRootDirectoryOption))
{
rootDirectory = parseResult.GetValue(TestingPlatformOptions.TestModulesRootDirectoryOption);
rootDirectory = parseResult.GetValue(MicrosoftTestingPlatformOptions.TestModulesRootDirectoryOption);

// If the root directory is not valid, we simply return
if (string.IsNullOrEmpty(rootDirectory) || !Directory.Exists(rootDirectory))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ public static void ValidateMutuallyExclusiveOptions(ParseResult parseResult)
static void ValidatePathOptions(ParseResult parseResult)
{
var count = 0;
if (parseResult.HasOption(TestingPlatformOptions.TestModulesFilterOption))
if (parseResult.HasOption(MicrosoftTestingPlatformOptions.TestModulesFilterOption))
count++;

if (parseResult.HasOption(TestingPlatformOptions.SolutionOption))
if (parseResult.HasOption(MicrosoftTestingPlatformOptions.SolutionOption))
count++;

if (parseResult.HasOption(TestingPlatformOptions.ProjectOption))
if (parseResult.HasOption(MicrosoftTestingPlatformOptions.ProjectOption))
count++;

if (count > 1)
Expand All @@ -35,14 +35,14 @@ static void ValidatePathOptions(ParseResult parseResult)

static void ValidateOptionsIrrelevantToModulesFilter(ParseResult parseResult)
{
if (!parseResult.HasOption(TestingPlatformOptions.TestModulesFilterOption))
if (!parseResult.HasOption(MicrosoftTestingPlatformOptions.TestModulesFilterOption))
{
return;
}

if (parseResult.HasOption(CommonOptions.ArchitectureOption) ||
parseResult.HasOption(TestingPlatformOptions.ConfigurationOption) ||
parseResult.HasOption(TestingPlatformOptions.FrameworkOption) ||
parseResult.HasOption(MicrosoftTestingPlatformOptions.ConfigurationOption) ||
parseResult.HasOption(MicrosoftTestingPlatformOptions.FrameworkOption) ||
parseResult.HasOption(CommonOptions.OperatingSystemOption) ||
parseResult.HasOption(CommonOptions.RuntimeOptionName))
{
Expand Down
36 changes: 18 additions & 18 deletions src/Cli/dotnet/Commands/Test/TestCommandParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,31 +227,31 @@ private static Command ConstructCommand()

private static Command GetTestingPlatformCliCommand()
{
var command = new TestingPlatformCommand("test", CliCommandStrings.DotnetTestCommand);
var command = new MicrosoftTestingPlatformTestCommand("test", CliCommandStrings.DotnetTestCommand);
command.SetAction(parseResult => command.Run(parseResult));
command.Options.Add(TestingPlatformOptions.ProjectOption);
command.Options.Add(TestingPlatformOptions.SolutionOption);
command.Options.Add(TestingPlatformOptions.TestModulesFilterOption);
command.Options.Add(TestingPlatformOptions.TestModulesRootDirectoryOption);
command.Options.Add(TestingPlatformOptions.ResultsDirectoryOption);
command.Options.Add(TestingPlatformOptions.ConfigFileOption);
command.Options.Add(TestingPlatformOptions.DiagnosticOutputDirectoryOption);
command.Options.Add(TestingPlatformOptions.MaxParallelTestModulesOption);
command.Options.Add(TestingPlatformOptions.MinimumExpectedTestsOption);
command.Options.Add(MicrosoftTestingPlatformOptions.ProjectOption);
command.Options.Add(MicrosoftTestingPlatformOptions.SolutionOption);
command.Options.Add(MicrosoftTestingPlatformOptions.TestModulesFilterOption);
command.Options.Add(MicrosoftTestingPlatformOptions.TestModulesRootDirectoryOption);
command.Options.Add(MicrosoftTestingPlatformOptions.ResultsDirectoryOption);
command.Options.Add(MicrosoftTestingPlatformOptions.ConfigFileOption);
command.Options.Add(MicrosoftTestingPlatformOptions.DiagnosticOutputDirectoryOption);
command.Options.Add(MicrosoftTestingPlatformOptions.MaxParallelTestModulesOption);
command.Options.Add(MicrosoftTestingPlatformOptions.MinimumExpectedTestsOption);
command.Options.Add(CommonOptions.ArchitectureOption);
command.Options.Add(CommonOptions.PropertiesOption);
command.Options.Add(TestingPlatformOptions.ConfigurationOption);
command.Options.Add(TestingPlatformOptions.FrameworkOption);
command.Options.Add(MicrosoftTestingPlatformOptions.ConfigurationOption);
command.Options.Add(MicrosoftTestingPlatformOptions.FrameworkOption);
command.Options.Add(CommonOptions.OperatingSystemOption);
command.Options.Add(CommonOptions.RuntimeOption(CliCommandStrings.TestRuntimeOptionDescription));
command.Options.Add(VerbosityOption);
command.Options.Add(CommonOptions.NoRestoreOption);
command.Options.Add(TestingPlatformOptions.NoBuildOption);
command.Options.Add(TestingPlatformOptions.NoAnsiOption);
command.Options.Add(TestingPlatformOptions.NoProgressOption);
command.Options.Add(TestingPlatformOptions.OutputOption);
command.Options.Add(TestingPlatformOptions.NoLaunchProfileOption);
command.Options.Add(TestingPlatformOptions.NoLaunchProfileArgumentsOption);
command.Options.Add(MicrosoftTestingPlatformOptions.NoBuildOption);
command.Options.Add(MicrosoftTestingPlatformOptions.NoAnsiOption);
command.Options.Add(MicrosoftTestingPlatformOptions.NoProgressOption);
command.Options.Add(MicrosoftTestingPlatformOptions.OutputOption);
command.Options.Add(MicrosoftTestingPlatformOptions.NoLaunchProfileOption);
command.Options.Add(MicrosoftTestingPlatformOptions.NoLaunchProfileArgumentsOption);
command.Options.Add(MTPTargetOption);

return command;
Expand Down
Loading
Loading