diff --git a/src/Microsoft.TestPlatform.Build/ArgumentEscaper.cs b/src/Microsoft.TestPlatform.Build/ArgumentEscaper.cs deleted file mode 100644 index 704dc54703..0000000000 --- a/src/Microsoft.TestPlatform.Build/ArgumentEscaper.cs +++ /dev/null @@ -1,102 +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; - -#nullable disable - -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 7329157c46..bb54a8496a 100644 --- a/src/Microsoft.TestPlatform.Build/Microsoft.TestPlatform.targets +++ b/src/Microsoft.TestPlatform.Build/Microsoft.TestPlatform.targets @@ -12,10 +12,14 @@ Copyright (c) .NET Foundation. All rights reserved. - Microsoft.TestPlatform.Build.dll - $([System.IO.Path]::Combine($(MSBuildThisFileDirectory),"vstest.console.dll")) + Microsoft.TestPlatform.Build.dll + $([System.IO.Path]::Combine($(MSBuildThisFileDirectory),"vstest.console.dll")) + False + False + <_VSTestMSBuildDependsOn Condition="$(_VSTestMSBuildDependsOn) == '' And !$(VSTestNoBuild)">$(MSBuildProjectDefaultTargets) + - + + + + + + + + + + + + + + - + - @@ -70,37 +122,36 @@ Copyright (c) .NET Foundation. All rights reserved. - + - - - - - - + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - - + + + + + + + + + - diff --git a/src/Microsoft.TestPlatform.Build/PublicAPI/PublicAPI.Shipped.txt b/src/Microsoft.TestPlatform.Build/PublicAPI/PublicAPI.Shipped.txt index 9cd80864ae..dc99f77016 100644 --- a/src/Microsoft.TestPlatform.Build/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Microsoft.TestPlatform.Build/PublicAPI/PublicAPI.Shipped.txt @@ -1,7 +1,56 @@ -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.VSTestForwardingTask +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.Cancel() -> void +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.TestFileFullPath.get -> Microsoft.Build.Framework.ITaskItem +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.TestFileFullPath.set -> void +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlame.get -> bool +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlame.set -> void +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlameCrash.get -> bool +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlameCrash.set -> void +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 -> 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 +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlameHangTimeout.get -> string +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestBlameHangTimeout.set -> void +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestCLIRunSettings.get -> string[] +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 -> 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 -> 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 -> bool +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestNoLogo.set -> void +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestArtifactsProcessingMode.get -> string +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestArtifactsProcessingMode.set -> void +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestSessionCorrelationId.get -> string +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestSessionCorrelationId.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 -> Microsoft.Build.Framework.ITaskItem +Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.VSTestResultsDirectory.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 -> 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 -> 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.Tasks.VSTestLogsTask Microsoft.TestPlatform.Build.Tasks.VSTestLogsTask.LogType.get -> string Microsoft.TestPlatform.Build.Tasks.VSTestLogsTask.LogType.set -> void @@ -9,18 +58,17 @@ Microsoft.TestPlatform.Build.Tasks.VSTestLogsTask.ProjectFilePath.get -> string Microsoft.TestPlatform.Build.Tasks.VSTestLogsTask.ProjectFilePath.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestLogsTask.VSTestLogsTask() -> void Microsoft.TestPlatform.Build.Tasks.VSTestTask -Microsoft.TestPlatform.Build.Tasks.VSTestTask.Cancel() -> void -Microsoft.TestPlatform.Build.Tasks.VSTestTask.TestFileFullPath.get -> string +Microsoft.TestPlatform.Build.Tasks.VSTestTask.TestFileFullPath.get -> Microsoft.Build.Framework.ITaskItem Microsoft.TestPlatform.Build.Tasks.VSTestTask.TestFileFullPath.set -> void -Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestBlame.get -> string +Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestBlame.get -> bool Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestBlame.set -> void -Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestBlameCrash.get -> string +Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestBlameCrash.get -> bool Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestBlameCrash.set -> void -Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestBlameCrashCollectAlways.get -> string +Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestBlameCrashCollectAlways.get -> bool Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestBlameCrashCollectAlways.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestBlameCrashDumpType.get -> string Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestBlameCrashDumpType.set -> void -Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestBlameHang.get -> string +Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestBlameHang.get -> bool Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestBlameHang.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestBlameHangDumpType.get -> string Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestBlameHangDumpType.set -> void @@ -30,17 +78,17 @@ Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestCLIRunSettings.get -> string Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestCLIRunSettings.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestCollect.get -> string[] Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestCollect.set -> void -Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestConsolePath.get -> string +Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestConsolePath.get -> Microsoft.Build.Framework.ITaskItem Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestConsolePath.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestDiag.get -> string Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestDiag.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestFramework.get -> string Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestFramework.set -> void -Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestListTests.get -> string +Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestListTests.get -> bool Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestListTests.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestLogger.get -> string[] Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestLogger.set -> void -Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestNoLogo.get -> string +Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestNoLogo.get -> bool Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestNoLogo.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestArtifactsProcessingMode.get -> string Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestArtifactsProcessingMode.set -> void @@ -48,23 +96,24 @@ Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestSessionCorrelationId.get -> Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestSessionCorrelationId.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestPlatform.get -> string Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestPlatform.set -> void -Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestResultsDirectory.get -> string +Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestResultsDirectory.get -> Microsoft.Build.Framework.ITaskItem Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestResultsDirectory.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestSetting.get -> string Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestSetting.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestTask() -> void -Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestTestAdapterPath.get -> string[] +Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestTestAdapterPath.get -> Microsoft.Build.Framework.ITaskItem[] Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestTestAdapterPath.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestTestCaseFilter.get -> string Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestTestCaseFilter.set -> void -Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestTraceDataCollectorDirectoryPath.get -> string +Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestTraceDataCollectorDirectoryPath.get -> Microsoft.Build.Framework.ITaskItem Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestTraceDataCollectorDirectoryPath.set -> void Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestVerbosity.get -> string Microsoft.TestPlatform.Build.Tasks.VSTestTask.VSTestVerbosity.set -> void Microsoft.TestPlatform.Build.Trace.Tracing -Microsoft.TestPlatform.Build.Utils.ArgumentEscaper +override Microsoft.TestPlatform.Build.Tasks.VSTestForwardingTask.Execute() -> bool override Microsoft.TestPlatform.Build.Tasks.VSTestLogsTask.Execute() -> bool -override Microsoft.TestPlatform.Build.Tasks.VSTestTask.Execute() -> bool +override Microsoft.TestPlatform.Build.Tasks.VSTestTask.GenerateCommandLineCommands() -> string +override Microsoft.TestPlatform.Build.Tasks.VSTestTask.GenerateFullPathToTool() -> string +override Microsoft.TestPlatform.Build.Tasks.VSTestTask.ToolName.get -> string 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/ITestTask.cs b/src/Microsoft.TestPlatform.Build/Tasks/ITestTask.cs new file mode 100644 index 0000000000..866729357f --- /dev/null +++ b/src/Microsoft.TestPlatform.Build/Tasks/ITestTask.cs @@ -0,0 +1,43 @@ +// 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.Build.Framework; +using Microsoft.Build.Utilities; + +#nullable disable + +namespace Microsoft.TestPlatform.Build.Tasks; + +internal interface ITestTask: + ITask, + ICancelableTask +{ + + ITaskItem TestFileFullPath { get; set; } + string VSTestSetting { get; set; } + ITaskItem[] VSTestTestAdapterPath { get; set; } + string VSTestFramework { get; set; } + string VSTestPlatform { get; set; } + string VSTestTestCaseFilter { get; set; } + string[] VSTestLogger { get; set; } + bool VSTestListTests { get; set; } + string VSTestDiag { get; set; } + string[] VSTestCLIRunSettings { get; set; } + ITaskItem VSTestConsolePath { get; set; } + ITaskItem VSTestResultsDirectory { get; set; } + string VSTestVerbosity { get; set; } + string[] VSTestCollect { get; set; } + bool VSTestBlame { get; set; } + bool VSTestBlameCrash { get; set; } + string VSTestBlameCrashDumpType { get; set; } + bool VSTestBlameCrashCollectAlways { get; set; } + bool VSTestBlameHang { get; set; } + string VSTestBlameHangDumpType { get; set; } + string VSTestBlameHangTimeout { get; set; } + ITaskItem VSTestTraceDataCollectorDirectoryPath { get; set; } + bool VSTestNoLogo { get; set; } + string VSTestArtifactsProcessingMode { get; set; } + string VSTestSessionCorrelationId { get; set; } + + TaskLoggingHelper Log { get; } +} diff --git a/src/Microsoft.TestPlatform.Build/Tasks/TestTaskExtensions.cs b/src/Microsoft.TestPlatform.Build/Tasks/TestTaskExtensions.cs new file mode 100644 index 0000000000..f40c676a5a --- /dev/null +++ b/src/Microsoft.TestPlatform.Build/Tasks/TestTaskExtensions.cs @@ -0,0 +1,228 @@ +// 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.Linq; + +using Microsoft.Build.Utilities; + +#nullable disable + +namespace Microsoft.TestPlatform.Build.Tasks; + +internal static class TestTaskExtensions +{ + + public static string CreateCommandLineArguments(this ITestTask task) + { + var isConsoleLoggerSpecifiedByUser = false; + var isCollectCodeCoverageEnabled = false; + var isRunSettingsEnabled = false; + + var builder = new CommandLineBuilder(); + builder.AppendSwitch("exec"); + if (task.VSTestConsolePath != null && !string.IsNullOrEmpty(task.VSTestConsolePath.ItemSpec)) + { + builder.AppendSwitchIfNotNull("", task.VSTestConsolePath); + } + else + { + builder.AppendSwitch("vstest.console.dll"); + } + + // TODO log arguments in task + if (!string.IsNullOrEmpty(task.VSTestSetting)) + { + isRunSettingsEnabled = true; + builder.AppendSwitchIfNotNull("--settings:", task.VSTestSetting); + } + + if (task.VSTestTestAdapterPath != null && task.VSTestTestAdapterPath.Any()) + { + foreach (var arg in task.VSTestTestAdapterPath) + { + builder.AppendSwitchIfNotNull("--testAdapterPath:", arg); + } + } + + if (!string.IsNullOrEmpty(task.VSTestFramework)) + { + builder.AppendSwitchIfNotNull("--framework:", task.VSTestFramework); + } + + // vstest.console only support x86 and x64 for argument platform + if (!string.IsNullOrEmpty(task.VSTestPlatform) && !task.VSTestPlatform.Contains("AnyCPU")) + { + builder.AppendSwitchIfNotNull("--platform:", task.VSTestPlatform); + } + + if (!string.IsNullOrEmpty(task.VSTestTestCaseFilter)) + { + builder.AppendSwitchIfNotNull("--testCaseFilter:", task.VSTestTestCaseFilter); + } + + if (task.VSTestLogger != null && task.VSTestLogger.Any()) + { + foreach (var arg in task.VSTestLogger) + { + builder.AppendSwitchIfNotNull("--logger:", arg); + + if (arg.StartsWith("console", StringComparison.OrdinalIgnoreCase)) + { + isConsoleLoggerSpecifiedByUser = true; + } + } + } + + if (task.VSTestResultsDirectory != null && !string.IsNullOrEmpty(task.VSTestResultsDirectory.ItemSpec)) + { + builder.AppendSwitchIfNotNull("--resultsDirectory:", task.VSTestResultsDirectory); + } + + if (task.VSTestListTests) + { + builder.AppendSwitch("--listTests"); + } + + if (!string.IsNullOrEmpty(task.VSTestDiag)) + { + builder.AppendSwitchIfNotNull("--Diag:", task.VSTestDiag); + } + + if (task.TestFileFullPath != null) + { + builder.AppendFileNameIfNotNull(task.TestFileFullPath); + } + + // Console logger was not specified by user, but verbosity was, hence add default console logger with verbosity as specified + if (!string.IsNullOrWhiteSpace(task.VSTestVerbosity) && !isConsoleLoggerSpecifiedByUser) + { + var normalTestLogging = new List() { "n", "normal", "d", "detailed", "diag", "diagnostic" }; + var quietTestLogging = new List() { "q", "quiet" }; + + string vsTestVerbosity = "minimal"; + if (normalTestLogging.Contains(task.VSTestVerbosity, StringComparer.InvariantCultureIgnoreCase)) + { + vsTestVerbosity = "normal"; + } + else if (quietTestLogging.Contains(task.VSTestVerbosity, StringComparer.InvariantCultureIgnoreCase)) + { + vsTestVerbosity = "quiet"; + } + + builder.AppendSwitchUnquotedIfNotNull("--logger:", $"Console;Verbosity={vsTestVerbosity}"); + } + + if (task.VSTestBlame) + { + var dumpArgs = new List(); + if (task.VSTestBlameCrash || task.VSTestBlameHang) + { + if (task.VSTestBlameCrash) + { + dumpArgs.Add("CollectDump"); + + if (task.VSTestBlameCrashCollectAlways) + { + dumpArgs.Add($"CollectAlways={task.VSTestBlameCrashCollectAlways}"); + } + + if (!string.IsNullOrEmpty(task.VSTestBlameCrashDumpType)) + { + dumpArgs.Add($"DumpType={task.VSTestBlameCrashDumpType}"); + } + } + + if (task.VSTestBlameHang) + { + dumpArgs.Add("CollectHangDump"); + + if (!string.IsNullOrEmpty(task.VSTestBlameHangDumpType)) + { + dumpArgs.Add($"HangDumpType={task.VSTestBlameHangDumpType}"); + } + + if (!string.IsNullOrEmpty(task.VSTestBlameHangTimeout)) + { + dumpArgs.Add($"TestTimeout={task.VSTestBlameHangTimeout}"); + } + } + } + + if (dumpArgs.Any()) + { + builder.AppendSwitchIfNotNull("--Blame:", string.Join(";", dumpArgs)); + } + else + { + builder.AppendSwitch("--Blame"); + } + } + + if (task.VSTestCollect != null && task.VSTestCollect.Any()) + { + foreach (var arg in task.VSTestCollect) + { + if (arg.Equals("Code Coverage", StringComparison.OrdinalIgnoreCase)) + { + isCollectCodeCoverageEnabled = true; + } + + builder.AppendSwitchIfNotNull("--collect:", arg); + } + } + + if (isCollectCodeCoverageEnabled || isRunSettingsEnabled) + { + // Pass TraceDataCollector path to vstest.console as TestAdapterPath if --collect "Code Coverage" + // or --settings (User can enable code coverage from runsettings) option given. + // Not parsing the runsettings for two reason: + // 1. To keep no knowledge of runsettings structure in VSTestTask. + // 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 (task.VSTestTraceDataCollectorDirectoryPath != null && !string.IsNullOrEmpty(task.VSTestTraceDataCollectorDirectoryPath.ItemSpec)) + { + builder.AppendSwitchIfNotNull("--testAdapterPath:", task.VSTestTraceDataCollectorDirectoryPath); + } + else + { + if (isCollectCodeCoverageEnabled) + { + // Not showing message in runsettings scenario, because we are not sure that code coverage is enabled. + // User might be using older Microsoft.NET.Test.Sdk which don't have CodeCoverage infra. + task.Log.LogWarning(Resources.Resources.UpdateTestSdkForCollectingCodeCoverage); + } + } + } + + if (task.VSTestNoLogo) + { + builder.AppendSwitch("--nologo"); + } + + if (!string.IsNullOrEmpty(task.VSTestArtifactsProcessingMode) && task.VSTestArtifactsProcessingMode.Equals("collect", StringComparison.OrdinalIgnoreCase)) + { + builder.AppendSwitch("--artifactsProcessingMode-collect"); + } + + if (!string.IsNullOrEmpty(task.VSTestSessionCorrelationId)) + { + builder.AppendSwitchIfNotNull("--testSessionCorrelationId:", task.VSTestSessionCorrelationId); + } + + // VSTestCLIRunSettings should be last argument as vstest.console ignore options after "--"(CLIRunSettings option). + if (task.VSTestCLIRunSettings != null && task.VSTestCLIRunSettings.Any()) + { + builder.AppendSwitch("--"); + + foreach (var arg in task.VSTestCLIRunSettings) + { + builder.AppendSwitchIfNotNull(string.Empty, arg); + } + } + + return builder.ToString(); + } +} diff --git a/src/Microsoft.TestPlatform.Build/Tasks/VSTestForwardingApp.cs b/src/Microsoft.TestPlatform.Build/Tasks/VSTestForwardingApp.cs deleted file mode 100644 index 46da877e92..0000000000 --- a/src/Microsoft.TestPlatform.Build/Tasks/VSTestForwardingApp.cs +++ /dev/null @@ -1,64 +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; - -#nullable disable - -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(string.Format("VSTest: Killing process throws ArgumentException with the following message {0}. It may be that process is not running", ex)); - } - } -} diff --git a/src/Microsoft.TestPlatform.Build/Tasks/VSTestForwardingTask.cs b/src/Microsoft.TestPlatform.Build/Tasks/VSTestForwardingTask.cs new file mode 100644 index 0000000000..1625f04b85 --- /dev/null +++ b/src/Microsoft.TestPlatform.Build/Tasks/VSTestForwardingTask.cs @@ -0,0 +1,110 @@ +// 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.Diagnostics; +using System.Threading; + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Microsoft.TestPlatform.Build.Trace; + +#nullable disable + +namespace Microsoft.TestPlatform.Build.Tasks; + +public class VSTestForwardingTask : Task, ITestTask +{ + private const string ToolExe = "dotnet"; + private int _activeProcessId; + + public ITaskItem TestFileFullPath { get; set; } + public string VSTestSetting { 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 bool VSTestListTests { get; set; } + public string VSTestDiag { get; set; } + public string[] VSTestCLIRunSettings { get; set; } + [Required] + public ITaskItem VSTestConsolePath { get; set; } + public ITaskItem VSTestResultsDirectory { get; set; } + public string VSTestVerbosity { get; set; } + public string[] VSTestCollect { get; set; } + public bool VSTestBlame { get; set; } + public bool VSTestBlameCrash { get; set; } + public string VSTestBlameCrashDumpType { get; set; } + public bool VSTestBlameCrashCollectAlways { get; set; } + public bool VSTestBlameHang { get; set; } + public string VSTestBlameHangDumpType { get; set; } + public string VSTestBlameHangTimeout { 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() + { + var traceEnabledValue = Environment.GetEnvironmentVariable("VSTEST_BUILD_TRACE"); + Tracing.traceEnabled = !string.IsNullOrEmpty(traceEnabledValue) && traceEnabledValue.Equals("1", StringComparison.OrdinalIgnoreCase); + + var debugEnabled = Environment.GetEnvironmentVariable("VSTEST_BUILD_DEBUG"); + if (!string.IsNullOrEmpty(debugEnabled) && debugEnabled.Equals("1", StringComparison.Ordinal)) + { + Console.WriteLine("Waiting for debugger attach..."); + + var currentProcess = Process.GetCurrentProcess(); + Console.WriteLine(string.Format("Process Id: {0}, Name: {1}", currentProcess.Id, currentProcess.ProcessName)); + + while (!Debugger.IsAttached) + { + Thread.Sleep(1000); + } + + Debugger.Break(); + } + + // Avoid logging "Task returned false but did not log an error." on test failure, because we don't + // write MSBuild error. https://github.com/dotnet/msbuild/blob/51a1071f8871e0c93afbaf1b2ac2c9e59c7b6491/src/Framework/IBuildEngine7.cs#L12 + var allowfailureWithoutError = BuildEngine.GetType().GetProperty("AllowFailureWithoutError"); + allowfailureWithoutError?.SetValue(BuildEngine, true); + + var processInfo = new ProcessStartInfo + { + FileName = ToolExe, + Arguments = this.CreateCommandLineArguments(), + UseShellExecute = false, + }; + + if (!string.IsNullOrEmpty(VSTestFramework)) + { + Console.WriteLine(Resources.Resources.TestRunningSummary, TestFileFullPath, VSTestFramework); + } + + 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..."); + 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)); + } + } +} diff --git a/src/Microsoft.TestPlatform.Build/Tasks/VSTestTask.cs b/src/Microsoft.TestPlatform.Build/Tasks/VSTestTask.cs index 6364c8cc31..dcc8ac1435 100644 --- a/src/Microsoft.TestPlatform.Build/Tasks/VSTestTask.cs +++ b/src/Microsoft.TestPlatform.Build/Tasks/VSTestTask.cs @@ -2,442 +2,100 @@ // 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 System.Linq; -using System.Threading; +using System.IO; +using System.Runtime.InteropServices; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; -using Microsoft.TestPlatform.Build.Trace; -using Microsoft.TestPlatform.Build.Utils; #nullable disable namespace Microsoft.TestPlatform.Build.Tasks; -public class VSTestTask : Task, ICancelableTask +public class VSTestTask : ToolTask, ITestTask { - // The process which is invoking vstest.console - private VSTestForwardingApp _vsTestForwardingApp; - - private const string VsTestAppName = "vstest.console.dll"; - private const string CodeCovergaeString = "Code Coverage"; - - public string TestFileFullPath - { - get; - set; - } - - public string VSTestSetting - { - get; - set; - } - - public string[] 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 string VSTestDiag - { - get; - set; - } - - public string[] VSTestCLIRunSettings - { - get; - set; - } + public ITaskItem TestFileFullPath { get; set; } + public string VSTestSetting { 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 bool VSTestListTests { get; set; } + public string VSTestDiag { get; set; } + public string[] VSTestCLIRunSettings { get; set; } [Required] - public string VSTestConsolePath - { - get; - set; - } - - public string VSTestResultsDirectory - { - get; - set; - } - - public string VSTestVerbosity - { - get; - set; - } - - public string[] VSTestCollect - { - get; - set; - } - - public string VSTestBlame - { - get; - set; - } - - public string VSTestBlameCrash - { - get; - set; - } - - public string VSTestBlameCrashDumpType - { - get; - set; - } - - public string VSTestBlameCrashCollectAlways - { - get; - set; - } - - public string VSTestBlameHang - { - get; - set; - } - - public string VSTestBlameHangDumpType - { - get; - set; - } - public string VSTestBlameHangTimeout - { - get; - set; - } - - public string VSTestTraceDataCollectorDirectoryPath - { - get; - set; - } - - public string VSTestNoLogo - { - get; - set; - } - - public string VSTestArtifactsProcessingMode - { - get; - set; - } - - public string VSTestSessionCorrelationId - { - get; - set; - } - - public override bool Execute() - { - var traceEnabledValue = Environment.GetEnvironmentVariable("VSTEST_BUILD_TRACE"); - Tracing.traceEnabled = !string.IsNullOrEmpty(traceEnabledValue) && traceEnabledValue.Equals("1", StringComparison.OrdinalIgnoreCase); - - var debugEnabled = Environment.GetEnvironmentVariable("VSTEST_BUILD_DEBUG"); - if (!string.IsNullOrEmpty(debugEnabled) && debugEnabled.Equals("1", StringComparison.Ordinal)) - { - Console.WriteLine("Waiting for debugger attach..."); - - var currentProcess = Process.GetCurrentProcess(); - Console.WriteLine(string.Format("Process Id: {0}, Name: {1}", currentProcess.Id, currentProcess.ProcessName)); - - while (!Debugger.IsAttached) - { - Thread.Sleep(1000); - } - - Debugger.Break(); - } - - // Avoid logging "Task returned false but did not log an error." on test failure, because we don't - // write MSBuild error. https://github.com/dotnet/msbuild/blob/51a1071f8871e0c93afbaf1b2ac2c9e59c7b6491/src/Framework/IBuildEngine7.cs#L12 - var allowfailureWithoutError = BuildEngine.GetType().GetProperty("AllowFailureWithoutError"); - allowfailureWithoutError?.SetValue(BuildEngine, true); - - _vsTestForwardingApp = new VSTestForwardingApp(VSTestConsolePath, CreateArgument()); - if (!string.IsNullOrEmpty(VSTestFramework)) - { - Console.WriteLine(Resources.Resources.TestRunningSummary, TestFileFullPath, VSTestFramework); + public ITaskItem VSTestConsolePath { get; set; } + public ITaskItem VSTestResultsDirectory { get; set; } + public string VSTestVerbosity { get; set; } + public string[] VSTestCollect { get; set; } + public bool VSTestBlame { get; set; } + public bool VSTestBlameCrash { get; set; } + public string VSTestBlameCrashDumpType { get; set; } + public bool VSTestBlameCrashCollectAlways { get; set; } + public bool VSTestBlameHang { get; set; } + public string VSTestBlameHangDumpType { get; set; } + public string VSTestBlameHangTimeout { get; set; } + public ITaskItem VSTestTraceDataCollectorDirectoryPath { get; set; } + public bool VSTestNoLogo { get; set; } + public string VSTestArtifactsProcessingMode { get; set; } + public string VSTestSessionCorrelationId { get; set; } + + protected override string ToolName + { + get + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return "dotnet.exe"; + else + return "dotnet"; } - - return _vsTestForwardingApp.Execute() == 0; } - public void Cancel() + public VSTestTask() { - Tracing.Trace("VSTest: Killing the process..."); - _vsTestForwardingApp.Cancel(); + LogStandardErrorAsError = true; + StandardOutputImportance = "Normal"; } - internal IEnumerable CreateArgument() + protected override string GenerateCommandLineCommands() { - var allArgs = AddArgs(); - - // VSTestCLIRunSettings should be last argument in allArgs as vstest.console ignore options after "--"(CLIRunSettings option). - AddCliRunSettingsArgs(allArgs); - - return allArgs; + return this.CreateCommandLineArguments(); } - private void AddCliRunSettingsArgs(List allArgs) + protected override string GenerateFullPathToTool() { - if (VSTestCLIRunSettings != null && VSTestCLIRunSettings.Length > 0) - { - allArgs.Add("--"); - foreach (var arg in VSTestCLIRunSettings) - { - allArgs.Add(ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(arg)); - } - } - } - - private List AddArgs() - { - var isConsoleLoggerSpecifiedByUser = false; - var isCollectCodeCoverageEnabled = false; - var isRunSettingsEnabled = false; - var allArgs = new List(); - - // TODO log arguments in task - if (!string.IsNullOrEmpty(VSTestSetting)) - { - isRunSettingsEnabled = true; - allArgs.Add("--settings:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(VSTestSetting)); - } - - if (VSTestTestAdapterPath != null && VSTestTestAdapterPath.Length > 0) - { - foreach (var arg in VSTestTestAdapterPath) - { - allArgs.Add("--testAdapterPath:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(arg)); - } - } - - if (!string.IsNullOrEmpty(VSTestFramework)) - { - allArgs.Add("--framework:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(VSTestFramework)); - } - - // vstest.console only support x86 and x64 for argument platform - if (!string.IsNullOrEmpty(VSTestPlatform) && !VSTestPlatform.Contains("AnyCPU")) - { - allArgs.Add("--platform:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(VSTestPlatform)); - } - - if (!string.IsNullOrEmpty(VSTestTestCaseFilter)) - { - allArgs.Add("--testCaseFilter:" + - ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(VSTestTestCaseFilter)); - } - - if (VSTestLogger != null && VSTestLogger.Length > 0) - { - foreach (var arg in VSTestLogger) - { - allArgs.Add("--logger:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(arg)); - - if (arg.StartsWith("console", StringComparison.OrdinalIgnoreCase)) - { - isConsoleLoggerSpecifiedByUser = true; - } - } - } - - if (!string.IsNullOrEmpty(VSTestResultsDirectory)) - { - allArgs.Add("--resultsDirectory:" + - ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(VSTestResultsDirectory)); - } - - if (!string.IsNullOrEmpty(VSTestListTests)) + if (!string.IsNullOrEmpty(ToolPath)) { - allArgs.Add("--listTests"); + return Path.Combine(Path.GetDirectoryName(Path.GetFullPath(ToolPath)), ToolExe); } - if (!string.IsNullOrEmpty(VSTestDiag)) - { - allArgs.Add("--Diag:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(VSTestDiag)); - } + //TODO: https://github.com/dotnet/sdk/issues/20 Need to get the dotnet path from MSBuild? - if (string.IsNullOrEmpty(TestFileFullPath)) - { - Log.LogError("Test file path cannot be empty or null."); - } - else + var dhp = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH"); + if (!string.IsNullOrEmpty(dhp)) { - allArgs.Add(ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(TestFileFullPath)); - } - - // Console logger was not specified by user, but verbosity was, hence add default console logger with verbosity as specified - if (!string.IsNullOrWhiteSpace(VSTestVerbosity) && !isConsoleLoggerSpecifiedByUser) - { - var normalTestLogging = new List() { "n", "normal", "d", "detailed", "diag", "diagnostic" }; - var quietTestLogging = new List() { "q", "quiet" }; - - string vsTestVerbosity = "minimal"; - if (normalTestLogging.Contains(VSTestVerbosity.ToLowerInvariant())) + var path = Path.Combine(Path.GetDirectoryName(Path.GetFullPath(dhp)), ToolExe); + if (File.Exists(path)) { - vsTestVerbosity = "normal"; + return path; } - else if (quietTestLogging.Contains(VSTestVerbosity.ToLowerInvariant())) - { - vsTestVerbosity = "quiet"; - } - - allArgs.Add("--logger:Console;Verbosity=" + vsTestVerbosity); - } - - var blameCrash = !string.IsNullOrEmpty(VSTestBlameCrash); - var blameHang = !string.IsNullOrEmpty(VSTestBlameHang); - if (!string.IsNullOrEmpty(VSTestBlame) || blameCrash || blameHang) - { - var blameArgs = "--Blame"; - - var dumpArgs = new List(); - if (blameCrash || blameHang) - { - if (blameCrash) - { - dumpArgs.Add("CollectDump"); - if (!string.IsNullOrEmpty(VSTestBlameCrashCollectAlways)) - { - dumpArgs.Add($"CollectAlways={VSTestBlameCrashCollectAlways}"); - } - - if (!string.IsNullOrEmpty(VSTestBlameCrashDumpType)) - { - dumpArgs.Add($"DumpType={VSTestBlameCrashDumpType}"); - } - } - - if (blameHang) - { - dumpArgs.Add("CollectHangDump"); - - if (!string.IsNullOrEmpty(VSTestBlameHangDumpType)) - { - dumpArgs.Add($"HangDumpType={VSTestBlameHangDumpType}"); - } - - if (!string.IsNullOrEmpty(VSTestBlameHangTimeout)) - { - dumpArgs.Add($"TestTimeout={VSTestBlameHangTimeout}"); - } - } - - if (dumpArgs.Any()) - { - blameArgs += $":\"{string.Join(";", dumpArgs)}\""; - } - } - - allArgs.Add(blameArgs); - } - - if (VSTestCollect != null && VSTestCollect.Length > 0) - { - foreach (var arg in VSTestCollect) - { - // For collecting code coverage, argument value can be either "Code Coverage" or "Code Coverage;a=b;c=d". - // 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)) - { - isCollectCodeCoverageEnabled = true; - } - - allArgs.Add("--collect:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(arg)); - } - } - - if (isCollectCodeCoverageEnabled || isRunSettingsEnabled) - { - // Pass TraceDataCollector path to vstest.console as TestAdapterPath if --collect "Code Coverage" - // or --settings (User can enable code coverage from runsettings) option given. - // Not parsing the runsettings for two reason: - // 1. To keep no knowledge of runsettings structure in VSTestTask. - // 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 (!string.IsNullOrEmpty(VSTestTraceDataCollectorDirectoryPath)) - { - allArgs.Add("--testAdapterPath:" + - ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart( - VSTestTraceDataCollectorDirectoryPath)); - } - else - { - if (isCollectCodeCoverageEnabled) - { - // Not showing message in runsettings scenario, because we are not sure that code coverage is enabled. - // User might be using older Microsoft.NET.Test.Sdk which don't have CodeCoverage infra. - Console.WriteLine(Resources.Resources.UpdateTestSdkForCollectingCodeCoverage); - } - } - } - - if (!string.IsNullOrWhiteSpace(VSTestNoLogo)) - { - allArgs.Add("--nologo"); } - if (!string.IsNullOrEmpty(VSTestArtifactsProcessingMode) && VSTestArtifactsProcessingMode.Equals("collect", StringComparison.OrdinalIgnoreCase)) + if (File.Exists(ToolExe)) { - allArgs.Add("--artifactsProcessingMode-collect"); + return Path.GetFullPath(ToolExe); } - if (!string.IsNullOrEmpty(VSTestSessionCorrelationId)) + var values = Environment.GetEnvironmentVariable("PATH"); + foreach (var p in values.Split(Path.PathSeparator)) { - allArgs.Add("--testSessionCorrelationId:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(VSTestSessionCorrelationId)); + var fullPath = Path.Combine(p, ToolExe); + if (File.Exists(fullPath)) + return fullPath; } - return allArgs; + return null; } } 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/FakeBuildEngine.cs b/test/Microsoft.TestPlatform.Build.UnitTests/FakeBuildEngine.cs new file mode 100644 index 0000000000..c0d077d74e --- /dev/null +++ b/test/Microsoft.TestPlatform.Build.UnitTests/FakeBuildEngine.cs @@ -0,0 +1,40 @@ +// 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.Collections; + +using Microsoft.Build.Framework; + +namespace Microsoft.TestPlatform.Build.UnitTests; + +public class FakeBuildEngine : IBuildEngine +{ + public bool ContinueOnError => false; + + public int LineNumberOfTaskNode => 0; + + public int ColumnNumberOfTaskNode => 0; + + public string ProjectFileOfTaskNode => string.Empty; + + public bool BuildProjectFile(string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs) + { + return false; + } + + public void LogCustomEvent(CustomBuildEventArgs e) + { + } + + public void LogErrorEvent(BuildErrorEventArgs e) + { + } + + public void LogMessageEvent(BuildMessageEventArgs e) + { + } + + public void LogWarningEvent(BuildWarningEventArgs e) + { + } +} diff --git a/test/Microsoft.TestPlatform.Build.UnitTests/VsTestTaskTests.cs b/test/Microsoft.TestPlatform.Build.UnitTests/VsTestTaskTests.cs index b8884568ba..ca4c3074bd 100644 --- a/test/Microsoft.TestPlatform.Build.UnitTests/VsTestTaskTests.cs +++ b/test/Microsoft.TestPlatform.Build.UnitTests/VsTestTaskTests.cs @@ -1,25 +1,26 @@ // 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; [TestClass] -public class VsTestTaskTests +public class VSTestTaskTests { private readonly VSTestTask _vsTestTask; - public VsTestTaskTests() + public VSTestTaskTests() { _vsTestTask = new VSTestTask { - TestFileFullPath = @"C:\path\to\test-assembly.dll", + BuildEngine = new FakeBuildEngine(), + TestFileFullPath = new TaskItem(@"C:\path\to\test-assembly.dll"), VSTestFramework = ".NETCoreapp,Version2.0" }; } @@ -34,22 +35,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.CreateCommandLineArguments(); - // 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 +57,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.CreateCommandLineArguments(); - // 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.CreateCommandLineArguments(); - Assert.AreEqual($"--resultsDirectory:\"{resultsDirectoryValue}\"", result[1]); + StringAssert.Contains(commandline, $"--resultsDirectory:\"{_vsTestTask.VSTestResultsDirectory.ItemSpec}\""); } [TestMethod] @@ -84,10 +81,10 @@ public void CreateArgumentShouldNotSetConsoleLoggerVerbosityIfConsoleLoggerIsGiv _vsTestTask.VSTestVerbosity = "diag"; _vsTestTask.VSTestLogger = new string[] { "Console;Verbosity=quiet" }; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateCommandLineArguments(); - 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 +92,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToNormalIfConsoleLogger { _vsTestTask.VSTestVerbosity = "n"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateCommandLineArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=normal"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=normal"); } [TestMethod] @@ -105,9 +102,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToNormalIfConsoleLogger { _vsTestTask.VSTestVerbosity = "normal"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateCommandLineArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=normal"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=normal"); } [TestMethod] @@ -115,9 +112,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToNormalIfConsoleLogger { _vsTestTask.VSTestVerbosity = "d"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateCommandLineArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=normal"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=normal"); } [TestMethod] @@ -125,9 +122,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToNormalIfConsoleLogger { _vsTestTask.VSTestVerbosity = "detailed"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateCommandLineArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=normal"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=normal"); } [TestMethod] @@ -135,9 +132,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToNormalIfConsoleLogger { _vsTestTask.VSTestVerbosity = "diag"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateCommandLineArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=normal"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=normal"); } [TestMethod] @@ -145,9 +142,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToNormalIfConsoleLogger { _vsTestTask.VSTestVerbosity = "diagnostic"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateCommandLineArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=normal"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=normal"); } [TestMethod] @@ -155,9 +152,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToQuietIfConsoleLoggerI { _vsTestTask.VSTestVerbosity = "q"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateCommandLineArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=quiet"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=quiet"); } [TestMethod] @@ -165,9 +162,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToQuietIfConsoleLoggerI { _vsTestTask.VSTestVerbosity = "quiet"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateCommandLineArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=quiet"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=quiet"); } [TestMethod] @@ -175,9 +172,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToMinimalIfConsoleLogge { _vsTestTask.VSTestVerbosity = "m"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateCommandLineArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=minimal"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=minimal"); } [TestMethod] @@ -185,9 +182,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToMinimalIfConsoleLogge { _vsTestTask.VSTestVerbosity = "minimal"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateCommandLineArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=minimal"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=minimal"); } [TestMethod] @@ -195,9 +192,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToNormalIfConsoleLogger { _vsTestTask.VSTestVerbosity = "Normal"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateCommandLineArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=normal"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=normal"); } [TestMethod] @@ -205,9 +202,9 @@ public void CreateArgumentShouldSetConsoleLoggerVerbosityToQuietIfConsoleLoggerI { _vsTestTask.VSTestVerbosity = "Quiet"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateCommandLineArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--logger:Console;Verbosity=quiet"))); + StringAssert.Contains(commandline, "--logger:Console;Verbosity=quiet"); } [TestMethod] @@ -215,9 +212,9 @@ public void CreateArgumentShouldPreserveWhiteSpaceInLogger() { _vsTestTask.VSTestLogger = new string[] { "trx;LogFileName=foo bar.trx" }; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateCommandLineArguments(); - 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 +225,91 @@ public void CreateArgumentShouldAddOneCollectArgumentForEachCollect() _vsTestTask.VSTestCollect[0] = "name1"; _vsTestTask.VSTestCollect[1] = "name 2"; - var allArguments = _vsTestTask.CreateArgument().ToArray(); + var commandline = _vsTestTask.CreateCommandLineArguments(); - 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.CreateCommandLineArguments(); - 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.CreateCommandLineArguments(); - 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.CreateCommandLineArguments(); - 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.CreateCommandLineArguments(); - 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.CreateCommandLineArguments(); - 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.CreateCommandLineArguments(); - 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.CreateCommandLineArguments(); - Assert.IsNotNull(Array.Find(allArguments, arg => arg.Contains("--nologo"))); + StringAssert.Contains(commandline, "--nologo"); } }