From 648f1f58eb095542a979ac14f20ff51090661deb Mon Sep 17 00:00:00 2001 From: Mathieu Cartoixa Date: Sat, 2 Jul 2022 21:31:25 +0200 Subject: [PATCH] Refactor the VSTestForwardingTask to use the MSBuild infrastructure (#680) --- .../ArgumentEscaper.cs | 100 --------- .../Microsoft.TestPlatform.targets | 1 + .../PublicAPI/PublicAPI.Unshipped.txt | 28 +-- .../Tasks/VSTestForwardingApp.cs | 62 ------ .../Tasks/VSTestForwardingTask.cs | 197 +++++++++--------- .../ArgumentEscaperTests.cs | 127 ----------- .../VsTestTaskTests.cs | 168 +++++++-------- 7 files changed, 185 insertions(+), 498 deletions(-) delete mode 100644 src/Microsoft.TestPlatform.Build/ArgumentEscaper.cs delete mode 100644 src/Microsoft.TestPlatform.Build/Tasks/VSTestForwardingApp.cs delete mode 100644 test/Microsoft.TestPlatform.Build.UnitTests/ArgumentEscaperTests.cs diff --git a/src/Microsoft.TestPlatform.Build/ArgumentEscaper.cs b/src/Microsoft.TestPlatform.Build/ArgumentEscaper.cs deleted file mode 100644 index 8d9f22b6b5..0000000000 --- a/src/Microsoft.TestPlatform.Build/ArgumentEscaper.cs +++ /dev/null @@ -1,100 +0,0 @@ -// 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; -using System.Text; - -namespace Microsoft.TestPlatform.Build.Utils; - -public static class ArgumentEscaper -{ - /// - /// Undo the processing which took place to create string[] args in Main, - /// so that the next process will receive the same string[] args - /// - /// See here for more info: - /// http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx - /// - /// - /// Return original string passed by client - public static string HandleEscapeSequenceInArgForProcessStart(string arg) - { - var sb = new StringBuilder(); - - var needsQuotes = ShouldSurroundWithQuotes(arg); - var isQuoted = needsQuotes || IsSurroundedWithQuotes(arg); - - if (needsQuotes) - { - sb.Append('\"'); - } - - for (int i = 0; i < arg.Length; ++i) - { - var backslashCount = 0; - - // Consume All Backslashes - while (i < arg.Length && arg[i] == '\\') - { - backslashCount++; - i++; - } - - // Escape any backslashes at the end of the arg - // when the argument is also quoted. - // This ensures the outside quote is interpreted as - // an argument delimiter - if (i == arg.Length && isQuoted) - { - sb.Append('\\', 2 * backslashCount); - } - - // At then end of the arg, which isn't quoted, - // just add the backslashes, no need to escape - else if (i == arg.Length) - { - sb.Append('\\', backslashCount); - } - - // Escape any preceding backslashes and the quote - else if (arg[i] == '"') - { - sb.Append('\\', (2 * backslashCount) + 1); - sb.Append('"'); - } - - // Output any consumed backslashes and the character - else - { - sb.Append('\\', backslashCount); - sb.Append(arg[i]); - } - } - - if (needsQuotes) - { - sb.Append('\"'); - } - - return sb.ToString(); - } - - internal static bool ShouldSurroundWithQuotes(string argument) - { - // Don't quote already quoted strings - if (IsSurroundedWithQuotes(argument)) - { - return false; - } - - // Only quote if whitespace exists in the string - return ArgumentContainsWhitespace(argument); - } - - internal static bool IsSurroundedWithQuotes(string argument) - => argument.StartsWith("\"", StringComparison.Ordinal) - && argument.EndsWith("\"", StringComparison.Ordinal); - - internal static bool ArgumentContainsWhitespace(string argument) - => argument.Contains(" ") || argument.Contains("\t") || argument.Contains("\n"); -} diff --git a/src/Microsoft.TestPlatform.Build/Microsoft.TestPlatform.targets b/src/Microsoft.TestPlatform.Build/Microsoft.TestPlatform.targets index 59e3dce9a1..a55c1e4aa6 100644 --- a/src/Microsoft.TestPlatform.Build/Microsoft.TestPlatform.targets +++ b/src/Microsoft.TestPlatform.Build/Microsoft.TestPlatform.targets @@ -14,6 +14,7 @@ Copyright (c) .NET Foundation. All rights reserved. Microsoft.TestPlatform.Build.dll $([System.IO.Path]::Combine($(MSBuildThisFileDirectory),"vstest.console.dll")) + False diff --git a/src/Microsoft.TestPlatform.Build/PublicAPI/PublicAPI.Unshipped.txt b/src/Microsoft.TestPlatform.Build/PublicAPI/PublicAPI.Unshipped.txt index 84e3b8a3f2..ba5b4e8453 100644 --- a/src/Microsoft.TestPlatform.Build/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Microsoft.TestPlatform.Build/PublicAPI/PublicAPI.Unshipped.txt @@ -1,8 +1,4 @@ #nullable enable -Microsoft.TestPlatform.Build.Tasks.VSTestForwardingApp -Microsoft.TestPlatform.Build.Tasks.VSTestForwardingApp.Cancel() -> void -Microsoft.TestPlatform.Build.Tasks.VSTestForwardingApp.Execute() -> int -Microsoft.TestPlatform.Build.Tasks.VSTestForwardingApp.VSTestForwardingApp(string! vsTestExePath, System.Collections.Generic.IEnumerable! argsToForward) -> void Microsoft.TestPlatform.Build.Tasks.VSTestLogsTask Microsoft.TestPlatform.Build.Tasks.VSTestLogsTask.LogType.get -> string? Microsoft.TestPlatform.Build.Tasks.VSTestLogsTask.LogType.set -> void @@ -11,19 +7,19 @@ Microsoft.TestPlatform.Build.Tasks.VSTestLogsTask.ProjectFilePath.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestLogsTask.VSTestLogsTask() -> void Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.Cancel() -> void -Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.TestFileFullPath.get -> string? +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.TestFileFullPath.get -> Microsoft.Build.Framework.ITaskItem? Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.TestFileFullPath.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestArtifactsProcessingMode.get -> string? Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestArtifactsProcessingMode.set -> void -Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlame.get -> string? +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlame.get -> bool Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlame.set -> void -Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlameCrash.get -> string? +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlameCrash.get -> bool Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlameCrash.set -> void -Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlameCrashCollectAlways.get -> string? +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlameCrashCollectAlways.get -> bool Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlameCrashCollectAlways.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlameCrashDumpType.get -> string? Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlameCrashDumpType.set -> void -Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlameHang.get -> string? +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlameHang.get -> bool Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlameHang.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlameHangDumpType.get -> string? Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlameHangDumpType.set -> void @@ -33,39 +29,37 @@ Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestCLIRunSettings.get Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestCLIRunSettings.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestCollect.get -> string![]? Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestCollect.set -> void -Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestConsolePath.get -> string! +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestConsolePath.get -> Microsoft.Build.Framework.ITaskItem? Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestConsolePath.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestDiag.get -> string? Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestDiag.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestFramework.get -> string? Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestFramework.set -> void -Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestListTests.get -> string? +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestListTests.get -> bool Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestListTests.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestLogger.get -> string![]? Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestLogger.set -> void -Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestNoLogo.get -> string? +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestNoLogo.get -> bool Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestNoLogo.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestPlatform.get -> string? Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestPlatform.set -> void -Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestResultsDirectory.get -> string? +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestResultsDirectory.get -> Microsoft.Build.Framework.ITaskItem? Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestResultsDirectory.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestSessionCorrelationId.get -> string? Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestSessionCorrelationId.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestSetting.get -> string? Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestSetting.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestForwardingTask() -> void -Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestTestAdapterPath.get -> string![]? +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestTestAdapterPath.get -> Microsoft.Build.Framework.ITaskItem![]? Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestTestAdapterPath.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestTestCaseFilter.get -> string? Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestTestCaseFilter.set -> void -Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestTraceDataCollectorDirectoryPath.get -> string? +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestTraceDataCollectorDirectoryPath.get -> Microsoft.Build.Framework.ITaskItem? Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestTraceDataCollectorDirectoryPath.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestVerbosity.get -> string? Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestVerbosity.set -> void Microsoft.TestPlatform.Build.Trace.Tracing -Microsoft.TestPlatform.Build.Utils.ArgumentEscaper override Microsoft.TestPlatform.Build.Tasks.VSTestLogsTask.Execute() -> bool override Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.Execute() -> bool static Microsoft.TestPlatform.Build.Trace.Tracing.Trace(string! message) -> void static Microsoft.TestPlatform.Build.Trace.Tracing.traceEnabled -> bool -static Microsoft.TestPlatform.Build.Utils.ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(string! arg) -> string! diff --git a/src/Microsoft.TestPlatform.Build/Tasks/VSTestForwardingApp.cs b/src/Microsoft.TestPlatform.Build/Tasks/VSTestForwardingApp.cs deleted file mode 100644 index 618cdf342c..0000000000 --- a/src/Microsoft.TestPlatform.Build/Tasks/VSTestForwardingApp.cs +++ /dev/null @@ -1,62 +0,0 @@ -// 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; -using System.Collections.Generic; -using System.Diagnostics; - -using Microsoft.TestPlatform.Build.Trace; -using Microsoft.TestPlatform.Build.Utils; - -namespace Microsoft.TestPlatform.Build.Tasks; - -public class VSTestForwardingApp -{ - private const string HostExe = "dotnet"; - private readonly List _allArgs = new(); - private int _activeProcessId; - - public VSTestForwardingApp(string vsTestExePath, IEnumerable argsToForward) - { - _allArgs.Add("exec"); - - // Ensure that path to vstest.console is whitespace friendly. User may install - // dotnet-cli to any folder containing whitespace (e.g. VS installs to program files). - // Arguments are already whitespace friendly. - _allArgs.Add(ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(vsTestExePath)); - _allArgs.AddRange(argsToForward); - } - - public int Execute() - { - var processInfo = new ProcessStartInfo - { - FileName = HostExe, - Arguments = string.Join(" ", _allArgs), - UseShellExecute = false, - }; - - Tracing.Trace("VSTest: Starting vstest.console..."); - Tracing.Trace("VSTest: Arguments: " + processInfo.FileName + " " + processInfo.Arguments); - - using var activeProcess = new Process { StartInfo = processInfo }; - activeProcess.Start(); - _activeProcessId = activeProcess.Id; - - activeProcess.WaitForExit(); - Tracing.Trace("VSTest: Exit code: " + activeProcess.ExitCode); - return activeProcess.ExitCode; - } - - public void Cancel() - { - try - { - Process.GetProcessById(_activeProcessId).Kill(); - } - catch (ArgumentException ex) - { - Tracing.Trace($"VSTest: Killing process throws ArgumentException with the following message {ex}. It may be that process is not running"); - } - } -} diff --git a/src/Microsoft.TestPlatform.Build/Tasks/VSTestForwardingTask.cs b/src/Microsoft.TestPlatform.Build/Tasks/VSTestForwardingTask.cs index 59f62ce275..cbe7e2fb96 100644 --- a/src/Microsoft.TestPlatform.Build/Tasks/VSTestForwardingTask.cs +++ b/src/Microsoft.TestPlatform.Build/Tasks/VSTestForwardingTask.cs @@ -10,69 +10,42 @@ using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using Microsoft.TestPlatform.Build.Trace; -using Microsoft.TestPlatform.Build.Utils; namespace Microsoft.TestPlatform.Build.Tasks; public class VSTestForwardingTask : Task, ICancelableTask { - // The process which is invoking vstest.console - private VSTestForwardingApp? _vsTestForwardingApp; + private int _activeProcessId; + private const string DotnetExe = "dotnet"; private const string VsTestAppName = "vstest.console.dll"; - private const string CodeCovergaeString = "Code Coverage"; - - public string? TestFileFullPath { get; set; } + private const string CodeCoverageString = "Code Coverage"; + public ITaskItem? TestFileFullPath { get; set; } public string? VSTestSetting { get; set; } - - public string[]? VSTestTestAdapterPath { get; set; } - + public ITaskItem[]? VSTestTestAdapterPath { get; set; } public string? VSTestFramework { get; set; } - public string? VSTestPlatform { get; set; } - public string? VSTestTestCaseFilter { get; set; } - public string[]? VSTestLogger { get; set; } - - public string? VSTestListTests { get; set; } - + public bool VSTestListTests { get; set; } public string? VSTestDiag { get; set; } - public string[]? VSTestCLIRunSettings { get; set; } - [Required] - // Initialized to empty string to allow declaring as non-nullable, the property is marked as - // required so we can ensure that the property is set to non-null before the task is executed. - public string VSTestConsolePath { get; set; } = ""; - - public string? VSTestResultsDirectory { get; set; } - + public ITaskItem? VSTestConsolePath { get; set; } + public ITaskItem? VSTestResultsDirectory { get; set; } public string? VSTestVerbosity { get; set; } - public string[]? VSTestCollect { get; set; } - - public string? VSTestBlame { get; set; } - - public string? VSTestBlameCrash { get; set; } - + public bool VSTestBlame { get; set; } + public bool VSTestBlameCrash { get; set; } public string? VSTestBlameCrashDumpType { get; set; } - - public string? VSTestBlameCrashCollectAlways { get; set; } - - public string? VSTestBlameHang { get; set; } - + public bool VSTestBlameCrashCollectAlways { get; set; } + public bool VSTestBlameHang { get; set; } public string? VSTestBlameHangDumpType { get; set; } - public string? VSTestBlameHangTimeout { get; set; } - - public string? VSTestTraceDataCollectorDirectoryPath { get; set; } - - public string? VSTestNoLogo { get; set; } - + public ITaskItem? VSTestTraceDataCollectorDirectoryPath { get; set; } + public bool VSTestNoLogo { get; set; } public string? VSTestArtifactsProcessingMode { get; set; } - public string? VSTestSessionCorrelationId { get; set; } public override bool Execute() @@ -101,87 +74,118 @@ public override bool Execute() var allowfailureWithoutError = BuildEngine.GetType().GetProperty("AllowFailureWithoutError"); allowfailureWithoutError?.SetValue(BuildEngine, true); - _vsTestForwardingApp = new VSTestForwardingApp(VSTestConsolePath, CreateArgument()); + var processInfo = new ProcessStartInfo + { + FileName = DotnetExe, + Arguments = CreateArguments(), + UseShellExecute = false, + }; + if (!VSTestFramework.IsNullOrEmpty()) { Console.WriteLine(Resources.Resources.TestRunningSummary, TestFileFullPath, VSTestFramework); } - return _vsTestForwardingApp.Execute() == 0; + Tracing.Trace("VSTest: Starting vstest.console..."); + Tracing.Trace("VSTest: Arguments: " + processInfo.FileName + " " + processInfo.Arguments); + + using var activeProcess = new Process { StartInfo = processInfo }; + activeProcess.Start(); + _activeProcessId = activeProcess.Id; + + activeProcess.WaitForExit(); + Tracing.Trace("VSTest: Exit code: " + activeProcess.ExitCode); + return activeProcess.ExitCode == 0; } public void Cancel() { Tracing.Trace("VSTest: Killing the process..."); - _vsTestForwardingApp?.Cancel(); + try + { + Process.GetProcessById(_activeProcessId).Kill(); + } + catch (ArgumentException ex) + { + Tracing.Trace(string.Format("VSTest: Killing process throws ArgumentException with the following message {0}. It may be that process is not running", ex)); + } } - internal IEnumerable CreateArgument() + internal string CreateArguments() { - var allArgs = AddArgs(); + var builder = new CommandLineBuilder(); + builder.AppendSwitch("exec"); + if (VSTestConsolePath != null && !VSTestConsolePath.ItemSpec.IsNullOrEmpty()) + { + builder.AppendSwitchIfNotNull("", VSTestConsolePath); + } + else + { + builder.AppendSwitch(VsTestAppName); + } + + CreateCommandLineArguments(builder); // VSTestCLIRunSettings should be last argument in allArgs as vstest.console ignore options after "--"(CLIRunSettings option). - AddCliRunSettingsArgs(allArgs); + AddCliRunSettingsArgs(builder); - return allArgs; + return builder.ToString(); } - private void AddCliRunSettingsArgs(List allArgs) + private void AddCliRunSettingsArgs(CommandLineBuilder builder) { - if (VSTestCLIRunSettings != null && VSTestCLIRunSettings.Length > 0) + if (VSTestCLIRunSettings != null && VSTestCLIRunSettings.Any()) { - allArgs.Add("--"); + builder.AppendSwitch("--"); foreach (var arg in VSTestCLIRunSettings) { - allArgs.Add(ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(arg)); + builder.AppendSwitchIfNotNull(string.Empty, arg); } } } - private List AddArgs() + private void CreateCommandLineArguments(CommandLineBuilder builder) { var isConsoleLoggerSpecifiedByUser = false; var isCollectCodeCoverageEnabled = false; var isRunSettingsEnabled = false; - var allArgs = new List(); // TODO log arguments in task if (!VSTestSetting.IsNullOrEmpty()) { isRunSettingsEnabled = true; - allArgs.Add("--settings:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(VSTestSetting)); + builder.AppendSwitchIfNotNull("--settings:", VSTestSetting); } - if (VSTestTestAdapterPath != null && VSTestTestAdapterPath.Length > 0) + if (VSTestTestAdapterPath != null && VSTestTestAdapterPath.Any()) { foreach (var arg in VSTestTestAdapterPath) { - allArgs.Add("--testAdapterPath:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(arg)); + builder.AppendSwitchIfNotNull("--testAdapterPath:", arg); } } if (!VSTestFramework.IsNullOrEmpty()) { - allArgs.Add("--framework:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(VSTestFramework)); + builder.AppendSwitchIfNotNull("--framework:", VSTestFramework); } // vstest.console only support x86 and x64 for argument platform if (!VSTestPlatform.IsNullOrEmpty() && !VSTestPlatform.Contains("AnyCPU")) { - allArgs.Add("--platform:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(VSTestPlatform)); + builder.AppendSwitchIfNotNull("--platform:", VSTestPlatform); } if (!VSTestTestCaseFilter.IsNullOrEmpty()) { - allArgs.Add("--testCaseFilter:" + - ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(VSTestTestCaseFilter)); + builder.AppendSwitchIfNotNull("--testCaseFilter:", VSTestTestCaseFilter); } if (VSTestLogger != null && VSTestLogger.Length > 0) { foreach (var arg in VSTestLogger) { - allArgs.Add("--logger:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(arg)); + builder.AppendSwitchIfNotNull("--logger:", arg); if (arg.StartsWith("console", StringComparison.OrdinalIgnoreCase)) { @@ -190,29 +194,28 @@ private List AddArgs() } } - if (!VSTestResultsDirectory.IsNullOrEmpty()) + if (VSTestResultsDirectory != null && !VSTestResultsDirectory.ItemSpec.IsNullOrEmpty()) { - allArgs.Add("--resultsDirectory:" + - ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(VSTestResultsDirectory)); + builder.AppendSwitchIfNotNull("--resultsDirectory:", VSTestResultsDirectory); } - if (!VSTestListTests.IsNullOrEmpty()) + if (VSTestListTests) { - allArgs.Add("--listTests"); + builder.AppendSwitch("--listTests"); } if (!VSTestDiag.IsNullOrEmpty()) { - allArgs.Add("--Diag:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(VSTestDiag)); + builder.AppendSwitchIfNotNull("--Diag:", VSTestDiag); } - if (TestFileFullPath.IsNullOrEmpty()) + if (TestFileFullPath == null) { Log.LogError("Test file path cannot be empty or null."); } else { - allArgs.Add(ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(TestFileFullPath)); + builder.AppendFileNameIfNotNull(TestFileFullPath); } // Console logger was not specified by user, but verbosity was, hence add default console logger with verbosity as specified @@ -231,22 +234,18 @@ private List AddArgs() vsTestVerbosity = "quiet"; } - allArgs.Add("--logger:Console;Verbosity=" + vsTestVerbosity); + builder.AppendSwitchUnquotedIfNotNull("--logger:", $"Console;Verbosity={vsTestVerbosity}"); } - var blameCrash = !VSTestBlameCrash.IsNullOrEmpty(); - var blameHang = !VSTestBlameHang.IsNullOrEmpty(); - if (!VSTestBlame.IsNullOrEmpty() || blameCrash || blameHang) + if (VSTestBlame || VSTestBlameCrash || VSTestBlameHang) { - var blameArgs = "--Blame"; - var dumpArgs = new List(); - if (blameCrash || blameHang) + if (VSTestBlameCrash || VSTestBlameHang) { - if (blameCrash) + if (VSTestBlameCrash) { dumpArgs.Add("CollectDump"); - if (!VSTestBlameCrashCollectAlways.IsNullOrEmpty()) + if (VSTestBlameCrashCollectAlways) { dumpArgs.Add($"CollectAlways={VSTestBlameCrashCollectAlways}"); } @@ -257,7 +256,7 @@ private List AddArgs() } } - if (blameHang) + if (VSTestBlameHang) { dumpArgs.Add("CollectHangDump"); @@ -271,17 +270,19 @@ private List AddArgs() dumpArgs.Add($"TestTimeout={VSTestBlameHangTimeout}"); } } - - if (dumpArgs.Any()) - { - blameArgs += $":\"{string.Join(";", dumpArgs)}\""; - } } - allArgs.Add(blameArgs); + if (dumpArgs.Any()) + { + builder.AppendSwitchIfNotNull("--Blame:", string.Join(";", dumpArgs)); + } + else + { + builder.AppendSwitch("--Blame"); + } } - if (VSTestCollect != null && VSTestCollect.Length > 0) + if (VSTestCollect != null && VSTestCollect.Any()) { foreach (var arg in VSTestCollect) { @@ -289,13 +290,13 @@ private List AddArgs() // Split the argument with ';' and compare first token value. var tokens = arg.Split(';'); - if (arg.Equals(CodeCovergaeString, StringComparison.OrdinalIgnoreCase) || - tokens[0].Equals(CodeCovergaeString, StringComparison.OrdinalIgnoreCase)) + if (arg.Equals(CodeCoverageString, StringComparison.OrdinalIgnoreCase) || + tokens[0].Equals(CodeCoverageString, StringComparison.OrdinalIgnoreCase)) { isCollectCodeCoverageEnabled = true; } - allArgs.Add("--collect:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(arg)); + builder.AppendSwitchIfNotNull("--collect:", arg); } } @@ -308,11 +309,9 @@ private List AddArgs() // 2. Impact of adding adapter path always is minimal. (worst case: loads additional data collector assembly in datacollector process.) // This is required due to currently trace datacollector not ships with dotnet sdk, can be remove once we have // go code coverage x-plat. - if (!VSTestTraceDataCollectorDirectoryPath.IsNullOrEmpty()) + if (VSTestTraceDataCollectorDirectoryPath != null && !VSTestTraceDataCollectorDirectoryPath.ItemSpec.IsNullOrEmpty()) { - allArgs.Add("--testAdapterPath:" + - ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart( - VSTestTraceDataCollectorDirectoryPath)); + builder.AppendSwitchIfNotNull("--testAdapterPath:", VSTestTraceDataCollectorDirectoryPath); } else { @@ -325,21 +324,19 @@ private List AddArgs() } } - if (!VSTestNoLogo.IsNullOrWhiteSpace()) + if (VSTestNoLogo) { - allArgs.Add("--nologo"); + builder.AppendSwitch("--nologo"); } if (!VSTestArtifactsProcessingMode.IsNullOrEmpty() && VSTestArtifactsProcessingMode.Equals("collect", StringComparison.OrdinalIgnoreCase)) { - allArgs.Add("--artifactsProcessingMode-collect"); + builder.AppendSwitch("--artifactsProcessingMode-collect"); } if (!VSTestSessionCorrelationId.IsNullOrEmpty()) { - allArgs.Add("--testSessionCorrelationId:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(VSTestSessionCorrelationId)); + builder.AppendSwitchIfNotNull("--testSessionCorrelationId:", VSTestSessionCorrelationId); } - - return allArgs; } } diff --git a/test/Microsoft.TestPlatform.Build.UnitTests/ArgumentEscaperTests.cs b/test/Microsoft.TestPlatform.Build.UnitTests/ArgumentEscaperTests.cs deleted file mode 100644 index 1508f313e6..0000000000 --- a/test/Microsoft.TestPlatform.Build.UnitTests/ArgumentEscaperTests.cs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Microsoft.TestPlatform.Build.Utils.UnitTests; - -[TestClass] -public class ArgumentEscaperTests -{ - [TestMethod] - public void EscapeArgForProcessStartShouldAddDoubleQuoteIfThereIsSpace() - { - string stringWithSpace = "Some string"; - - string expected = "\"Some string\""; - string result = ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(stringWithSpace); - - Assert.AreEqual(expected, result); - } - - [TestMethod] - public void EscapeArgForProcessStartShouldAddDoubleQuoteIfThereIsSpaceAtEnd() - { - string stringWithSpaceAtEnd = "Some string "; - - string expected = "\"Some string \""; - string result = ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(stringWithSpaceAtEnd); - - Assert.AreEqual(expected, result); - } - - [TestMethod] - public void EscapeArgForProcessStartShouldHandleForwardSlash() - { - string stringWithForwardSlash = "Some/string"; - - string expected = "Some/string"; - string result = ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(stringWithForwardSlash); - - Assert.AreEqual(expected, result); - } - - [TestMethod] - public void EscapeArgForProcessStartShouldPreserveDoubleQuote() - { - string stringWithDoubleQuote = "Some\"string"; - - string expected = "Some\\\"string"; - string result = ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(stringWithDoubleQuote); - - Assert.AreEqual(expected, result); - } - - [TestMethod] - public void EscapeArgForProcessStartShouldPreserveSingleQuote() - { - string stringWithSingleQuote = "Some'string"; - - string expected = "Some'string"; - string result = ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(stringWithSingleQuote); - - Assert.AreEqual(expected, result); - } - - [TestMethod] - public void EscapeArgForProcessStartShouldPreserveBackSlash() - { - string stringWithBackSlash = @"Some\\string"; - - string expected = "Some\\\\string"; - string result = ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(stringWithBackSlash); - - Assert.AreEqual(expected, result); - } - - [TestMethod] - public void EscapeArgForProcessStartShouldPreserveBackSlashIfStringHasWhiteSpace() - { - string stringWithBackSlash = @"Some string With Space\\"; - - string expected = @"""Some string With Space\\\\"""; - string result = ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(stringWithBackSlash); - - Assert.AreEqual(expected, result); - } - - [TestMethod] - public void ShouldSurroundWithQuotesShouldReturnFalseIfAlreadySurroundWithQuotes() - { - string stringSurroundWithQuotes = "\"some string\""; - - Assert.IsFalse(ArgumentEscaper.ShouldSurroundWithQuotes(stringSurroundWithQuotes)); - } - - [TestMethod] - public void ShouldSurroundWithQuotesShouldReturnFalseIfItIsNotSurroundWithQuotesAndHasNoWhiteSpace() - { - string stringWithoutSpace = "someStringWithNoWhiteSpace"; - - Assert.IsFalse(ArgumentEscaper.ShouldSurroundWithQuotes(stringWithoutSpace)); - } - - [TestMethod] - public void ShouldSurroundWithQuotesShouldReturnTrueIfItIsNotSurroundWithQuotesAndHasWhiteSpace() - { - string stringWithSpace = "some String With WhiteSpace"; - - Assert.IsTrue(ArgumentEscaper.ShouldSurroundWithQuotes(stringWithSpace)); - } - - [TestMethod] - public void IsSurroundedWithQuotesShouldReturnTrueIfStringIsSurrondedByQuotes() - { - string stringSurroundWithQuotes = "\"some string\""; - - Assert.IsTrue(ArgumentEscaper.IsSurroundedWithQuotes(stringSurroundWithQuotes)); - } - - [TestMethod] - public void IsSurroundedWithQuotesShouldReturnFalseIfStringIsNotSurrondedByQuotes() - { - string stringNotSurroundWithQuotes = "some string"; - - Assert.IsFalse(ArgumentEscaper.IsSurroundedWithQuotes(stringNotSurroundWithQuotes)); - } -} diff --git a/test/Microsoft.TestPlatform.Build.UnitTests/VsTestTaskTests.cs b/test/Microsoft.TestPlatform.Build.UnitTests/VsTestTaskTests.cs index d0ad688f43..27a5f852bb 100644 --- a/test/Microsoft.TestPlatform.Build.UnitTests/VsTestTaskTests.cs +++ b/test/Microsoft.TestPlatform.Build.UnitTests/VsTestTaskTests.cs @@ -1,11 +1,11 @@ // 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; -using System.Linq; +using System.Text.RegularExpressions; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; using Microsoft.TestPlatform.Build.Tasks; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.TestPlatform.Build.UnitTests; @@ -19,7 +19,7 @@ public VsTestTaskTests() { _vsTestTask = new VSTestForwardingTask { - TestFileFullPath = @"C:\path\to\test-assembly.dll", + TestFileFullPath = new TaskItem(@"C:\path\to\test-assembly.dll"), VSTestFramework = ".NETCoreapp,Version2.0" }; } @@ -34,22 +34,20 @@ public void CreateArgumentShouldAddOneEntryForCLIRunSettings() _vsTestTask.VSTestCLIRunSettings[0] = arg1; _vsTestTask.VSTestCLIRunSettings[1] = arg2; - var result = _vsTestTask.CreateArgument().ToArray(); - - Assert.AreEqual(5, result.Length); + var commandline = _vsTestTask.CreateArguments(); - // First, second and third args would be framework:".NETCoreapp,Version2.0", testfilepath and -- respectively. - Assert.AreEqual($"\"{arg1}\"", result[3]); - Assert.AreEqual($"{arg2}", result[4]); + StringAssert.Contains(commandline, " -- "); + StringAssert.Contains(commandline, $"\"{arg1}\""); + StringAssert.Contains(commandline, $"{arg2}"); } [TestMethod] - public void CreateArgumentShouldAddCliRunSettingsArgAtEnd() + public void CreateArgumentShouldAddCLIRunSettingsArgAtEnd() { const string codeCoverageOption = "Code Coverage"; _vsTestTask.VSTestCollect = new string[] { codeCoverageOption }; - _vsTestTask.VSTestBlame = "Blame"; + _vsTestTask.VSTestBlame = true; const string arg1 = "RunConfiguration.ResultsDirectory=Path having Space"; const string arg2 = "MSTest.DeploymentEnabled"; @@ -58,24 +56,22 @@ public void CreateArgumentShouldAddCliRunSettingsArgAtEnd() _vsTestTask.VSTestCLIRunSettings[0] = arg1; _vsTestTask.VSTestCLIRunSettings[1] = arg2; - var result = _vsTestTask.CreateArgument().ToArray(); - - Assert.AreEqual(7, result.Length); + var commandline = _vsTestTask.CreateArguments(); - // Following are expected --framework:".NETCoreapp,Version2.0", testfilepath, blame, collect:"Code coverage" -- respectively. - Assert.AreEqual($"\"{arg1}\"", result[5]); - Assert.AreEqual($"{arg2}", result[6]); + StringAssert.Contains(commandline, " -- "); + StringAssert.Contains(commandline, $"\"{arg1}\""); + StringAssert.Contains(commandline, $"{arg2}"); } [TestMethod] public void CreateArgumentShouldPassResultsDirectoryCorrectly() { const string resultsDirectoryValue = @"C:\tmp\Results Directory"; - _vsTestTask.VSTestResultsDirectory = resultsDirectoryValue; + _vsTestTask.VSTestResultsDirectory = new TaskItem(resultsDirectoryValue); - var result = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - Assert.AreEqual($"--resultsDirectory:\"{resultsDirectoryValue}\"", result[1]); + StringAssert.Contains(commandline, $"--resultsDirectory:\"{_vsTestTask.VSTestResultsDirectory?.ItemSpec}\""); } [TestMethod] @@ -84,10 +80,10 @@ public void CreateArgumentShouldNotSetConsoleLoggerVerbosityIfConsoleLoggerIsGiv _vsTestTask.VSTestVerbosity = "diag"; _vsTestTask.VSTestLogger = new string[] { "Console;Verbosity=quiet" }; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - Assert.IsNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=normal"))); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=quiet"))); + StringAssert.DoesNotMatch(commandline, new Regex("(--logger:\"Console;Verbosity=normal\")")); + StringAssert.Contains(commandline, "--logger:\"Console;Verbosity=quiet\""); } [TestMethod] @@ -95,9 +91,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToNormalIfConsoleLogger { _vsTestTask.VSTestVerbosity = "n"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=normal"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=normal"); } [TestMethod] @@ -105,9 +101,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToNormalIfConsoleLogger { _vsTestTask.VSTestVerbosity = "normal"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=normal"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=normal"); } [TestMethod] @@ -115,9 +111,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToNormalIfConsoleLogger { _vsTestTask.VSTestVerbosity = "d"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=normal"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=normal"); } [TestMethod] @@ -125,9 +121,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToNormalIfConsoleLogger { _vsTestTask.VSTestVerbosity = "detailed"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=normal"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=normal"); } [TestMethod] @@ -135,9 +131,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToNormalIfConsoleLogger { _vsTestTask.VSTestVerbosity = "diag"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=normal"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=normal"); } [TestMethod] @@ -145,9 +141,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToNormalIfConsoleLogger { _vsTestTask.VSTestVerbosity = "diagnostic"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=normal"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=normal"); } [TestMethod] @@ -155,9 +151,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToQuietIfConsoleLoggerI { _vsTestTask.VSTestVerbosity = "q"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=quiet"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=quiet"); } [TestMethod] @@ -165,9 +161,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToQuietIfConsoleLoggerI { _vsTestTask.VSTestVerbosity = "quiet"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=quiet"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=quiet"); } [TestMethod] @@ -175,9 +171,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToMinimalIfConsoleLogge { _vsTestTask.VSTestVerbosity = "m"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=minimal"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=minimal"); } [TestMethod] @@ -185,9 +181,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToMinimalIfConsoleLogge { _vsTestTask.VSTestVerbosity = "minimal"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=minimal"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=minimal"); } [TestMethod] @@ -195,9 +191,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToNormalIfConsoleLogger { _vsTestTask.VSTestVerbosity = "Normal"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=normal"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=normal"); } [TestMethod] @@ -205,9 +201,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToQuietIfConsoleLoggerI { _vsTestTask.VSTestVerbosity = "Quiet"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=quiet"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=quiet"); } [TestMethod] @@ -215,9 +211,9 @@ public void CreateArgumentShouldPreserveWhiteSpaceInLogger() { _vsTestTask.VSTestLogger = new string[] { "trx;LogFileName=foo bar.trx" }; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:\"trx;LogFileName=foo bar.trx\""))); + StringAssert.Contains(commandline, "--logger:\"trx;LogFileName=foo bar.trx\""); } [TestMethod] @@ -228,103 +224,91 @@ public void CreateArgumentShouldAddOneCollectArgumentForEachCollect() _vsTestTask.VSTestCollect[0] = "name1"; _vsTestTask.VSTestCollect[1] = "name 2"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--collect:name1"))); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--collect:\"name 2\""))); + StringAssert.Contains(commandline, "--collect:name1"); + StringAssert.Contains(commandline, "--collect:\"name 2\""); } [TestMethod] public void CreateArgumentShouldAddMultipleTestAdapterPaths() { - _vsTestTask.VSTestTestAdapterPath = new string[] { "path1", "path2" }; + _vsTestTask.VSTestTestAdapterPath = new ITaskItem[] { new TaskItem("path1"), new TaskItem("path2") }; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--testAdapterPath:path1"))); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--testAdapterPath:path2"))); + StringAssert.Contains(commandline, "--testAdapterPath:path1"); + StringAssert.Contains(commandline, "--testAdapterPath:path2"); } [TestMethod] public void CreateArgumentShouldAddMultipleLoggers() { _vsTestTask.VSTestLogger = new string[] { "trx;LogFileName=foo bar.trx", "console" }; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:\"trx;LogFileName=foo bar.trx\""))); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:console"))); + StringAssert.Contains(commandline, "--logger:\"trx;LogFileName=foo bar.trx\""); + StringAssert.Contains(commandline, "--logger:console"); } [TestMethod] public void CreateArgumentShouldAddTraceCollectorDirectoryPathAsTestAdapterForCodeCoverageCollect() { const string traceDataCollectorDirectoryPath = @"c:\path\to\tracedata collector"; - _vsTestTask.VSTestTraceDataCollectorDirectoryPath = traceDataCollectorDirectoryPath; + _vsTestTask.VSTestTraceDataCollectorDirectoryPath = new TaskItem(traceDataCollectorDirectoryPath); _vsTestTask.VSTestCollect = new string[] { "code coverage" }; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - const string expectedArg = "--testAdapterPath:\"c:\\path\\to\\tracedata collector\""; - CollectionAssert.Contains(allArguments, expectedArg, $"Expected argument: '''{expectedArg}''' not present in [{string.Join(", ", allArguments)}]"); - } - - [TestMethod] - public void CreateArgumentShouldAddTraceCollectorDirectoryPathAsTestAdapterForCodeCoverageCollectWithExtraConfigurations() - { - const string traceDataCollectorDirectoryPath = @"c:\path\to\tracedata collector"; - _vsTestTask.VSTestTraceDataCollectorDirectoryPath = traceDataCollectorDirectoryPath; - _vsTestTask.VSTestCollect = new string[] { "code coverage;someParameter=someValue" }; - - var allArguments = _vsTestTask.CreateArgument().ToArray(); - - const string expectedArg = "--testAdapterPath:\"c:\\path\\to\\tracedata collector\""; - CollectionAssert.Contains(allArguments, expectedArg, $"Expected argument: '''{expectedArg}''' not present in [{string.Join(", ", allArguments)}]"); + string expectedArg = $"--testAdapterPath:\"{_vsTestTask.VSTestTraceDataCollectorDirectoryPath?.ItemSpec}\""; + StringAssert.Contains(commandline, expectedArg); } [TestMethod] public void CreateArgumentShouldNotAddTraceCollectorDirectoryPathAsTestAdapterForNonCodeCoverageCollect() { const string traceDataCollectorDirectoryPath = @"c:\path\to\tracedata collector"; - _vsTestTask.VSTestTraceDataCollectorDirectoryPath = traceDataCollectorDirectoryPath; + _vsTestTask.VSTestTraceDataCollectorDirectoryPath = new TaskItem(traceDataCollectorDirectoryPath); _vsTestTask.VSTestCollect = new string[] { "not code coverage" }; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - const string notExpectedArg = "--testAdapterPath:\"c:\\path\\to\\tracedata collector\""; - CollectionAssert.DoesNotContain(allArguments, notExpectedArg, $"Not expected argument: '''{notExpectedArg}''' present in [{string.Join(", ", allArguments)}]"); + string notExpectedArg = $"--testAdapterPath:\"{this._vsTestTask.VSTestTraceDataCollectorDirectoryPath?.ItemSpec}\""; + StringAssert.DoesNotMatch(commandline, new Regex(Regex.Escape(notExpectedArg))); } [TestMethod] public void CreateArgumentShouldAddTraceCollectorDirectoryPathAsTestAdapterIfSettingsGiven() { const string traceDataCollectorDirectoryPath = @"c:\path\to\tracedatacollector\"; - _vsTestTask.VSTestTraceDataCollectorDirectoryPath = traceDataCollectorDirectoryPath; + _vsTestTask.VSTestTraceDataCollectorDirectoryPath = new TaskItem(traceDataCollectorDirectoryPath); _vsTestTask.VSTestSetting = @"c:\path\to\sample.runsettings"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - const string expectedArg = "--testAdapterPath:c:\\path\\to\\tracedatacollector\\"; - CollectionAssert.Contains(allArguments, expectedArg, $"Expected argument: '''{expectedArg}''' not present in [{string.Join(", ", allArguments)}]"); + string expectedArg = $"--testAdapterPath:{_vsTestTask.VSTestTraceDataCollectorDirectoryPath?.ItemSpec}"; + StringAssert.Contains(commandline, expectedArg); } [TestMethod] - public void CreateArgumentShouldNotAddTestAdapterPathIfVsTestTraceDataCollectorDirectoryPathIsEmpty() + public void CreateArgumentShouldNotAddTestAdapterPathIfVSTestTraceDataCollectorDirectoryPathIsEmpty() { - _vsTestTask.VSTestTraceDataCollectorDirectoryPath = string.Empty; + _vsTestTask.VSTestTraceDataCollectorDirectoryPath = null; _vsTestTask.VSTestSetting = @"c:\path\to\sample.runsettings"; _vsTestTask.VSTestCollect = new string[] { "code coverage" }; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateArguments(); - Assert.IsNull(Array.Find(allArguments, arg => arg.Contains("--testAdapterPath:"))); + StringAssert.DoesNotMatch(commandline, new Regex(@"(--testAdapterPath:)")); } [TestMethod] public void CreateArgumentShouldAddNoLogoOptionIfSpecifiedByUser() { - _vsTestTask.VSTestNoLogo = "--nologo"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + _vsTestTask.VSTestNoLogo = true; + + var commandline = _vsTestTask.CreateArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--nologo"))); + StringAssert.Contains(commandline, "--nologo"); } }