From 41d38e833c1a32d976b34ef814d69b3a759ba385 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 ++ .../ConfigurationManagerTests.cs | 12 +- 18 files changed, 318 insertions(+), 12 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/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