Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleaning command line validations and adding unit tests #2842

Merged
merged 8 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ internal sealed class PlatformCommandLineProvider : ICommandLineOptionsProvider
public const string TestHostControllerPIDOptionKey = "internal-testhostcontroller-pid";
public const string ExitOnProcessExitOptionKey = "exit-on-process-exit";

private static readonly string[] VerbosityOptions = ["Trace", "Debug", "Information", "Warning", "Error", "Critical"];

private static readonly CommandLineOption MinimumExpectedTests = new(MinimumExpectedTestsOptionKey, "Specifies the minimum number of tests that are expected to run.", ArgumentArity.ZeroOrOne, false, isBuiltIn: true);

private static readonly IReadOnlyCollection<CommandLineOption> PlatformCommandLineProviderCache = new[]
Expand Down Expand Up @@ -83,61 +85,25 @@ public IReadOnlyCollection<CommandLineOption> GetCommandLineOptions()

public Task<ValidationResult> ValidateOptionArgumentsAsync(CommandLineOption commandOption, string[] arguments)
{
if (commandOption.Name == HelpOptionKey && arguments.Length > 0)
{
return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLineOptionExpectsNoArgumentErrorMessage, HelpOptionKey));
}

if (commandOption.Name == InfoOptionKey && arguments.Length > 0)
{
return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLineOptionExpectsNoArgumentErrorMessage, InfoOptionKey));
}

if (commandOption.Name == DiagnosticVerbosityOptionKey)
{
if (arguments.Length != 1
|| (!arguments[0].Equals("Trace", StringComparison.OrdinalIgnoreCase)
&& !arguments[0].Equals("Debug", StringComparison.OrdinalIgnoreCase)
&& !arguments[0].Equals("Information", StringComparison.OrdinalIgnoreCase)
&& !arguments[0].Equals("Warning", StringComparison.OrdinalIgnoreCase)
&& !arguments[0].Equals("Error", StringComparison.OrdinalIgnoreCase)
&& !arguments[0].Equals("Critical", StringComparison.OrdinalIgnoreCase)))
if (!VerbosityOptions.Contains(arguments[0], StringComparer.OrdinalIgnoreCase))
{
return ValidationResult.InvalidTask(PlatformResources.PlatformCommandLineDiagnosticOptionExpectsSingleArgumentErrorMessage);
}
}

if (commandOption.Name == DiagnosticOutputDirectoryOptionKey && arguments.Length != 1)
{
return ValidationResult.InvalidTask(PlatformResources.PlatformCommandLineDiagnosticOutputDirectoryOptionSingleArgument);
}

if (commandOption.Name == DiagnosticOutputFilePrefixOptionKey && arguments.Length != 1)
{
return ValidationResult.InvalidTask(PlatformResources.PlatformCommandLineDiagnosticFilePrefixOptionSingleArgument);
}

if (commandOption.Name == ResultDirectoryOptionKey && arguments.Length != 1)
{
return ValidationResult.InvalidTask($"Invalid arguments for --{ResultDirectoryOptionKey}, expected usage: --results-directory ./CustomTestResultsFolder");
}

if (commandOption.Name == PortOptionKey && (arguments.Length != 1 || !int.TryParse(arguments[0], out int _)))
if (commandOption.Name == PortOptionKey && (!int.TryParse(arguments[0], out int _)))
{
return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLinePortOptionSingleArgument, PortOptionKey));
}

if (commandOption.Name == ClientPortOptionKey && (arguments.Length != 1 || !int.TryParse(arguments[0], out int _)))
if (commandOption.Name == ClientPortOptionKey && (!int.TryParse(arguments[0], out int _)))
{
return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLinePortOptionSingleArgument, ClientPortOptionKey));
}

if (commandOption.Name == ClientHostOptionKey && arguments.Length != 1)
{
return ValidationResult.InvalidTask(PlatformResources.PlatformCommandLineClientHostOptionSingleArgument);
}

