From fce6c4b7f88e25f8a6d47215921d7ea5cdb8dd97 Mon Sep 17 00:00:00 2001 From: Youssef1313 Date: Sun, 17 Nov 2024 16:48:22 +0100 Subject: [PATCH] Support rsp in MTP --- .../CommandLine/ParseResult.cs | 6 +- .../CommandLine/Parser.cs | 13 +- .../CommandLine/ResponseFileHelper.cs | 162 ++++++++++++++++++ .../Resources/PlatformResources.resx | 7 + .../Resources/xlf/PlatformResources.cs.xlf | 10 ++ .../Resources/xlf/PlatformResources.de.xlf | 10 ++ .../Resources/xlf/PlatformResources.es.xlf | 10 ++ .../Resources/xlf/PlatformResources.fr.xlf | 10 ++ .../Resources/xlf/PlatformResources.it.xlf | 10 ++ .../Resources/xlf/PlatformResources.ja.xlf | 10 ++ .../Resources/xlf/PlatformResources.ko.xlf | 10 ++ .../Resources/xlf/PlatformResources.pl.xlf | 10 ++ .../Resources/xlf/PlatformResources.pt-BR.xlf | 10 ++ .../Resources/xlf/PlatformResources.ru.xlf | 10 ++ .../Resources/xlf/PlatformResources.tr.xlf | 10 ++ .../xlf/PlatformResources.zh-Hans.xlf | 10 ++ .../xlf/PlatformResources.zh-Hant.xlf | 10 ++ .../CommandLine/CommandLineHandlerTests.cs | 2 +- .../CommandLine/CommandLineTests.cs | 46 ++--- .../ConfigurationManagerTests.cs | 12 +- 20 files changed, 342 insertions(+), 36 deletions(-) create mode 100644 src/Platform/Microsoft.Testing.Platform/CommandLine/ResponseFileHelper.cs diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/ParseResult.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/ParseResult.cs index 9d965377db..1291fedc58 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/ParseResult.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/ParseResult.cs @@ -5,11 +5,11 @@ namespace Microsoft.Testing.Platform.CommandLine; -internal sealed class CommandLineParseResult(string? toolName, IReadOnlyList options, IReadOnlyList errors, IReadOnlyList originalArguments) : IEquatable +internal sealed class CommandLineParseResult(string? toolName, IReadOnlyList options, IReadOnlyList errors) : IEquatable { public const char OptionPrefix = '-'; - public static CommandLineParseResult Empty => new(null, [], [], []); + public static CommandLineParseResult Empty => new(null, [], []); public string? ToolName { get; } = toolName; @@ -17,8 +17,6 @@ internal sealed class CommandLineParseResult(string? toolName, IReadOnlyList Errors { get; } = errors; - public IReadOnlyList OriginalArguments { get; } = originalArguments; - public bool HasError => Errors.Count > 0; public bool HasTool => ToolName is not null; diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/Parser.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/Parser.cs index 074d05cb5a..b3d03c6b6f 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/Parser.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/Parser.cs @@ -33,6 +33,9 @@ internal static class CommandLineParser /// * A POSIX convention lets you omit the delimiter when you are specifying a single-character option alias, i.e. myapp -vquiet. /// public static CommandLineParseResult Parse(string[] args, IEnvironment environment) + => Parse(args.ToList(), environment); + + private static CommandLineParseResult Parse(List args, IEnvironment environment) { List options = []; List errors = []; @@ -41,8 +44,14 @@ public static CommandLineParseResult Parse(string[] args, IEnvironment environme string? currentArg = null; string? toolName = null; List currentOptionArguments = []; - for (int i = 0; i < args.Length; i++) + for (int i = 0; i < args.Count; i++) { + if (args[i].StartsWith('@') && ResponseFileHelper.TryReadResponseFile(args[i].Substring(1), errors, out string[]? newArguments)) + { + args.InsertRange(i + 1, newArguments); + continue; + } + bool argumentHandled = false; currentArg = args[i]; @@ -118,7 +127,7 @@ public static CommandLineParseResult Parse(string[] args, IEnvironment environme options.Add(new(currentOption, currentOptionArguments.ToArray())); } - return new CommandLineParseResult(toolName, options, errors, args); + return new CommandLineParseResult(toolName, options, errors); static void ParseOptionAndSeparators(string arg, out string? currentOption, out string? currentArg) { diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/ResponseFileHelper.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/ResponseFileHelper.cs new file mode 100644 index 0000000000..f983808db3 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/ResponseFileHelper.cs @@ -0,0 +1,162 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics.CodeAnalysis; +using System.Globalization; + +using Microsoft.Testing.Platform.Resources; + +// Most of the core logic is from https://github.com/dotnet/command-line-api/blob/feb61c7f328a2401d74f4317b39d02126cfdfe24/src/System.CommandLine/Parsing/CliParser.cs#L49 +internal static class ResponseFileHelper +{ + internal static bool TryReadResponseFile(string rspFilePath, List errors, [NotNullWhen(true)] out string[]? newArguments) + { + try + { + newArguments = ExpandResponseFile(rspFilePath).ToArray(); + return true; + } + catch (FileNotFoundException) + { + errors.Add(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineParserResponseFileNotFound, rspFilePath)); + } + catch (IOException e) + { + errors.Add(string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineParserFailedToReadResponseFile, rspFilePath, e.Message)); + } + + newArguments = null; + return false; + + static IEnumerable ExpandResponseFile(string filePath) + { + string[] lines = File.ReadAllLines(filePath); + + for (int i = 0; i < lines.Length; i++) + { + string line = lines[i]; + + foreach (string p in SplitLine(line)) + { + yield return p; + } + } + } + + static IEnumerable SplitLine(string line) + { + string arg = line.Trim(); + + if (arg.Length == 0 || arg[0] == '#') + { + yield break; + } + + foreach (string word in SplitCommandLine(arg)) + { + yield return word; + } + } + } + + private enum Boundary + { + TokenStart, + WordEnd, + QuoteStart, + QuoteEnd, + } + + public static IEnumerable SplitCommandLine(string commandLine) + { + int startTokenIndex = 0; + + int pos = 0; + + Boundary seeking = Boundary.TokenStart; + Boundary seekingQuote = Boundary.QuoteStart; + + while (pos < commandLine.Length) + { + char c = commandLine[pos]; + + if (char.IsWhiteSpace(c)) + { + if (seekingQuote == Boundary.QuoteStart) + { + switch (seeking) + { + case Boundary.WordEnd: + yield return CurrentToken(); + startTokenIndex = pos; + seeking = Boundary.TokenStart; + break; + + case Boundary.TokenStart: + startTokenIndex = pos; + break; + } + } + } + else if (c == '\"') + { + if (seeking == Boundary.TokenStart) + { + switch (seekingQuote) + { + case Boundary.QuoteEnd: + yield return CurrentToken(); + startTokenIndex = pos; + seekingQuote = Boundary.QuoteStart; + break; + + case Boundary.QuoteStart: + startTokenIndex = pos + 1; + seekingQuote = Boundary.QuoteEnd; + break; + } + } + else + { + switch (seekingQuote) + { + case Boundary.QuoteEnd: + seekingQuote = Boundary.QuoteStart; + break; + + case Boundary.QuoteStart: + seekingQuote = Boundary.QuoteEnd; + break; + } + } + } + else if (seeking == Boundary.TokenStart && seekingQuote == Boundary.QuoteStart) + { + seeking = Boundary.WordEnd; + startTokenIndex = pos; + } + + Advance(); + + if (IsAtEndOfInput()) + { + switch (seeking) + { + case Boundary.TokenStart: + break; + default: + yield return CurrentToken(); + break; + } + } + } + + void Advance() => pos++; + + string CurrentToken() => commandLine.Substring(startTokenIndex, IndexOfEndOfToken()).ToString().Replace("\"", string.Empty); + + int IndexOfEndOfToken() => pos - startTokenIndex; + + bool IsAtEndOfInput() => pos == commandLine.Length; + } +} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx b/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx index 9128ec9304..b1dd4411d7 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx +++ b/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx @@ -655,4 +655,11 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is Exit code + + The response file '{0}' was not found + + + Failed to read response file '{0}'. {1}. + {1} is the exception + diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.cs.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.cs.xlf index 502f10b7e5..b33837e6fa 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.cs.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.cs.xlf @@ -117,6 +117,16 @@ Rozhraní ICommandLineOptions ještě není sestavené. + + Failed to read response file '{0}'. {1}. + Failed to read response file '{0}'. {1}. + {1} is the exception + + + The response file '{0}' was not found + The response file '{0}' was not found + + Unexpected argument {0} Neočekávaný argument {0} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.de.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.de.xlf index 7384723311..f8d567c9b7 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.de.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.de.xlf @@ -117,6 +117,16 @@ ICommandLineOptions wurde noch nicht erstellt. + + Failed to read response file '{0}'. {1}. + Failed to read response file '{0}'. {1}. + {1} is the exception + + + The response file '{0}' was not found + The response file '{0}' was not found + + Unexpected argument {0} Unerwartetes Argument {0} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.es.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.es.xlf index b1057b6026..eda3a9e8f0 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.es.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.es.xlf @@ -117,6 +117,16 @@ ICommandLineOptions aún no se ha compilado. + + Failed to read response file '{0}'. {1}. + Failed to read response file '{0}'. {1}. + {1} is the exception + + + The response file '{0}' was not found + The response file '{0}' was not found + + Unexpected argument {0} Argumento inesperado {0} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.fr.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.fr.xlf index d11948f1cb..7ecd0b5298 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.fr.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.fr.xlf @@ -117,6 +117,16 @@ ICommandLineOptions n’a pas encore été généré. + + Failed to read response file '{0}'. {1}. + Failed to read response file '{0}'. {1}. + {1} is the exception + + + The response file '{0}' was not found + The response file '{0}' was not found + + Unexpected argument {0} Arguments inattendue {0} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.it.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.it.xlf index a6fb960a87..20d1223985 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.it.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.it.xlf @@ -117,6 +117,16 @@ ICommandLineOptions non è stato ancora compilato. + + Failed to read response file '{0}'. {1}. + Failed to read response file '{0}'. {1}. + {1} is the exception + + + The response file '{0}' was not found + The response file '{0}' was not found + + Unexpected argument {0} Argomento imprevisto {0} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ja.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ja.xlf index 6ed3e67ad9..45aae8867f 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ja.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ja.xlf @@ -117,6 +117,16 @@ ICommandLineOptions はまだ構築されていません。 + + Failed to read response file '{0}'. {1}. + Failed to read response file '{0}'. {1}. + {1} is the exception + + + The response file '{0}' was not found + The response file '{0}' was not found + + Unexpected argument {0} 予期しない引数 {0} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ko.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ko.xlf index f14b6eb609..4c4da34607 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ko.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ko.xlf @@ -117,6 +117,16 @@ ICommandLineOptions가 아직 빌드되지 않았습니다. + + Failed to read response file '{0}'. {1}. + Failed to read response file '{0}'. {1}. + {1} is the exception + + + The response file '{0}' was not found + The response file '{0}' was not found + + Unexpected argument {0} 예기치 않은 인수 {0} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pl.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pl.xlf index 15b60aa192..7f8f32b46a 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pl.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pl.xlf @@ -117,6 +117,16 @@ Obiekt ICommandLineOptions nie został jeszcze skompilowany. + + Failed to read response file '{0}'. {1}. + Failed to read response file '{0}'. {1}. + {1} is the exception + + + The response file '{0}' was not found + The response file '{0}' was not found + + Unexpected argument {0} Nieoczekiwany argument {0} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pt-BR.xlf index 25cf5b6b64..2e38a7382c 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pt-BR.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pt-BR.xlf @@ -117,6 +117,16 @@ O ICommandLineOptions ainda não foi criado. + + Failed to read response file '{0}'. {1}. + Failed to read response file '{0}'. {1}. + {1} is the exception + + + The response file '{0}' was not found + The response file '{0}' was not found + + Unexpected argument {0} Argumento inesperado {0} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ru.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ru.xlf index 351aa80a96..0e33c3d227 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ru.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ru.xlf @@ -117,6 +117,16 @@ Параметр ICommandLineOptions еще не создан. + + Failed to read response file '{0}'. {1}. + Failed to read response file '{0}'. {1}. + {1} is the exception + + + The response file '{0}' was not found + The response file '{0}' was not found + + Unexpected argument {0} Неожиданный аргумент {0} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.tr.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.tr.xlf index 751d9d7d3d..16cd6c5a79 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.tr.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.tr.xlf @@ -117,6 +117,16 @@ ICommandLineOptions henüz derlenmedi. + + Failed to read response file '{0}'. {1}. + Failed to read response file '{0}'. {1}. + {1} is the exception + + + The response file '{0}' was not found + The response file '{0}' was not found + + Unexpected argument {0} {0} bağımsız değişkeni beklenmiyordu diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hans.xlf index 9e617c4e75..4e2c042134 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hans.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hans.xlf @@ -117,6 +117,16 @@ ICommandLineOptions 尚未生成。 + + Failed to read response file '{0}'. {1}. + Failed to read response file '{0}'. {1}. + {1} is the exception + + + The response file '{0}' was not found + The response file '{0}' was not found + + Unexpected argument {0} 意外的参数 {0} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hant.xlf index 9e7ef80435..64cf106a2d 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hant.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hant.xlf @@ -117,6 +117,16 @@ 尚未建置 ICommandLineOptions。 + + Failed to read response file '{0}'. {1}. + Failed to read response file '{0}'. {1}. + {1} is the exception + + + The response file '{0}' was not found + The response file '{0}' was not found + + Unexpected argument {0} 未預期的引數 {0} diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineHandlerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineHandlerTests.cs index eb75b61c2f..b7544c39ee 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineHandlerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineHandlerTests.cs @@ -243,7 +243,7 @@ public void GetOptionValue_OptionExists_ReturnsOptionValue() // Arrange OptionRecord optionRecord = new("name", ["value1", "value2"]); CommandLineHandler commandLineHandler = new( - new CommandLineParseResult(string.Empty, [optionRecord], [], []), _extensionCommandLineOptionsProviders, + new CommandLineParseResult(string.Empty, [optionRecord], []), _extensionCommandLineOptionsProviders, _systemCommandLineOptionsProviders, _testApplicationModuleInfoMock.Object, _runtimeFeatureMock.Object); // Act diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineTests.cs index b3c1493b67..5862369a58 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineTests.cs @@ -38,37 +38,37 @@ internal void ParserTests(int testNum, string[] args, CommandLineParseResult par internal static IEnumerable<(int TestNum, string[] Args, CommandLineParseResult ParseResult)> ParserTestsData() { - yield return (1, ["--option1", "a"], new CommandLineParseResult(null, new List { new("option1", ["a"]) }.ToArray(), [], [])); - yield return (2, ["--option1", "a", "b"], new CommandLineParseResult(null, new List { new("option1", ["a", "b"]) }.ToArray(), [], [])); - yield return (3, ["-option1", "a"], new CommandLineParseResult(null, new List { new("option1", ["a"]) }.ToArray(), [], [])); + yield return (1, ["--option1", "a"], new CommandLineParseResult(null, new List { new("option1", ["a"]) }.ToArray(), [])); + yield return (2, ["--option1", "a", "b"], new CommandLineParseResult(null, new List { new("option1", ["a", "b"]) }.ToArray(), [])); + yield return (3, ["-option1", "a"], new CommandLineParseResult(null, new List { new("option1", ["a"]) }.ToArray(), [])); yield return (4, ["--option1", "a", "-option2", "c"], new CommandLineParseResult(null, new List { new("option1", ["a"]), new("option2", ["c"]), - }.ToArray(), [], [])); - yield return (5, ["---option1", "a"], new CommandLineParseResult(null, new List().ToArray(), ["Unexpected argument ---option1", "Unexpected argument a"], [])); - yield return (6, ["--option1", "'a'"], new CommandLineParseResult(null, new List { new("option1", ["a"]) }.ToArray(), [], [])); + }.ToArray(), [])); + yield return (5, ["---option1", "a"], new CommandLineParseResult(null, new List().ToArray(), ["Unexpected argument ---option1", "Unexpected argument a"])); + yield return (6, ["--option1", "'a'"], new CommandLineParseResult(null, new List { new("option1", ["a"]) }.ToArray(), [])); yield return (7, ["--option1", "'a'", "--option2", "'hello'"], new CommandLineParseResult(null, new List { new("option1", ["a"]), new("option2", ["hello"]), - }.ToArray(), [], [])); - yield return (8, ["--option1", "'a'b'"], new CommandLineParseResult(null, new List { new("option1", []) }.ToArray(), ["Unexpected single quote in argument: 'a'b' for option option1"], [])); - yield return (9, ["option1", "--option1"], new CommandLineParseResult("option1", new List { new("option1", []) }.ToArray(), [], [])); - yield return (10, ["--option1", @"""\\"""], new CommandLineParseResult(null, new List { new("option1", ["\\"]) }.ToArray(), [], [])); - yield return (11, ["--option1", @" "" \"" "" "], new CommandLineParseResult(null, new List { new("option1", [" \" "]) }.ToArray(), [], [])); - yield return (12, ["--option1", @" "" \$ "" "], new CommandLineParseResult(null, new List { new("option1", [" $ "]) }.ToArray(), [], [])); - yield return (13, ["--option1", $@" "" \{Environment.NewLine} "" "], new CommandLineParseResult(null, new List { new("option1", [$" {Environment.NewLine} "]) }.ToArray(), [], [])); - yield return (14, ["--option1", "a"], new CommandLineParseResult(null, new List { new("option1", ["a"]) }.ToArray(), [], [])); - yield return (15, ["--option1:a"], new CommandLineParseResult(null, new List { new("option1", ["a"]) }.ToArray(), [], [])); - yield return (16, ["--option1=a"], new CommandLineParseResult(null, new List { new("option1", ["a"]) }.ToArray(), [], [])); - yield return (17, ["--option1=a", "--option1=b"], new CommandLineParseResult(null, new List { new("option1", ["a"]), new("option1", ["b"]) }.ToArray(), [], [])); - yield return (18, ["--option1=a", "--option1 b"], new CommandLineParseResult(null, new List { new("option1", ["a"]), new("option1", ["b"]) }.ToArray(), [], [])); - yield return (19, ["--option1=a=a"], new CommandLineParseResult(null, new List { new("option1", ["a=a"]) }.ToArray(), [], [])); - yield return (20, ["--option1=a:a"], new CommandLineParseResult(null, new List { new("option1", ["a:a"]) }.ToArray(), [], [])); - yield return (21, ["--option1:a=a"], new CommandLineParseResult(null, new List { new("option1", ["a=a"]) }.ToArray(), [], [])); - yield return (22, ["--option1:a:a"], new CommandLineParseResult(null, new List { new("option1", ["a:a"]) }.ToArray(), [], [])); - yield return (23, ["--option1:a:a", "--option1:a=a"], new CommandLineParseResult(null, new List { new("option1", ["a:a"]), new("option1", ["a=a"]) }.ToArray(), [], [])); + }.ToArray(), [])); + yield return (8, ["--option1", "'a'b'"], new CommandLineParseResult(null, new List { new("option1", []) }.ToArray(), ["Unexpected single quote in argument: 'a'b' for option option1"])); + yield return (9, ["option1", "--option1"], new CommandLineParseResult("option1", new List { new("option1", []) }.ToArray(), [])); + yield return (10, ["--option1", @"""\\"""], new CommandLineParseResult(null, new List { new("option1", ["\\"]) }.ToArray(), [])); + yield return (11, ["--option1", @" "" \"" "" "], new CommandLineParseResult(null, new List { new("option1", [" \" "]) }.ToArray(), [])); + yield return (12, ["--option1", @" "" \$ "" "], new CommandLineParseResult(null, new List { new("option1", [" $ "]) }.ToArray(), [])); + yield return (13, ["--option1", $@" "" \{Environment.NewLine} "" "], new CommandLineParseResult(null, new List { new("option1", [$" {Environment.NewLine} "]) }.ToArray(), [])); + yield return (14, ["--option1", "a"], new CommandLineParseResult(null, new List { new("option1", ["a"]) }.ToArray(), [])); + yield return (15, ["--option1:a"], new CommandLineParseResult(null, new List { new("option1", ["a"]) }.ToArray(), [])); + yield return (16, ["--option1=a"], new CommandLineParseResult(null, new List { new("option1", ["a"]) }.ToArray(), [])); + yield return (17, ["--option1=a", "--option1=b"], new CommandLineParseResult(null, new List { new("option1", ["a"]), new("option1", ["b"]) }.ToArray(), [])); + yield return (18, ["--option1=a", "--option1 b"], new CommandLineParseResult(null, new List { new("option1", ["a"]), new("option1", ["b"]) }.ToArray(), [])); + yield return (19, ["--option1=a=a"], new CommandLineParseResult(null, new List { new("option1", ["a=a"]) }.ToArray(), [])); + yield return (20, ["--option1=a:a"], new CommandLineParseResult(null, new List { new("option1", ["a:a"]) }.ToArray(), [])); + yield return (21, ["--option1:a=a"], new CommandLineParseResult(null, new List { new("option1", ["a=a"]) }.ToArray(), [])); + yield return (22, ["--option1:a:a"], new CommandLineParseResult(null, new List { new("option1", ["a:a"]) }.ToArray(), [])); + yield return (23, ["--option1:a:a", "--option1:a=a"], new CommandLineParseResult(null, new List { new("option1", ["a:a"]), new("option1", ["a=a"]) }.ToArray(), [])); } public void CommandLineOptionWithNumber_IsSupported() diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationManagerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationManagerTests.cs index 0aeff2b74b..8b516c7269 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationManagerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationManagerTests.cs @@ -35,7 +35,7 @@ public async ValueTask GetConfigurationValueFromJson(string jsonFileConfig, stri CurrentTestApplicationModuleInfo testApplicationModuleInfo = new(new SystemEnvironment(), new SystemProcessHandler()); ConfigurationManager configurationManager = new(fileSystem.Object, testApplicationModuleInfo); configurationManager.AddConfigurationSource(() => new JsonConfigurationSource(testApplicationModuleInfo, fileSystem.Object, null)); - IConfiguration configuration = await configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), Array.Empty(), Array.Empty())); + IConfiguration configuration = await configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), Array.Empty())); Assert.AreEqual(result, configuration[key], $"Expected '{result}' found '{configuration[key]}'"); } @@ -65,7 +65,7 @@ public async ValueTask InvalidJson_Fail() ConfigurationManager configurationManager = new(fileSystem.Object, testApplicationModuleInfo); configurationManager.AddConfigurationSource(() => new JsonConfigurationSource(testApplicationModuleInfo, fileSystem.Object, null)); - await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), Array.Empty(), Array.Empty()))); + await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), Array.Empty()))); } [ArgumentsProvider(nameof(GetConfigurationValueFromJsonData))] @@ -91,7 +91,7 @@ public async ValueTask GetConfigurationValueFromJsonWithFileLoggerProvider(strin configurationManager.AddConfigurationSource(() => new JsonConfigurationSource(testApplicationModuleInfo, fileSystem.Object, null)); - IConfiguration configuration = await configurationManager.BuildAsync(loggerProviderMock.Object, new CommandLineParseResult(null, new List(), Array.Empty(), Array.Empty())); + IConfiguration configuration = await configurationManager.BuildAsync(loggerProviderMock.Object, new CommandLineParseResult(null, new List(), Array.Empty())); Assert.AreEqual(result, configuration[key], $"Expected '{result}' found '{configuration[key]}'"); loggerMock.Verify(x => x.LogAsync(LogLevel.Trace, It.IsAny(), null, LoggingExtensions.Formatter), Times.Once); @@ -101,7 +101,7 @@ public async ValueTask BuildAsync_EmptyConfigurationSources_ThrowsException() { CurrentTestApplicationModuleInfo testApplicationModuleInfo = new(new SystemEnvironment(), new SystemProcessHandler()); ConfigurationManager configurationManager = new(new SystemFileSystem(), testApplicationModuleInfo); - await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), Array.Empty(), Array.Empty()))); + await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), Array.Empty()))); } public async ValueTask BuildAsync_ConfigurationSourcesNotEnabledAsync_ThrowsException() @@ -113,7 +113,7 @@ public async ValueTask BuildAsync_ConfigurationSourcesNotEnabledAsync_ThrowsExce ConfigurationManager configurationManager = new(new SystemFileSystem(), testApplicationModuleInfo); configurationManager.AddConfigurationSource(() => mockConfigurationSource.Object); - await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), Array.Empty(), Array.Empty()))); + await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), Array.Empty()))); mockConfigurationSource.Verify(x => x.IsEnabledAsync(), Times.Once); } @@ -132,7 +132,7 @@ public async ValueTask BuildAsync_ConfigurationSourceIsAsyncInitializableExtensi ConfigurationManager configurationManager = new(new SystemFileSystem(), testApplicationModuleInfo); configurationManager.AddConfigurationSource(() => fakeConfigurationSource); - await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), Array.Empty(), Array.Empty()))); + await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), Array.Empty()))); } private class FakeConfigurationSource : IConfigurationSource, IAsyncInitializableExtension