if (commandOption.Name == ExitOnProcessExitOptionKey && (arguments.Length != 1 || !int.TryParse(arguments[0], out int _)))
if (commandOption.Name == ExitOnProcessExitOptionKey && (!int.TryParse(arguments[0], out int _)))
{
return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLineExitOnProcessExitSingleArgument, ExitOnProcessExitOptionKey));
}
Expand Down Expand Up @@ -187,19 +153,19 @@ public Task<ValidationResult> ValidateCommandLineOptionsAsync(ICommandLineOption

if (commandLineOptions.IsOptionSet(ExitOnProcessExitOptionKey))
{
commandLineOptions.TryGetOptionArgumentList(ExitOnProcessExitOptionKey, out string[]? pid);
_ = commandLineOptions.TryGetOptionArgumentList(ExitOnProcessExitOptionKey, out string[]? pid);
ApplicationStateGuard.Ensure(pid is not null);
RoslynDebug.Assert(pid.Length == 1);
int parentProcessPid = int.Parse(pid[0], CultureInfo.InvariantCulture);
try
{
// We let the api to do the validity check before to go down the subscription path.
// If we don't fail here but we fail below means that the parent process is not there anymore and we can take it as exited.
Process.GetProcessById(parentProcessPid);
_ = Process.GetProcessById(parentProcessPid);
}
catch (Exception ex)
catch (ArgumentException ex)
{
return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLineExitOnProcessExitInvalidDependantProcess, parentProcessPid, ex));
return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLineExitOnProcessExitInvalidDependentProcess, parentProcessPid, ex));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,11 @@ internal sealed class TreeNodeFilterCommandLineOptionsProvider(IExtension extens
public IReadOnlyCollection<CommandLineOption> GetCommandLineOptions()
=> new CommandLineOption[]
{
new(TreenodeFilter, PlatformResources.TreeNodeFilterDescription, ArgumentArity.ZeroOrOne, false),
new(TreenodeFilter, PlatformResources.TreeNodeFilterDescription, ArgumentArity.ExactlyOne, false),
};

public Task<ValidationResult> ValidateOptionArgumentsAsync(CommandLineOption commandOption, string[] arguments)
{
if (commandOption.Name == TreenodeFilter && arguments.Length != 1)
{
return ValidationResult.InvalidTask(PlatformResources.TreeNodeFilterInvalidArgumentCount);
}

// No problem found
return ValidationResult.ValidTask;
}
=> ValidationResult.ValidTask;

public Task<ValidationResult> ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions)
=> ValidationResult.ValidTask;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -355,18 +355,12 @@
<data name="PlatformCommandLineClientHostOptionDescription" xml:space="preserve">
<value>Specify the hostname of the client.</value>
</data>
<data name="PlatformCommandLineClientHostOptionSingleArgument" xml:space="preserve">
<value>'--client-host' expects a single host name as argument</value>
</data>
<data name="PlatformCommandLineClientPortOptionDescription" xml:space="preserve">
<value>Specify the port of the client.</value>
</data>
<data name="PlatformCommandLineDiagnosticFileLoggerSynchronousWriteOptionDescription" xml:space="preserve">
<value>Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). Note that this is slowing down the test execution.</value>
</data>
<data name="PlatformCommandLineDiagnosticFilePrefixOptionSingleArgument" xml:space="preserve">
<value>'--diagnostic-output-fileprefix' expects a single file name prefix argument</value>
</data>
<data name="PlatformCommandLineDiagnosticOptionDescription" xml:space="preserve">
<value>Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag</value>
</data>
Expand All @@ -379,9 +373,6 @@
<data name="PlatformCommandLineDiagnosticOutputDirectoryOptionDescription" xml:space="preserve">
<value>Output directory of the diagnostic logging, if not specified the file will be generated inside the default 'TestResults' directory.</value>
</data>
<data name="PlatformCommandLineDiagnosticOutputDirectoryOptionSingleArgument" xml:space="preserve">
<value>'--diagnostic-output-directory' expects a single directory name argument</value>
</data>
<data name="PlatformCommandLineDiagnosticOutputFilePrefixOptionDescription" xml:space="preserve">
<value>Prefix for the log file name that will replace '[log]_.'</value>
</data>
Expand All @@ -406,9 +397,6 @@
<data name="PlatformCommandLineNoBannerOptionDescription" xml:space="preserve">
<value>Do not display the startup banner, the copyright message or the telemetry banner.</value>
</data>
<data name="PlatformCommandLineOptionExpectsNoArgumentErrorMessage" xml:space="preserve">
<value>'--{0}' expects no argument</value>
</data>
<data name="PlatformCommandLinePortOptionDescription" xml:space="preserve">
<value>Specify the port of the server.</value>
</data>
Expand Down Expand Up @@ -439,9 +427,6 @@
<data name="TreeNodeFilterDescription" xml:space="preserve">
<value>Use a tree filter to filter down the tests to execute</value>
</data>
<data name="TreeNodeFilterInvalidArgumentCount" xml:space="preserve">
<value>A single argument is expected (e.g. '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]')</value>
</data>
<data name="ConnectingToClientHost" xml:space="preserve">
<value>Connecting to client host '{0}' port '{1}'</value>
</data>
Expand Down Expand Up @@ -495,7 +480,7 @@ Read more about Microsoft Testing Platform telemetry: https://aka.ms/testingplat
<data name="PlatformCommandLineExitOnProcessExitSingleArgument" xml:space="preserve">
<value>'--{0}' expects a single int PID argument</value>
</data>
<data name="PlatformCommandLineExitOnProcessExitInvalidDependantProcess" xml:space="preserve">
<data name="PlatformCommandLineExitOnProcessExitInvalidDependentProcess" xml:space="preserve">
<value>Invalid PID '{0}'
{1}</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,11 +264,6 @@
<target state="translated">Zadejte název hostitele klienta.</target>
<note />
</trans-unit>
<trans-unit id="PlatformCommandLineClientHostOptionSingleArgument">
<source>'--client-host' expects a single host name as argument</source>
<target state="translated">--client-host očekává jako argument jeden název hostitele.</target>
<note />
</trans-unit>
<trans-unit id="PlatformCommandLineClientPortOptionDescription">
<source>Specify the port of the client.</source>
<target state="translated">Zadejte port klienta.</target>
Expand All @@ -279,11 +274,6 @@
<target state="translated">Umožňuje vynutit synchronní zápis do protokolu integrovaným protokolovacím nástrojem souborů. Užitečné pro scénář, kdy nechcete přijít o žádný protokol (například v případě chybového ukončení). Poznámka: Toto zpomaluje provádění testu.</target>
<note />
</trans-unit>
<trans-unit id="PlatformCommandLineDiagnosticFilePrefixOptionSingleArgument">
<source>'--diagnostic-output-fileprefix' expects a single file name prefix argument</source>
<target state="translated">--diagnostic-output-fileprefix očekává jeden argument předpony názvu souboru.</target>
<note />
</trans-unit>
<trans-unit id="PlatformCommandLineDiagnosticOptionDescription">
<source>Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag</source>
<target state="translated">Umožňuje povolit protokolování diagnostiky. Výchozí úroveň protokolování je Trasování. Soubor se zapíše do výstupního adresáře s názvem log_[MMddHHssfff].diag.</target>
Expand All @@ -304,11 +294,6 @@
<target state="translated">Výstupní adresář diagnostického protokolování. Pokud není zadaný, soubor se vygeneruje ve výchozím adresáři TestResults.</target>
<note />
</trans-unit>
<trans-unit id="PlatformCommandLineDiagnosticOutputDirectoryOptionSingleArgument">
<source>'--diagnostic-output-directory' expects a single directory name argument</source>
<target state="translated">--diagnostic-output-directory očekává jeden argument názvu adresáře.</target>
<note />
</trans-unit>
<trans-unit id="PlatformCommandLineDiagnosticOutputFilePrefixOptionDescription">
<source>Prefix for the log file name that will replace '[log]_.'</source>
<target state="translated">Předpona pro název souboru protokolu, který nahradí [log]_</target>
Expand All @@ -324,10 +309,10 @@
<target state="translated">Umožňuje zobrazit seznam dostupných testů.</target>
<note />
</trans-unit>
<trans-unit id="PlatformCommandLineExitOnProcessExitInvalidDependantProcess">
<trans-unit id="PlatformCommandLineExitOnProcessExitInvalidDependentProcess">
<source>Invalid PID '{0}'
{1}</source>
<target state="translated">Neplatný identifikátor PID {0}
<target state="new">Invalid PID '{0}'
{1}</target>
<note />
</trans-unit>
Expand Down Expand Up @@ -371,11 +356,6 @@
<target state="translated">Nezobrazovat úvodní banner, zprávu o autorských právech ani banner telemetrie</target>
<note />
</trans-unit>
<trans-unit id="PlatformCommandLineOptionExpectsNoArgumentErrorMessage">
<source>'--{0}' expects no argument</source>
<target state="translated">--{0} neočekává žádný argument.</target>
<note />
</trans-unit>
<trans-unit id="PlatformCommandLinePortOptionDescription">
<source>Specify the port of the server.</source>
<target state="translated">Zadejte port serveru.</target>
Expand Down Expand Up @@ -576,11 +556,6 @@ Přečtěte si další informace o telemetrii Microsoft Testing Platform: https:
<target state="translated">Řetězec filtru by neměl být ukončen řídicím znakem {0}.</target>
<note />
</trans-unit>
<trans-unit id="TreeNodeFilterInvalidArgumentCount">
<source>A single argument is expected (e.g. '/MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]')</source>
<target state="translated">Očekává se jeden argument (například /MyAssembly/MyNamespace/MyClass/MyTestMethod*[OS=Linux]).</target>
<note />
</trans-unit>
<trans-unit id="TreeNodeFilterOnlyLastLevelCanContainMutiLevelWildcardErrorMessage">
<source>Only the final filter path can contain '**' wildcard</source>
<target state="translated">Zástupný znak ** může obsahovat pouze konečná cesta filtru.</target>
Expand Down