diff --git a/eng/Versions.props b/eng/Versions.props index 8e04bff1c351..bac5b3e88e2a 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -28,6 +28,7 @@ true 6.0.1 true + 1.6.0-preview.25072.4 diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/PathUtility.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/PathUtility.cs index 60069f1aa672..f4f74e5aae2f 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/PathUtility.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/PathUtility.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Text.RegularExpressions; using Microsoft.DotNet.Cli.Utils; using NuGet.Configuration; @@ -388,5 +389,15 @@ public static void EnsureAllPathsExist( public static bool IsDirectory(this string path) => File.GetAttributes(path).HasFlag(FileAttributes.Directory); + + public static string FixFilePath(string path) + { + return string.IsNullOrEmpty(path) || Path.DirectorySeparatorChar == '\\' ? path : path.Replace('\\', '/'); + } + + public static string GetDirectorySeparatorChar() + { + return Regex.Escape(Path.DirectorySeparatorChar.ToString()); + } } } diff --git a/src/Cli/dotnet/commands/dotnet-test/CliConstants.cs b/src/Cli/dotnet/commands/dotnet-test/CliConstants.cs index 8f578b5f31dc..b0d32dd371e6 100644 --- a/src/Cli/dotnet/commands/dotnet-test/CliConstants.cs +++ b/src/Cli/dotnet/commands/dotnet-test/CliConstants.cs @@ -22,7 +22,10 @@ internal static class CliConstants public const string TestSectionKey = "test"; - public const string RestoreCommand = "restore"; + public const string RestoreCommand = "Restore"; + public const string BuildCommand = "Build"; + public const string Configuration = "Configuration"; + public const string RuntimeIdentifier = "RuntimeIdentifier"; public static readonly string[] ProjectExtensions = { ".proj", ".csproj", ".vbproj", ".fsproj" }; public static readonly string[] SolutionExtensions = { ".sln", ".slnx" }; diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs index b1adead6ad14..6fc6fbaef65d 100644 --- a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs +++ b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs @@ -6,7 +6,10 @@ using Microsoft.Build.Execution; using Microsoft.Build.Framework; using Microsoft.Build.Logging; +using Microsoft.DotNet.Tools.Common; using Microsoft.DotNet.Tools.Test; +using Microsoft.Testing.Platform.OutputDevice.Terminal; + namespace Microsoft.DotNet.Cli { @@ -15,17 +18,17 @@ internal sealed class MSBuildHandler : IDisposable private readonly List _args; private readonly TestApplicationActionQueue _actionQueue; private readonly int _degreeOfParallelism; + private TerminalTestReporter _output; private readonly ConcurrentBag _testApplications = new(); private bool _areTestingPlatformApplications = true; - private static readonly Lock buildLock = new(); - - public MSBuildHandler(List args, TestApplicationActionQueue actionQueue, int degreeOfParallelism) + public MSBuildHandler(List args, TestApplicationActionQueue actionQueue, int degreeOfParallelism, TerminalTestReporter output) { _args = args; _actionQueue = actionQueue; _degreeOfParallelism = degreeOfParallelism; + _output = output; } public async Task RunMSBuild(BuildPathsOptions buildPathOptions) @@ -36,23 +39,27 @@ public async Task RunMSBuild(BuildPathsOptions buildPathOptions) } int msbuildExitCode; + string path; if (!string.IsNullOrEmpty(buildPathOptions.ProjectPath)) { - msbuildExitCode = await RunBuild(buildPathOptions.ProjectPath, isSolution: false); + path = PathUtility.GetFullPath(buildPathOptions.ProjectPath); + msbuildExitCode = await RunBuild(path, isSolution: false, buildPathOptions); } else if (!string.IsNullOrEmpty(buildPathOptions.SolutionPath)) { - msbuildExitCode = await RunBuild(buildPathOptions.SolutionPath, isSolution: true); + path = PathUtility.GetFullPath(buildPathOptions.SolutionPath); + msbuildExitCode = await RunBuild(path, isSolution: true, buildPathOptions); } else { - msbuildExitCode = await RunBuild(buildPathOptions.DirectoryPath ?? Directory.GetCurrentDirectory()); + path = PathUtility.GetFullPath(buildPathOptions.DirectoryPath ?? Directory.GetCurrentDirectory()); + msbuildExitCode = await RunBuild(path, buildPathOptions); } if (msbuildExitCode != ExitCodes.Success) { - VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdMSBuildProjectsPropertiesErrorDescription, msbuildExitCode)); + _output.WriteMessage(string.Format(LocalizableStrings.CmdMSBuildProjectsPropertiesErrorDescription, msbuildExitCode)); return false; } @@ -65,7 +72,7 @@ private bool ValidateBuildPathOptions(BuildPathsOptions buildPathOptions) (!string.IsNullOrEmpty(buildPathOptions.ProjectPath) && !string.IsNullOrEmpty(buildPathOptions.DirectoryPath)) || (!string.IsNullOrEmpty(buildPathOptions.SolutionPath) && !string.IsNullOrEmpty(buildPathOptions.DirectoryPath))) { - VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMultipleBuildPathOptionsErrorDescription); + _output.WriteMessage(LocalizableStrings.CmdMultipleBuildPathOptionsErrorDescription); return false; } @@ -81,49 +88,50 @@ private bool ValidateBuildPathOptions(BuildPathsOptions buildPathOptions) if (!string.IsNullOrEmpty(buildPathOptions.DirectoryPath) && !Directory.Exists(buildPathOptions.DirectoryPath)) { - VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdNonExistentDirectoryErrorDescription, Path.GetFullPath(buildPathOptions.DirectoryPath))); + _output.WriteMessage(string.Format(LocalizableStrings.CmdNonExistentDirectoryErrorDescription, buildPathOptions.DirectoryPath)); return false; } return true; } - private static bool ValidateFilePath(string filePath, string[] validExtensions, string errorMessage) + private bool ValidateFilePath(string filePath, string[] validExtensions, string errorMessage) { if (!validExtensions.Contains(Path.GetExtension(filePath))) { - VSTestTrace.SafeWriteTrace(() => string.Format(errorMessage, filePath)); + _output.WriteMessage(string.Format(errorMessage, filePath)); return false; } if (!File.Exists(filePath)) { - VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdNonExistentFileErrorDescription, Path.GetFullPath(filePath))); + _output.WriteMessage(string.Format(LocalizableStrings.CmdNonExistentFileErrorDescription, Path.GetFullPath(filePath))); return false; } return true; } - private async Task RunBuild(string directoryPath) + private async Task RunBuild(string directoryPath, BuildPathsOptions buildPathOptions) { - bool solutionOrProjectFileFound = SolutionAndProjectUtility.TryGetProjectOrSolutionFilePath(directoryPath, out string projectOrSolutionFilePath, out bool isSolution); + (bool solutionOrProjectFileFound, string message) = SolutionAndProjectUtility.TryGetProjectOrSolutionFilePath(directoryPath, out string projectOrSolutionFilePath, out bool isSolution); if (!solutionOrProjectFileFound) { + _output.WriteMessage(message); return ExitCodes.GenericFailure; } - (IEnumerable modules, bool restored) = await GetProjectsProperties(projectOrSolutionFilePath, isSolution); + (IEnumerable modules, bool restored) = await GetProjectsProperties(projectOrSolutionFilePath, isSolution, buildPathOptions); InitializeTestApplications(modules); return restored ? ExitCodes.Success : ExitCodes.GenericFailure; } - private async Task RunBuild(string filePath, bool isSolution) + private async Task RunBuild(string filePath, bool isSolution, BuildPathsOptions buildPathOptions) { - (IEnumerable modules, bool restored) = await GetProjectsProperties(filePath, isSolution); + (IEnumerable modules, bool restored) = await GetProjectsProperties(filePath, isSolution, buildPathOptions); InitializeTestApplications(modules); @@ -166,10 +174,12 @@ public bool EnqueueTestApplications() return true; } - private async Task<(IEnumerable, bool Restored)> GetProjectsProperties(string solutionOrProjectFilePath, bool isSolution) + private async Task<(IEnumerable, bool Restored)> GetProjectsProperties(string solutionOrProjectFilePath, bool isSolution, BuildPathsOptions buildPathOptions) { + bool isBuiltOrRestored = true; var allProjects = new ConcurrentBag(); - bool restored = true; + var projectCollection = new ProjectCollection(); + bool allowBinLog = IsBinaryLoggerEnabled(_args, out string binLogFileName); if (isSolution) { @@ -179,77 +189,141 @@ public bool EnqueueTestApplications() : fileDirectory; var projects = await SolutionAndProjectUtility.ParseSolution(solutionOrProjectFilePath, rootDirectory); - ProcessProjectsInParallel(projects, allProjects, ref restored); + + MSBuildBuildAndRestoreSettings msBuildBuildAndRestoreSettings = new(GetCommands(buildPathOptions.HasNoRestore, buildPathOptions.HasNoBuild), buildPathOptions.Configuration, buildPathOptions.RuntimeIdentifier, allowBinLog, binLogFileName); + isBuiltOrRestored = BuildOrRestoreProjectOrSolution(solutionOrProjectFilePath, projectCollection, msBuildBuildAndRestoreSettings); + + ProcessProjectsInParallel(projectCollection, projects, allProjects); } else { - bool allowBinLog = IsBinaryLoggerEnabled(_args, out string binLogFileName); + if (!buildPathOptions.HasNoRestore) + { + MSBuildBuildAndRestoreSettings msBuildRestoreSettings = new([CliConstants.RestoreCommand], buildPathOptions.Configuration, buildPathOptions.RuntimeIdentifier, allowBinLog, binLogFileName); + isBuiltOrRestored = BuildOrRestoreProjectOrSolution(solutionOrProjectFilePath, projectCollection, msBuildRestoreSettings); + } - var (relatedProjects, isProjectBuilt) = GetProjectPropertiesInternal(solutionOrProjectFilePath, allowBinLog, binLogFileName); - foreach (var relatedProject in relatedProjects) + if (!buildPathOptions.HasNoBuild) { - allProjects.Add(relatedProject); + MSBuildBuildAndRestoreSettings msBuildBuildSettings = new([CliConstants.BuildCommand], buildPathOptions.Configuration, buildPathOptions.RuntimeIdentifier, allowBinLog, binLogFileName); + isBuiltOrRestored = isBuiltOrRestored && BuildOrRestoreProjectOrSolution(solutionOrProjectFilePath, projectCollection, msBuildBuildSettings); } - if (!isProjectBuilt) + IEnumerable relatedProjects = GetProjectPropertiesInternal(solutionOrProjectFilePath, projectCollection); + foreach (var relatedProject in relatedProjects) { - restored = false; + allProjects.Add(relatedProject); } } - return (allProjects, restored); + + LogProjectProperties(allProjects); + + return (allProjects, isBuiltOrRestored); } - private void ProcessProjectsInParallel(IEnumerable projects, ConcurrentBag allProjects, ref bool restored) + public static string[] GetCommands(bool hasNoRestore, bool hasNoBuild) { - bool allProjectsRestored = true; - bool allowBinLog = IsBinaryLoggerEnabled(_args, out string binLogFileName); + var commands = new List(); + + if (!hasNoRestore) + { + commands.Add(CliConstants.RestoreCommand); + } + if (!hasNoBuild) + { + commands.Add(CliConstants.BuildCommand); + } + + return commands.ToArray(); + } + + private void ProcessProjectsInParallel(ProjectCollection projectCollection, IEnumerable projects, ConcurrentBag allProjects) + { Parallel.ForEach( projects, new ParallelOptions { MaxDegreeOfParallelism = _degreeOfParallelism }, - () => true, - (project, state, localRestored) => + (project, state) => { - var (relatedProjects, isRestored) = GetProjectPropertiesInternal(project, allowBinLog, binLogFileName); + IEnumerable relatedProjects = GetProjectPropertiesInternal(project, projectCollection); foreach (var relatedProject in relatedProjects) { allProjects.Add(relatedProject); } + }); + } - return localRestored && isRestored; - }, - localRestored => - { - if (!localRestored) + private static IEnumerable GetProjectPropertiesInternal(string projectFilePath, ProjectCollection projectCollection) + { + var project = projectCollection.LoadProject(projectFilePath); + return ExtractModulesFromProject(project); + } + + private static bool BuildOrRestoreProjectOrSolution(string projectFilePath, ProjectCollection projectCollection, MSBuildBuildAndRestoreSettings msBuildBuildAndRestoreSettings) + { + var parameters = GetBuildParameters(projectCollection, msBuildBuildAndRestoreSettings); + var globalProperties = GetGlobalProperties(msBuildBuildAndRestoreSettings); + + var buildRequestData = new BuildRequestData(projectFilePath, globalProperties, null, msBuildBuildAndRestoreSettings.Commands, null); + + BuildResult buildResult = BuildManager.DefaultBuildManager.Build(parameters, buildRequestData); + + return buildResult.OverallResult == BuildResultCode.Success; + } + + private static BuildParameters GetBuildParameters(ProjectCollection projectCollection, MSBuildBuildAndRestoreSettings msBuildBuildAndRestoreSettings) + { + BuildParameters parameters = new(projectCollection) + { + Loggers = [new ConsoleLogger(LoggerVerbosity.Quiet)] + }; + + if (!msBuildBuildAndRestoreSettings.AllowBinLog) + return parameters; + + parameters.Loggers = + [ + .. parameters.Loggers, + .. new[] + { + new BinaryLogger { - allProjectsRestored = false; + Parameters = msBuildBuildAndRestoreSettings.BinLogFileName } - }); + }, + ]; - restored = allProjectsRestored; + return parameters; } - private static (IEnumerable Modules, bool Restored) GetProjectPropertiesInternal(string projectFilePath, bool allowBinLog, string binLogFileName) + private static Dictionary GetGlobalProperties(MSBuildBuildAndRestoreSettings msBuildBuildAndRestoreSettings) { - var projectCollection = new ProjectCollection(); - var project = projectCollection.LoadProject(projectFilePath); - var buildResult = RestoreProject(projectFilePath, projectCollection, allowBinLog, binLogFileName); + var globalProperties = new Dictionary(); - bool restored = buildResult.OverallResult == BuildResultCode.Success; + if (!string.IsNullOrEmpty(msBuildBuildAndRestoreSettings.Configuration)) + { + globalProperties[CliConstants.Configuration] = msBuildBuildAndRestoreSettings.Configuration; + } - if (!restored) + if (!string.IsNullOrEmpty(msBuildBuildAndRestoreSettings.RuntimeIdentifier)) { - return (Array.Empty(), restored); + globalProperties[CliConstants.RuntimeIdentifier] = msBuildBuildAndRestoreSettings.RuntimeIdentifier; } - return (ExtractModulesFromProject(project), restored); + return globalProperties; } private static IEnumerable ExtractModulesFromProject(Project project) { - _ = bool.TryParse(project.GetPropertyValue(ProjectProperties.IsTestingPlatformApplication), out bool isTestingPlatformApplication); _ = bool.TryParse(project.GetPropertyValue(ProjectProperties.IsTestProject), out bool isTestProject); + if (!isTestProject) + { + return []; + } + + _ = bool.TryParse(project.GetPropertyValue(ProjectProperties.IsTestingPlatformApplication), out bool isTestingPlatformApplication); + string targetFramework = project.GetPropertyValue(ProjectProperties.TargetFramework); string targetFrameworks = project.GetPropertyValue(ProjectProperties.TargetFrameworks); string targetPath = project.GetPropertyValue(ProjectProperties.TargetPath); @@ -260,7 +334,7 @@ private static IEnumerable ExtractModulesFromProject(Project project) if (string.IsNullOrEmpty(targetFrameworks)) { - projects.Add(new Module(targetPath, projectFullPath, targetFramework, runSettingsFilePath, isTestingPlatformApplication, isTestProject)); + projects.Add(new Module(targetPath, PathUtility.FixFilePath(projectFullPath), targetFramework, runSettingsFilePath, isTestingPlatformApplication, isTestProject)); } else { @@ -271,7 +345,7 @@ private static IEnumerable ExtractModulesFromProject(Project project) project.ReevaluateIfNecessary(); projects.Add(new Module(project.GetPropertyValue(ProjectProperties.TargetPath), - projectFullPath, + PathUtility.FixFilePath(projectFullPath), framework, runSettingsFilePath, isTestingPlatformApplication, @@ -282,34 +356,29 @@ private static IEnumerable ExtractModulesFromProject(Project project) return projects; } - private static BuildResult RestoreProject(string projectFilePath, ProjectCollection projectCollection, bool allowBinLog, string binLogFileName) + private void LogProjectProperties(IEnumerable modules) { - BuildParameters parameters = new(projectCollection) + if (!VSTestTrace.TraceEnabled) { - Loggers = [new ConsoleLogger(LoggerVerbosity.Quiet)] - }; - - if (allowBinLog) - { - parameters.Loggers = parameters.Loggers.Concat([ - new BinaryLogger - { - Parameters = binLogFileName - } - ]); + return; } - var buildRequestData = new BuildRequestData(projectFilePath, new Dictionary(), null, [CliConstants.RestoreCommand], null); - BuildResult buildResult; - lock (buildLock) + foreach (var module in modules) { - buildResult = BuildManager.DefaultBuildManager.Build(parameters, buildRequestData); - } + Console.WriteLine(); - return buildResult; + VSTestTrace.SafeWriteTrace(() => $"{ProjectProperties.ProjectFullPath}: {module.ProjectPath}"); + VSTestTrace.SafeWriteTrace(() => $"{ProjectProperties.IsTestProject}: {module.IsTestProject}"); + VSTestTrace.SafeWriteTrace(() => $"{ProjectProperties.IsTestingPlatformApplication}: {module.IsTestingPlatformApplication}"); + VSTestTrace.SafeWriteTrace(() => $"{ProjectProperties.TargetFramework}: {module.TargetFramework}"); + VSTestTrace.SafeWriteTrace(() => $"{ProjectProperties.TargetPath}: {module.DllOrExePath}"); + VSTestTrace.SafeWriteTrace(() => $"{ProjectProperties.RunSettingsFilePath}: {module.RunSettingsFilePath}"); + + Console.WriteLine(); + } } - private static bool IsBinaryLoggerEnabled(List args, out string binLogFileName) + internal static bool IsBinaryLoggerEnabled(List args, out string binLogFileName) { binLogFileName = string.Empty; var binLogArgs = new List(); diff --git a/src/Cli/dotnet/commands/dotnet-test/Models.cs b/src/Cli/dotnet/commands/dotnet-test/Models.cs index 17ca6a4261ec..cd59dec9327c 100644 --- a/src/Cli/dotnet/commands/dotnet-test/Models.cs +++ b/src/Cli/dotnet/commands/dotnet-test/Models.cs @@ -20,4 +20,6 @@ internal sealed record FlatException(string? ErrorMessage, string? ErrorType, st internal sealed record FileArtifact(string? FullPath, string? DisplayName, string? Description, string? TestUid, string? TestDisplayName, string? SessionUid); internal sealed record TestSession(byte? SessionType, string? SessionUid, string? ExecutionId); + + internal record MSBuildBuildAndRestoreSettings(string[] Commands, string Configuration, string RuntimeIdentifier, bool AllowBinLog, string BinLogFileName); } diff --git a/src/Cli/dotnet/commands/dotnet-test/Options.cs b/src/Cli/dotnet/commands/dotnet-test/Options.cs index 598e3cb7d722..d87f185995f0 100644 --- a/src/Cli/dotnet/commands/dotnet-test/Options.cs +++ b/src/Cli/dotnet/commands/dotnet-test/Options.cs @@ -3,7 +3,7 @@ namespace Microsoft.DotNet.Cli { - internal record BuildConfigurationOptions(bool HasNoRestore, bool HasNoBuild, bool HasListTests, string Configuration, string Architecture); + internal record BuildConfigurationOptions(bool HasListTests, string Configuration, string Architecture); - internal record BuildPathsOptions(string ProjectPath, string SolutionPath, string DirectoryPath); + internal record BuildPathsOptions(string ProjectPath, string SolutionPath, string DirectoryPath, bool HasNoRestore, bool HasNoBuild, string Configuration, string RuntimeIdentifier); } diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index a3ca1af6b4c6..07799a885983 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -11,14 +11,14 @@ namespace Microsoft.DotNet.Cli { internal static class SolutionAndProjectUtility { - public static bool TryGetProjectOrSolutionFilePath(string directory, out string projectOrSolutionFilePath, out bool isSolution) + public static (bool SolutionOrProjectFileFound, string Message) TryGetProjectOrSolutionFilePath(string directory, out string projectOrSolutionFilePath, out bool isSolution) { projectOrSolutionFilePath = string.Empty; isSolution = false; if (!Directory.Exists(directory)) { - return false; + return (false, string.Format(LocalizableStrings.CmdNonExistentDirectoryErrorDescription, directory)); } var possibleSolutionPaths = GetSolutionFilePaths(directory); @@ -26,8 +26,7 @@ public static bool TryGetProjectOrSolutionFilePath(string directory, out string // If more than a single sln file is found, an error is thrown since we can't determine which one to choose. if (possibleSolutionPaths.Length > 1) { - VSTestTrace.SafeWriteTrace(() => string.Format(CommonLocalizableStrings.MoreThanOneSolutionInDirectory, directory)); - return false; + return (false, string.Format(CommonLocalizableStrings.MoreThanOneSolutionInDirectory, directory)); } if (possibleSolutionPaths.Length == 1) @@ -38,11 +37,10 @@ public static bool TryGetProjectOrSolutionFilePath(string directory, out string { projectOrSolutionFilePath = possibleSolutionPaths[0]; isSolution = true; - return true; + return (true, string.Empty); } - VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMultipleProjectOrSolutionFilesErrorDescription); - return false; + return (false, LocalizableStrings.CmdMultipleProjectOrSolutionFilesErrorDescription); } else // If no solutions are found, look for a project file { @@ -50,19 +48,16 @@ public static bool TryGetProjectOrSolutionFilePath(string directory, out string if (possibleProjectPath.Length == 0) { - VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdNoProjectOrSolutionFileErrorDescription); - return false; + return (false, LocalizableStrings.CmdNoProjectOrSolutionFileErrorDescription); } if (possibleProjectPath.Length == 1) { projectOrSolutionFilePath = possibleProjectPath[0]; - return true; + return (true, string.Empty); } - VSTestTrace.SafeWriteTrace(() => string.Format(CommonLocalizableStrings.MoreThanOneProjectInDirectory, directory)); - - return false; + return (false, string.Format(CommonLocalizableStrings.MoreThanOneSolutionInDirectory, directory)); } } diff --git a/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs b/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs index 6918d2bcc2a7..ca0a6cdd7dda 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs @@ -31,12 +31,8 @@ internal sealed class TestApplication : IDisposable public event EventHandler SessionEventReceived; public event EventHandler ErrorReceived; public event EventHandler TestProcessExited; - public event EventHandler Run; public event EventHandler ExecutionIdReceived; - private const string TestingPlatformVsTestBridgeRunSettingsFileEnvVar = "TESTINGPLATFORM_VSTESTBRIDGE_RUNSETTINGS_FILE"; - private const string DLLExtension = "dll"; - public Module Module => _module; public TestApplication(Module module, List args) @@ -52,8 +48,6 @@ public void AddExecutionId(string executionId) public async Task RunAsync(bool hasFilterMode, bool enableHelp, BuildConfigurationOptions buildConfigurationOptions) { - Run?.Invoke(this, EventArgs.Empty); - if (hasFilterMode && !ModulePathExists()) { return 1; @@ -89,11 +83,10 @@ private ProcessStartInfo CreateProcessStartInfo(bool hasFilterMode, bool isDll, return processStartInfo; } - private void WaitOnTestApplicationPipeConnectionLoop() { _cancellationToken.Cancel(); - _testAppPipeConnectionLoop.Wait((int)TimeSpan.FromSeconds(30).TotalMilliseconds); + _testAppPipeConnectionLoop?.Wait((int)TimeSpan.FromSeconds(30).TotalMilliseconds); } private async Task WaitConnectionAsync(CancellationToken token) @@ -109,9 +102,14 @@ private async Task WaitConnectionAsync(CancellationToken token) _testAppPipeConnections.Add(pipeConnection); } } - catch (OperationCanceledException ex) when (ex.CancellationToken == token) + catch (OperationCanceledException ex) { // We are exiting + if (VSTestTrace.TraceEnabled) + { + string tokenType = ex.CancellationToken == token ? "internal token" : "external token"; + VSTestTrace.SafeWriteTrace(() => $"WaitConnectionAsync() throws OperationCanceledException with {tokenType}"); + } } catch (Exception ex) { @@ -212,7 +210,7 @@ private async Task StartProcess(ProcessStartInfo processStartInfo) { if (VSTestTrace.TraceEnabled) { - VSTestTrace.SafeWriteTrace(() => $"Updated args: {processStartInfo.Arguments}"); + VSTestTrace.SafeWriteTrace(() => $"Test application arguments: {processStartInfo.Arguments}"); } var process = Process.Start(processStartInfo); @@ -262,15 +260,9 @@ private string BuildArgsWithDotnetRun(bool hasHelp, BuildConfigurationOptions bu builder.Append($"{CliConstants.DotnetRunCommand} {TestingPlatformOptions.ProjectOption.Name} \"{_module.ProjectPath}\""); - if (buildConfigurationOptions.HasNoRestore) - { - builder.Append($" {TestingPlatformOptions.NoRestoreOption.Name}"); - } - - if (buildConfigurationOptions.HasNoBuild) - { - builder.Append($" {TestingPlatformOptions.NoBuildOption.Name}"); - } + // Because we restored and built before in MSHandler, we will skip those with dotnet run + builder.Append($" {TestingPlatformOptions.NoRestoreOption.Name}"); + builder.Append($" {TestingPlatformOptions.NoBuildOption.Name}"); if (buildConfigurationOptions.HasListTests) { @@ -382,12 +374,14 @@ public override string ToString() if (!string.IsNullOrEmpty(_module.ProjectPath)) { builder.Append($"Project: {_module.ProjectPath}"); - }; + } + ; if (!string.IsNullOrEmpty(_module.TargetFramework)) { builder.Append($"Target Framework: {_module.TargetFramework}"); - }; + } + ; return builder.ToString(); } diff --git a/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs b/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs index df414cbfa4e9..34b6a0a2e865 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs @@ -168,9 +168,6 @@ private static bool IsTestingPlatformEnabled() private static CliCommand ConstructCommand() { -#if RELEASE - return GetVSTestCliCommand(); -#else bool isTestingPlatformEnabled = IsTestingPlatformEnabled(); string testingSdkName = isTestingPlatformEnabled ? "testingplatform" : "vstest"; @@ -184,7 +181,6 @@ private static CliCommand ConstructCommand() } throw new InvalidOperationException($"Testing sdk not supported: {testingSdkName}"); -#endif } private static CliCommand GetTestingPlatformCliCommand() diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs index e73dddebf244..42528d8b6f88 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Concurrent; using System.CommandLine; using Microsoft.DotNet.Tools.Test; @@ -14,8 +13,6 @@ namespace Microsoft.DotNet.Cli { internal partial class TestingPlatformCommand : CliCommand, ICustomHelp { - private readonly ConcurrentBag _testApplications = []; - private MSBuildHandler _msBuildHandler; private TestModulesFilterHandler _testModulesFilterHandler; private TerminalTestReporter _output; @@ -36,91 +33,34 @@ public int Run(ParseResult parseResult) bool hasFailed = false; try { - Console.CancelKeyPress += (s, e) => - { - _output?.StartCancelling(); - CompleteRun(); - }; + SetupCancelKeyPressHandler(); int degreeOfParallelism = GetDegreeOfParallelism(parseResult); BuildConfigurationOptions buildConfigurationOptions = GetBuildConfigurationOptions(parseResult); - InitializeActionQueue(parseResult, degreeOfParallelism, buildConfigurationOptions); - - bool filterModeEnabled = parseResult.HasOption(TestingPlatformOptions.TestModulesFilterOption); - - if (filterModeEnabled && parseResult.HasOption(TestingPlatformOptions.ArchitectureOption)) - { - VSTestTrace.SafeWriteTrace(() => $"The --arch option is not supported yet."); - } - if (parseResult.HasOption(TestingPlatformOptions.ListTestsOption)) - { - _isDiscovery = true; - } + _isDiscovery = parseResult.HasOption(TestingPlatformOptions.ListTestsOption); + _args = [.. parseResult.UnmatchedTokens]; + _isHelp = ContainsHelpOption(parseResult.GetArguments()); - BuildConfigurationOptions builtInOptions = new( - parseResult.HasOption(TestingPlatformOptions.NoRestoreOption), - parseResult.HasOption(TestingPlatformOptions.NoBuildOption), - parseResult.HasOption(TestingPlatformOptions.ListTestsOption), - parseResult.GetValue(TestingPlatformOptions.ConfigurationOption), - parseResult.GetValue(TestingPlatformOptions.ArchitectureOption)); + InitializeOutput(degreeOfParallelism); - var console = new SystemConsole(); - var output = new TerminalTestReporter(console, new TerminalTestReporterOptions() - { - ShowPassedTests = Environment.GetEnvironmentVariable("SHOW_PASSED") == "1" ? () => true : () => false, - ShowProgress = () => Environment.GetEnvironmentVariable("NO_PROGRESS") != "1", - UseAnsi = Environment.GetEnvironmentVariable("NO_ANSI") != "1", - ShowAssembly = true, - ShowAssemblyStartAndComplete = true, - }); - _output = output; - - _isHelp = false; - if (ContainsHelpOption(parseResult.GetArguments())) + bool filterModeEnabled = parseResult.HasOption(TestingPlatformOptions.TestModulesFilterOption); + if (_isHelp) { - _isHelp = true; - _actionQueue = new(degreeOfParallelism, async (TestApplication testApp) => - { - testApp.HelpRequested += OnHelpRequested; - testApp.ErrorReceived += OnErrorReceived; - testApp.TestProcessExited += OnTestProcessExited; - testApp.Run += OnTestApplicationRun; - testApp.ExecutionIdReceived += OnExecutionIdReceived; - - var result = await testApp.RunAsync(filterModeEnabled, enableHelp: true, builtInOptions); - return result; - }); + InitializeHelpActionQueue(degreeOfParallelism, buildConfigurationOptions, filterModeEnabled); } else { - _output.TestExecutionStarted(DateTimeOffset.Now, degreeOfParallelism, _isDiscovery); - - _actionQueue = new(degreeOfParallelism, async (TestApplication testApp) => - { - testApp.HandshakeReceived += OnHandshakeReceived; - testApp.DiscoveredTestsReceived += OnDiscoveredTestsReceived; - testApp.TestResultsReceived += OnTestResultsReceived; - testApp.FileArtifactsReceived += OnFileArtifactsReceived; - testApp.SessionEventReceived += OnSessionEventReceived; - testApp.ErrorReceived += OnErrorReceived; - testApp.TestProcessExited += OnTestProcessExited; - testApp.Run += OnTestApplicationRun; - testApp.ExecutionIdReceived += OnExecutionIdReceived; - - return await testApp.RunAsync(filterModeEnabled, enableHelp: false, builtInOptions); - }); + InitializeTestExecutionActionQueue(degreeOfParallelism, buildConfigurationOptions, filterModeEnabled); } - _args = [.. parseResult.UnmatchedTokens]; - _msBuildHandler = new(_args, _actionQueue, degreeOfParallelism); + _msBuildHandler = new(_args, _actionQueue, degreeOfParallelism, _output); _testModulesFilterHandler = new(_args, _actionQueue); - if (parseResult.HasOption(TestingPlatformOptions.TestModulesFilterOption)) + if (filterModeEnabled) { if (!_testModulesFilterHandler.RunWithTestModulesFilter(parseResult)) { - CompleteRun(); return ExitCodes.GenericFailure; } } @@ -129,14 +69,12 @@ public int Run(ParseResult parseResult) var buildPathOptions = GetBuildPathOptions(parseResult); if (!_msBuildHandler.RunMSBuild(buildPathOptions).GetAwaiter().GetResult()) { - CompleteRun(); return ExitCodes.GenericFailure; } if (!_msBuildHandler.EnqueueTestApplications()) { - VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdUnsupportedVSTestTestApplicationsDescription); - CompleteRun(); + _output.WriteMessage(LocalizableStrings.CmdUnsupportedVSTestTestApplicationsDescription); return ExitCodes.GenericFailure; } } @@ -146,13 +84,70 @@ public int Run(ParseResult parseResult) } finally { + CompleteRun(); CleanUp(); } - CompleteRun(); return hasFailed ? ExitCodes.GenericFailure : ExitCodes.Success; } + private void SetupCancelKeyPressHandler() + { + Console.CancelKeyPress += (s, e) => + { + _output?.StartCancelling(); + CompleteRun(); + }; + } + + private void InitializeOutput(int degreeOfParallelism) + { + var console = new SystemConsole(); + _output = new TerminalTestReporter(console, new TerminalTestReporterOptions() + { + ShowPassedTests = Environment.GetEnvironmentVariable("SHOW_PASSED") == "1" ? () => true : () => false, + ShowProgress = () => Environment.GetEnvironmentVariable("NO_PROGRESS") != "1", + UseAnsi = Environment.GetEnvironmentVariable("NO_ANSI") != "1", + ShowAssembly = true, + ShowAssemblyStartAndComplete = true, + }); + + if (!_isHelp) + { + _output.TestExecutionStarted(DateTimeOffset.Now, degreeOfParallelism, _isDiscovery); + } + } + + private void InitializeHelpActionQueue(int degreeOfParallelism, BuildConfigurationOptions buildConfigurationOptions, bool filterModeEnabled) + { + _actionQueue = new(degreeOfParallelism, async (TestApplication testApp) => + { + testApp.HelpRequested += OnHelpRequested; + testApp.ErrorReceived += OnErrorReceived; + testApp.TestProcessExited += OnTestProcessExited; + testApp.ExecutionIdReceived += OnExecutionIdReceived; + + return await testApp.RunAsync(filterModeEnabled, enableHelp: true, buildConfigurationOptions); + }); + } + + private void InitializeTestExecutionActionQueue(int degreeOfParallelism, BuildConfigurationOptions buildConfigurationOptions, bool filterModeEnabled) + { + _actionQueue = new(degreeOfParallelism, async (TestApplication testApp) => + { + testApp.HandshakeReceived += OnHandshakeReceived; + testApp.DiscoveredTestsReceived += OnDiscoveredTestsReceived; + testApp.TestResultsReceived += OnTestResultsReceived; + testApp.FileArtifactsReceived += OnFileArtifactsReceived; + testApp.SessionEventReceived += OnSessionEventReceived; + testApp.ErrorReceived += OnErrorReceived; + testApp.TestProcessExited += OnTestProcessExited; + testApp.ExecutionIdReceived += OnExecutionIdReceived; + + return await testApp.RunAsync(filterModeEnabled, enableHelp: false, buildConfigurationOptions); + }); + } + private static int GetDegreeOfParallelism(ParseResult parseResult) { if (!int.TryParse(parseResult.GetValue(TestingPlatformOptions.MaxParallelTestModulesOption), out int degreeOfParallelism) || degreeOfParallelism <= 0) @@ -161,50 +156,20 @@ private static int GetDegreeOfParallelism(ParseResult parseResult) } private static BuildConfigurationOptions GetBuildConfigurationOptions(ParseResult parseResult) => - new(parseResult.HasOption(TestingPlatformOptions.NoRestoreOption), - parseResult.HasOption(TestingPlatformOptions.NoBuildOption), - parseResult.HasOption(TestingPlatformOptions.ListTestsOption), + new(parseResult.HasOption(TestingPlatformOptions.ListTestsOption), parseResult.GetValue(TestingPlatformOptions.ConfigurationOption), parseResult.GetValue(TestingPlatformOptions.ArchitectureOption)); private static BuildPathsOptions GetBuildPathOptions(ParseResult parseResult) => new(parseResult.GetValue(TestingPlatformOptions.ProjectOption), parseResult.GetValue(TestingPlatformOptions.SolutionOption), - parseResult.GetValue(TestingPlatformOptions.DirectoryOption)); - - private void InitializeActionQueue(ParseResult parseResult, int degreeOfParallelism, BuildConfigurationOptions buildConfigurationOptions) - { - if (!ContainsHelpOption(parseResult.GetArguments())) - { - _actionQueue = new(degreeOfParallelism, async (TestApplication testApp) => - { - testApp.HandshakeReceived += OnHandshakeReceived; - testApp.DiscoveredTestsReceived += OnDiscoveredTestsReceived; - testApp.TestResultsReceived += OnTestResultsReceived; - testApp.FileArtifactsReceived += OnFileArtifactsReceived; - testApp.SessionEventReceived += OnSessionEventReceived; - testApp.ErrorReceived += OnErrorReceived; - testApp.TestProcessExited += OnTestProcessExited; - testApp.Run += OnTestApplicationRun; - testApp.ExecutionIdReceived += OnExecutionIdReceived; - - return await testApp.RunAsync(hasFilterMode: false, enableHelp: false, buildConfigurationOptions); - }); - } - else - { - _actionQueue = new(degreeOfParallelism, async (TestApplication testApp) => - { - testApp.HelpRequested += OnHelpRequested; - testApp.ErrorReceived += OnErrorReceived; - testApp.TestProcessExited += OnTestProcessExited; - testApp.Run += OnTestApplicationRun; - testApp.ExecutionIdReceived += OnExecutionIdReceived; - - return await testApp.RunAsync(hasFilterMode: true, enableHelp: true, buildConfigurationOptions); - }); - } - } + parseResult.GetValue(TestingPlatformOptions.DirectoryOption), + parseResult.HasOption(TestingPlatformOptions.NoRestoreOption), + parseResult.HasOption(TestingPlatformOptions.NoBuildOption), + parseResult.GetValue(TestingPlatformOptions.ConfigurationOption), + parseResult.HasOption(TestingPlatformOptions.ArchitectureOption) ? + CommonOptions.ResolveRidShorthandOptionsToRuntimeIdentifier(string.Empty, parseResult.GetValue(TestingPlatformOptions.ArchitectureOption)) : + string.Empty); private static bool ContainsHelpOption(IEnumerable args) => args.Contains(CliConstants.HelpOptionKey) || args.Contains(CliConstants.HelpOptionKey.Substring(0, 2)); @@ -222,9 +187,9 @@ private void CompleteRun() private void CleanUp() { _msBuildHandler.Dispose(); - foreach (var testApplication in _testApplications) + foreach (var execution in _executions) { - testApplication.Dispose(); + execution.Key.Dispose(); } } @@ -242,10 +207,24 @@ private void OnHandshakeReceived(object sender, HandshakeArgs args) foreach (var property in args.Handshake.Properties) { - VSTestTrace.SafeWriteTrace(() => $"{property.Key}: {property.Value}"); + VSTestTrace.SafeWriteTrace(() => $"{GetHandshakePropertyName(property.Key)}: {property.Value}"); } } + private static string GetHandshakePropertyName(byte propertyId) => + propertyId switch + { + HandshakeMessagePropertyNames.PID => nameof(HandshakeMessagePropertyNames.PID), + HandshakeMessagePropertyNames.Architecture => nameof(HandshakeMessagePropertyNames.Architecture), + HandshakeMessagePropertyNames.Framework => nameof(HandshakeMessagePropertyNames.Framework), + HandshakeMessagePropertyNames.OS => nameof(HandshakeMessagePropertyNames.OS), + HandshakeMessagePropertyNames.SupportedProtocolVersions => nameof(HandshakeMessagePropertyNames.SupportedProtocolVersions), + HandshakeMessagePropertyNames.HostType => nameof(HandshakeMessagePropertyNames.HostType), + HandshakeMessagePropertyNames.ModulePath => nameof(HandshakeMessagePropertyNames.ModulePath), + HandshakeMessagePropertyNames.ExecutionId => nameof(HandshakeMessagePropertyNames.ExecutionId), + _ => string.Empty, + }; + private void OnDiscoveredTestsReceived(object sender, DiscoveredTestEventArgs args) { var testApp = (TestApplication)sender; @@ -410,12 +389,6 @@ private void OnTestProcessExited(object sender, TestProcessExitEventArgs args) } } - private void OnTestApplicationRun(object sender, EventArgs args) - { - TestApplication testApp = sender as TestApplication; - _testApplications.Add(testApp); - } - private void OnExecutionIdReceived(object sender, ExecutionEventArgs args) { } diff --git a/test/Microsoft.NET.TestFramework/Commands/TestCommand.cs b/test/Microsoft.NET.TestFramework/Commands/TestCommand.cs index 96d555bb293f..bd8efafeb284 100644 --- a/test/Microsoft.NET.TestFramework/Commands/TestCommand.cs +++ b/test/Microsoft.NET.TestFramework/Commands/TestCommand.cs @@ -61,6 +61,12 @@ public TestCommand WithTraceOutput() return this; } + public TestCommand WithEnableTestingPlatform() + { + WithEnvironmentVariable("DOTNET_CLI_TESTINGPLATFORM_ENABLE", "1"); + return this; + } + private SdkCommandSpec CreateCommandSpec(IEnumerable args) { var commandSpec = CreateCommand(args); diff --git a/test/TestAssets/TestProjects/EmptyFolder/file.txt b/test/TestAssets/TestProjects/EmptyFolder/file.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/test/TestAssets/TestProjects/HybridTestRunnerTestProjects/AnotherTestProject/AnotherTestProject.csproj b/test/TestAssets/TestProjects/HybridTestRunnerTestProjects/AnotherTestProject/AnotherTestProject.csproj new file mode 100644 index 000000000000..37286601726d --- /dev/null +++ b/test/TestAssets/TestProjects/HybridTestRunnerTestProjects/AnotherTestProject/AnotherTestProject.csproj @@ -0,0 +1,20 @@ + + + + + $(CurrentTargetFramework) + latest + enable + enable + + + + + + + + + + + + \ No newline at end of file diff --git a/test/TestAssets/TestProjects/HybridTestRunnerTestProjects/AnotherTestProject/MSTestSettings.cs b/test/TestAssets/TestProjects/HybridTestRunnerTestProjects/AnotherTestProject/MSTestSettings.cs new file mode 100644 index 000000000000..aaf278c844f0 --- /dev/null +++ b/test/TestAssets/TestProjects/HybridTestRunnerTestProjects/AnotherTestProject/MSTestSettings.cs @@ -0,0 +1 @@ +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)] diff --git a/test/TestAssets/TestProjects/HybridTestRunnerTestProjects/AnotherTestProject/Test1.cs b/test/TestAssets/TestProjects/HybridTestRunnerTestProjects/AnotherTestProject/Test1.cs new file mode 100644 index 000000000000..46eeff4520ba --- /dev/null +++ b/test/TestAssets/TestProjects/HybridTestRunnerTestProjects/AnotherTestProject/Test1.cs @@ -0,0 +1,11 @@ +namespace AnotherTestProject +{ + [TestClass] + public sealed class Test1 + { + [TestMethod] + public void TestMethod1() + { + } + } +} diff --git a/test/TestAssets/TestProjects/HybridTestRunnerTestProjects/HybridTestRunnerTestProjects.sln b/test/TestAssets/TestProjects/HybridTestRunnerTestProjects/HybridTestRunnerTestProjects.sln new file mode 100644 index 000000000000..027f72f15fbe --- /dev/null +++ b/test/TestAssets/TestProjects/HybridTestRunnerTestProjects/HybridTestRunnerTestProjects.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35415.258 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OtherTestProject", "OtherTestProject\OtherTestProject.csproj", "{8683AE78-764B-46C2-98D4-8A3031CBA27E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnotherTestProject", "AnotherTestProject\AnotherTestProject.csproj", "{2604B2BA-D36A-461D-9BB3-207E4253051C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8683AE78-764B-46C2-98D4-8A3031CBA27E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8683AE78-764B-46C2-98D4-8A3031CBA27E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8683AE78-764B-46C2-98D4-8A3031CBA27E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8683AE78-764B-46C2-98D4-8A3031CBA27E}.Release|Any CPU.Build.0 = Release|Any CPU + {2604B2BA-D36A-461D-9BB3-207E4253051C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2604B2BA-D36A-461D-9BB3-207E4253051C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2604B2BA-D36A-461D-9BB3-207E4253051C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2604B2BA-D36A-461D-9BB3-207E4253051C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/test/TestAssets/TestProjects/HybridTestRunnerTestProjects/OtherTestProject/OtherTestProject.csproj b/test/TestAssets/TestProjects/HybridTestRunnerTestProjects/OtherTestProject/OtherTestProject.csproj new file mode 100644 index 000000000000..11c124c3ee7e --- /dev/null +++ b/test/TestAssets/TestProjects/HybridTestRunnerTestProjects/OtherTestProject/OtherTestProject.csproj @@ -0,0 +1,17 @@ + + + + + Exe + $(CurrentTargetFramework) + enable + enable + true + true + + + + + + + diff --git a/test/TestAssets/TestProjects/HybridTestRunnerTestProjects/OtherTestProject/Program.cs b/test/TestAssets/TestProjects/HybridTestRunnerTestProjects/OtherTestProject/Program.cs new file mode 100644 index 000000000000..f8fcbb648156 --- /dev/null +++ b/test/TestAssets/TestProjects/HybridTestRunnerTestProjects/OtherTestProject/Program.cs @@ -0,0 +1,106 @@ +//See https://aka.ms/new-console-template for more information + +//Opt -out telemetry + +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +//testApplicationBuilder.AddMSTest(() => new[] { Assembly.GetEntryAssembly()! }); +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => new[] { + typeof(TestNodeUpdateMessage), + typeof(SessionFileArtifact), + typeof(TestNodeFileArtifact), + typeof(FileArtifact), }; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test0", + DisplayName = "Test0", + Properties = new PropertyBag(new DiscoveredTestNodeStateProperty()), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test0", + DisplayName = "Test0", + Properties = new PropertyBag(new PassedTestNodeStateProperty("OK")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test1", + DisplayName = "Test1", + Properties = new PropertyBag(new SkippedTestNodeStateProperty("OK skipped!")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test2", + DisplayName = "Test2", + Properties = new PropertyBag(new FailedTestNodeStateProperty(new Exception("this is a failed test"), "not OK")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test3", + DisplayName = "Test3", + Properties = new PropertyBag(new TimeoutTestNodeStateProperty(new Exception("this is a timeout exception"), "not OK")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test4", + DisplayName = "Test4", + Properties = new PropertyBag(new ErrorTestNodeStateProperty(new Exception("this is an exception"), "not OK")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test5", + DisplayName = "Test5", + Properties = new PropertyBag(new CancelledTestNodeStateProperty(new Exception("this is a cancelled exception"), "not OK")), + })); + + await context.MessageBus.PublishAsync(this, new FileArtifact(new FileInfo("file.txt"), "file", "file description")); + + await context.MessageBus.PublishAsync(this, new SessionFileArtifact(context.Request.Session.SessionUid, new FileInfo("sessionFile.txt"), "sessionFile", "description")); + + await context.MessageBus.PublishAsync(this, new TestNodeFileArtifact(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test6 id", + DisplayName = "Test6", + Properties = new PropertyBag(new PassedTestNodeStateProperty("OK")), + }, new FileInfo("testNodeFile.txt"), "testNodeFile", "description")); + //await Task.CompletedTask; + context.Complete(); + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/MSTestMetaPackageProjectWithMultipleTFMsSolution/MSTestMetaPackageProjectWithMultipleTFMsSolution.sln b/test/TestAssets/TestProjects/MSTestMetaPackageProjectWithMultipleTFMsSolution/MSTestMetaPackageProjectWithMultipleTFMsSolution.sln new file mode 100644 index 000000000000..27a9cf62907c --- /dev/null +++ b/test/TestAssets/TestProjects/MSTestMetaPackageProjectWithMultipleTFMsSolution/MSTestMetaPackageProjectWithMultipleTFMsSolution.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35415.258 main +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject", "TestProject\TestProject.csproj", "{0D246F24-651A-479B-AC3C-8D94F66A5E7E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0D246F24-651A-479B-AC3C-8D94F66A5E7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0D246F24-651A-479B-AC3C-8D94F66A5E7E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0D246F24-651A-479B-AC3C-8D94F66A5E7E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0D246F24-651A-479B-AC3C-8D94F66A5E7E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/test/TestAssets/TestProjects/MSTestMetaPackageProjectWithMultipleTFMsSolution/TestProject/TestProject.csproj b/test/TestAssets/TestProjects/MSTestMetaPackageProjectWithMultipleTFMsSolution/TestProject/TestProject.csproj new file mode 100644 index 000000000000..7b590e68eec5 --- /dev/null +++ b/test/TestAssets/TestProjects/MSTestMetaPackageProjectWithMultipleTFMsSolution/TestProject/TestProject.csproj @@ -0,0 +1,36 @@ + + + + + + true + + Exe + + net8.0;net9.0 + enable + enable + + false + true + true + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/TestAssets/TestProjects/MSTestMetaPackageProjectWithMultipleTFMsSolution/TestProject/UnitTest1.cs b/test/TestAssets/TestProjects/MSTestMetaPackageProjectWithMultipleTFMsSolution/TestProject/UnitTest1.cs new file mode 100644 index 000000000000..19f247ea1459 --- /dev/null +++ b/test/TestAssets/TestProjects/MSTestMetaPackageProjectWithMultipleTFMsSolution/TestProject/UnitTest1.cs @@ -0,0 +1,25 @@ +namespace TestProject; + +[TestClass] +public class UnitTest1 +{ + [TestMethod] + public void TestMethod1() + { + Assert.AreEqual(1, 1); + } + +#if NET8_0 + [TestMethod] + public void TestMethod2() + { + Assert.AreEqual(1, 1); + } +#endif + + [TestMethod] + public void TestMethod3() + { + Assert.AreEqual(1, 0); + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/AnotherTestProject/AnotherTestProject.csproj b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/AnotherTestProject/AnotherTestProject.csproj new file mode 100644 index 000000000000..e78435c52b0b --- /dev/null +++ b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/AnotherTestProject/AnotherTestProject.csproj @@ -0,0 +1,20 @@ + + + + + net8.0 + Exe + + enable + enable + true + + false + false + true + + + + + + diff --git a/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/AnotherTestProject/Program.cs b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/AnotherTestProject/Program.cs new file mode 100644 index 000000000000..28a9001705af --- /dev/null +++ b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/AnotherTestProject/Program.cs @@ -0,0 +1,46 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => new[] { + typeof(TestNodeUpdateMessage) + }; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test0", + DisplayName = "Test0", + Properties = new PropertyBag(new PassedTestNodeStateProperty("OK")), + })); + + context.Complete(); + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/MultiTestProjectSolutionWithDifferentFailures.sln b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/MultiTestProjectSolutionWithDifferentFailures.sln new file mode 100644 index 000000000000..46fb718cc0c1 --- /dev/null +++ b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/MultiTestProjectSolutionWithDifferentFailures.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35415.258 main +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject", "TestProject\TestProject.csproj", "{EB61ABDF-A476-4CD0-8F05-78C1CFB8D011}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OtherTestProject", "OtherTestProject\OtherTestProject.csproj", "{10324BA1-E676-44C6-AF77-5186CEF704FC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnotherTestProject", "AnotherTestProject\AnotherTestProject.csproj", "{7BCB85D3-DFED-4FAA-BCB5-5F0638331CC3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EB61ABDF-A476-4CD0-8F05-78C1CFB8D011}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EB61ABDF-A476-4CD0-8F05-78C1CFB8D011}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EB61ABDF-A476-4CD0-8F05-78C1CFB8D011}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EB61ABDF-A476-4CD0-8F05-78C1CFB8D011}.Release|Any CPU.Build.0 = Release|Any CPU + {10324BA1-E676-44C6-AF77-5186CEF704FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {10324BA1-E676-44C6-AF77-5186CEF704FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {10324BA1-E676-44C6-AF77-5186CEF704FC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {10324BA1-E676-44C6-AF77-5186CEF704FC}.Release|Any CPU.Build.0 = Release|Any CPU + {7BCB85D3-DFED-4FAA-BCB5-5F0638331CC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7BCB85D3-DFED-4FAA-BCB5-5F0638331CC3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7BCB85D3-DFED-4FAA-BCB5-5F0638331CC3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7BCB85D3-DFED-4FAA-BCB5-5F0638331CC3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/OtherTestProject/OtherTestProject.csproj b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/OtherTestProject/OtherTestProject.csproj new file mode 100644 index 000000000000..e78435c52b0b --- /dev/null +++ b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/OtherTestProject/OtherTestProject.csproj @@ -0,0 +1,20 @@ + + + + + net8.0 + Exe + + enable + enable + true + + false + false + true + + + + + + diff --git a/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/OtherTestProject/Program.cs b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/OtherTestProject/Program.cs new file mode 100644 index 000000000000..bba220658aa8 --- /dev/null +++ b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/OtherTestProject/Program.cs @@ -0,0 +1,60 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => new[] { + typeof(TestNodeUpdateMessage) + }; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test1", + DisplayName = "Test1", + Properties = new PropertyBag(new PassedTestNodeStateProperty("OK")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test2", + DisplayName = "Test2", + Properties = new PropertyBag(new SkippedTestNodeStateProperty("OK skipped!")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test3", + DisplayName = "Test3", + Properties = new PropertyBag(new FailedTestNodeStateProperty(new Exception("this is a failed test"), "not OK")), + })); + + context.Complete(); + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/TestProject/Program.cs b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/TestProject/Program.cs new file mode 100644 index 000000000000..b8efb9c72be9 --- /dev/null +++ b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/TestProject/Program.cs @@ -0,0 +1,38 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => []; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + context.Complete(); + await Task.CompletedTask; + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/TestProject/TestProject.csproj b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/TestProject/TestProject.csproj new file mode 100644 index 000000000000..e78435c52b0b --- /dev/null +++ b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDifferentFailures/TestProject/TestProject.csproj @@ -0,0 +1,20 @@ + + + + + net8.0 + Exe + + enable + enable + true + + false + false + true + + + + + + diff --git a/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDiscoveredTests/MultiTestProjectSolutionWithDiscoveredTests.sln b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDiscoveredTests/MultiTestProjectSolutionWithDiscoveredTests.sln new file mode 100644 index 000000000000..17275f0f8f30 --- /dev/null +++ b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDiscoveredTests/MultiTestProjectSolutionWithDiscoveredTests.sln @@ -0,0 +1,32 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35322.30 main +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject", "TestProject\TestProject.csproj", "{C43FCD94-028D-4DE8-96EC-A3663AE225B4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OtherTestProject", "OtherTestProject\OtherTestProject.csproj", "{6171FC1F-E2F2-4F66-A8DE-EC4765081661}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C43FCD94-028D-4DE8-96EC-A3663AE225B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C43FCD94-028D-4DE8-96EC-A3663AE225B4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C43FCD94-028D-4DE8-96EC-A3663AE225B4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C43FCD94-028D-4DE8-96EC-A3663AE225B4}.Release|Any CPU.Build.0 = Release|Any CPU + {C43FCD94-028D-4DE8-96EC-A3663AE225B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C43FCD94-028D-4DE8-96EC-A3663AE225B4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C43FCD94-028D-4DE8-96EC-A3663AE225B4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C43FCD94-028D-4DE8-96EC-A3663AE225B4}.Release|Any CPU.Build.0 = Release|Any CPU + {6171FC1F-E2F2-4F66-A8DE-EC4765081661}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6171FC1F-E2F2-4F66-A8DE-EC4765081661}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6171FC1F-E2F2-4F66-A8DE-EC4765081661}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6171FC1F-E2F2-4F66-A8DE-EC4765081661}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDiscoveredTests/OtherTestProject/OtherTestProject.csproj b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDiscoveredTests/OtherTestProject/OtherTestProject.csproj new file mode 100644 index 000000000000..19a9d61c98f1 --- /dev/null +++ b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDiscoveredTests/OtherTestProject/OtherTestProject.csproj @@ -0,0 +1,20 @@ + + + + + net8.0 + Exe + + enable + enable + true + + false + false + true + + + + + + diff --git a/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDiscoveredTests/OtherTestProject/Program.cs b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDiscoveredTests/OtherTestProject/Program.cs new file mode 100644 index 000000000000..ea33026cd253 --- /dev/null +++ b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDiscoveredTests/OtherTestProject/Program.cs @@ -0,0 +1,46 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => new[] { + typeof(TestNodeUpdateMessage) + }; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test1", + DisplayName = "Test1", + Properties = new PropertyBag(new DiscoveredTestNodeStateProperty()), + })); + + context.Complete(); + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDiscoveredTests/TestProject/Program.cs b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDiscoveredTests/TestProject/Program.cs new file mode 100644 index 000000000000..d6f6d84b2367 --- /dev/null +++ b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDiscoveredTests/TestProject/Program.cs @@ -0,0 +1,53 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => new[] { + typeof(TestNodeUpdateMessage) + }; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test0", + DisplayName = "Test0", + Properties = new PropertyBag(new DiscoveredTestNodeStateProperty()), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test2", + DisplayName = "Test2", + Properties = new PropertyBag(new DiscoveredTestNodeStateProperty()), + })); + + context.Complete(); + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDiscoveredTests/TestProject/TestProject.csproj b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDiscoveredTests/TestProject/TestProject.csproj new file mode 100644 index 000000000000..19a9d61c98f1 --- /dev/null +++ b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDiscoveredTests/TestProject/TestProject.csproj @@ -0,0 +1,20 @@ + + + + + net8.0 + Exe + + enable + enable + true + + false + false + true + + + + + + diff --git a/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDiscoveredTests/global.json b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDiscoveredTests/global.json new file mode 100644 index 000000000000..daa3ae1f149d --- /dev/null +++ b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithDiscoveredTests/global.json @@ -0,0 +1,7 @@ +{ + "test" : { + "runner": { + "name": "MicrosoftTestingPlatform" + } + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/MultiTestProjectSolutionWithTests/MultiTestProjectSolutionWithTests.sln b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithTests/MultiTestProjectSolutionWithTests.sln new file mode 100644 index 000000000000..17275f0f8f30 --- /dev/null +++ b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithTests/MultiTestProjectSolutionWithTests.sln @@ -0,0 +1,32 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35322.30 main +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject", "TestProject\TestProject.csproj", "{C43FCD94-028D-4DE8-96EC-A3663AE225B4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OtherTestProject", "OtherTestProject\OtherTestProject.csproj", "{6171FC1F-E2F2-4F66-A8DE-EC4765081661}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C43FCD94-028D-4DE8-96EC-A3663AE225B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C43FCD94-028D-4DE8-96EC-A3663AE225B4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C43FCD94-028D-4DE8-96EC-A3663AE225B4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C43FCD94-028D-4DE8-96EC-A3663AE225B4}.Release|Any CPU.Build.0 = Release|Any CPU + {C43FCD94-028D-4DE8-96EC-A3663AE225B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C43FCD94-028D-4DE8-96EC-A3663AE225B4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C43FCD94-028D-4DE8-96EC-A3663AE225B4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C43FCD94-028D-4DE8-96EC-A3663AE225B4}.Release|Any CPU.Build.0 = Release|Any CPU + {6171FC1F-E2F2-4F66-A8DE-EC4765081661}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6171FC1F-E2F2-4F66-A8DE-EC4765081661}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6171FC1F-E2F2-4F66-A8DE-EC4765081661}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6171FC1F-E2F2-4F66-A8DE-EC4765081661}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/test/TestAssets/TestProjects/MultiTestProjectSolutionWithTests/OtherTestProject/OtherTestProject.csproj b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithTests/OtherTestProject/OtherTestProject.csproj new file mode 100644 index 000000000000..19a9d61c98f1 --- /dev/null +++ b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithTests/OtherTestProject/OtherTestProject.csproj @@ -0,0 +1,20 @@ + + + + + net8.0 + Exe + + enable + enable + true + + false + false + true + + + + + + diff --git a/test/TestAssets/TestProjects/MultiTestProjectSolutionWithTests/OtherTestProject/Program.cs b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithTests/OtherTestProject/Program.cs new file mode 100644 index 000000000000..7f1ee5386c15 --- /dev/null +++ b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithTests/OtherTestProject/Program.cs @@ -0,0 +1,53 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => new[] { + typeof(TestNodeUpdateMessage) + }; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test1", + DisplayName = "Test1", + Properties = new PropertyBag(new PassedTestNodeStateProperty("OK")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test2", + DisplayName = "Test2", + Properties = new PropertyBag(new SkippedTestNodeStateProperty("skipped")), + })); + + context.Complete(); + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/MultiTestProjectSolutionWithTests/TestProject/Program.cs b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithTests/TestProject/Program.cs new file mode 100644 index 000000000000..cdecea10ffeb --- /dev/null +++ b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithTests/TestProject/Program.cs @@ -0,0 +1,60 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => new[] { + typeof(TestNodeUpdateMessage) + }; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test0", + DisplayName = "Test0", + Properties = new PropertyBag(new PassedTestNodeStateProperty("OK")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test1", + DisplayName = "Test1", + Properties = new PropertyBag(new SkippedTestNodeStateProperty("OK skipped!")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test2", + DisplayName = "Test2", + Properties = new PropertyBag(new FailedTestNodeStateProperty(new Exception("this is a failed test"), "not OK")), + })); + + context.Complete(); + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/MultiTestProjectSolutionWithTests/TestProject/TestProject.csproj b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithTests/TestProject/TestProject.csproj new file mode 100644 index 000000000000..19a9d61c98f1 --- /dev/null +++ b/test/TestAssets/TestProjects/MultiTestProjectSolutionWithTests/TestProject/TestProject.csproj @@ -0,0 +1,20 @@ + + + + + net8.0 + Exe + + enable + enable + true + + false + false + true + + + + + + diff --git a/test/TestAssets/TestProjects/MultipleTestProjectSolution/AnotherTestProject/AnotherTestProject.csproj b/test/TestAssets/TestProjects/MultipleTestProjectSolution/AnotherTestProject/AnotherTestProject.csproj new file mode 100644 index 000000000000..19a9d61c98f1 --- /dev/null +++ b/test/TestAssets/TestProjects/MultipleTestProjectSolution/AnotherTestProject/AnotherTestProject.csproj @@ -0,0 +1,20 @@ + + + + + net8.0 + Exe + + enable + enable + true + + false + false + true + + + + + + diff --git a/test/TestAssets/TestProjects/MultipleTestProjectSolution/AnotherTestProject/Program.cs b/test/TestAssets/TestProjects/MultipleTestProjectSolution/AnotherTestProject/Program.cs new file mode 100644 index 000000000000..b8efb9c72be9 --- /dev/null +++ b/test/TestAssets/TestProjects/MultipleTestProjectSolution/AnotherTestProject/Program.cs @@ -0,0 +1,38 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => []; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + context.Complete(); + await Task.CompletedTask; + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/MultipleTestProjectSolution/MultipleTestProjectSolution.sln b/test/TestAssets/TestProjects/MultipleTestProjectSolution/MultipleTestProjectSolution.sln new file mode 100644 index 000000000000..e9eba761916e --- /dev/null +++ b/test/TestAssets/TestProjects/MultipleTestProjectSolution/MultipleTestProjectSolution.sln @@ -0,0 +1,33 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35322.30 main +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject", "TestProject\TestProject.csproj", "{C6D846BA-EFD0-44A5-A07C-6C50D264F63E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OtherTestProject", "OtherTestProject\OtherTestProject.csproj", "{0E527CEF-292C-4265-81EB-4C9F51072219}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnotherTestProject", "AnotherTestProject\AnotherTestProject.csproj", "{9C734AA0-998F-4CB8-BEB8-043D47AB9996}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C6D846BA-EFD0-44A5-A07C-6C50D264F63E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C6D846BA-EFD0-44A5-A07C-6C50D264F63E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C6D846BA-EFD0-44A5-A07C-6C50D264F63E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C6D846BA-EFD0-44A5-A07C-6C50D264F63E}.Release|Any CPU.Build.0 = Release|Any CPU + {0E527CEF-292C-4265-81EB-4C9F51072219}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0E527CEF-292C-4265-81EB-4C9F51072219}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0E527CEF-292C-4265-81EB-4C9F51072219}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0E527CEF-292C-4265-81EB-4C9F51072219}.Release|Any CPU.Build.0 = Release|Any CPU + {9C734AA0-998F-4CB8-BEB8-043D47AB9996}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9C734AA0-998F-4CB8-BEB8-043D47AB9996}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9C734AA0-998F-4CB8-BEB8-043D47AB9996}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9C734AA0-998F-4CB8-BEB8-043D47AB9996}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/test/TestAssets/TestProjects/MultipleTestProjectSolution/OtherTestProject/OtherTestProject.csproj b/test/TestAssets/TestProjects/MultipleTestProjectSolution/OtherTestProject/OtherTestProject.csproj new file mode 100644 index 000000000000..1acd030edec2 --- /dev/null +++ b/test/TestAssets/TestProjects/MultipleTestProjectSolution/OtherTestProject/OtherTestProject.csproj @@ -0,0 +1,20 @@ + + + + + net8.0 + Exe + + enable + enable + true + + false + false + true + + + + + + diff --git a/test/TestAssets/TestProjects/MultipleTestProjectSolution/OtherTestProject/Program.cs b/test/TestAssets/TestProjects/MultipleTestProjectSolution/OtherTestProject/Program.cs new file mode 100644 index 000000000000..b8efb9c72be9 --- /dev/null +++ b/test/TestAssets/TestProjects/MultipleTestProjectSolution/OtherTestProject/Program.cs @@ -0,0 +1,38 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => []; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + context.Complete(); + await Task.CompletedTask; + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/MultipleTestProjectSolution/TestProject/Program.cs b/test/TestAssets/TestProjects/MultipleTestProjectSolution/TestProject/Program.cs new file mode 100644 index 000000000000..b8efb9c72be9 --- /dev/null +++ b/test/TestAssets/TestProjects/MultipleTestProjectSolution/TestProject/Program.cs @@ -0,0 +1,38 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => []; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + context.Complete(); + await Task.CompletedTask; + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/MultipleTestProjectSolution/TestProject/TestProject.csproj b/test/TestAssets/TestProjects/MultipleTestProjectSolution/TestProject/TestProject.csproj new file mode 100644 index 000000000000..35acc103cb62 --- /dev/null +++ b/test/TestAssets/TestProjects/MultipleTestProjectSolution/TestProject/TestProject.csproj @@ -0,0 +1,20 @@ + + + + + net8.0 + Exe + + enable + enable + true + + false + false + true + + + + + + diff --git a/test/TestAssets/TestProjects/MultipleTestProjectsWithoutSolution/AnotherTestProject/AnotherTestProject.csproj b/test/TestAssets/TestProjects/MultipleTestProjectsWithoutSolution/AnotherTestProject/AnotherTestProject.csproj new file mode 100644 index 000000000000..19a9d61c98f1 --- /dev/null +++ b/test/TestAssets/TestProjects/MultipleTestProjectsWithoutSolution/AnotherTestProject/AnotherTestProject.csproj @@ -0,0 +1,20 @@ + + + + + net8.0 + Exe + + enable + enable + true + + false + false + true + + + + + + diff --git a/test/TestAssets/TestProjects/MultipleTestProjectsWithoutSolution/AnotherTestProject/Program.cs b/test/TestAssets/TestProjects/MultipleTestProjectsWithoutSolution/AnotherTestProject/Program.cs new file mode 100644 index 000000000000..b8efb9c72be9 --- /dev/null +++ b/test/TestAssets/TestProjects/MultipleTestProjectsWithoutSolution/AnotherTestProject/Program.cs @@ -0,0 +1,38 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => []; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + context.Complete(); + await Task.CompletedTask; + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/MultipleTestProjectsWithoutSolution/OtherTestProject/OtherTestProject.csproj b/test/TestAssets/TestProjects/MultipleTestProjectsWithoutSolution/OtherTestProject/OtherTestProject.csproj new file mode 100644 index 000000000000..1acd030edec2 --- /dev/null +++ b/test/TestAssets/TestProjects/MultipleTestProjectsWithoutSolution/OtherTestProject/OtherTestProject.csproj @@ -0,0 +1,20 @@ + + + + + net8.0 + Exe + + enable + enable + true + + false + false + true + + + + + + diff --git a/test/TestAssets/TestProjects/MultipleTestProjectsWithoutSolution/OtherTestProject/Program.cs b/test/TestAssets/TestProjects/MultipleTestProjectsWithoutSolution/OtherTestProject/Program.cs new file mode 100644 index 000000000000..b8efb9c72be9 --- /dev/null +++ b/test/TestAssets/TestProjects/MultipleTestProjectsWithoutSolution/OtherTestProject/Program.cs @@ -0,0 +1,38 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => []; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + context.Complete(); + await Task.CompletedTask; + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/MultipleTestProjectsWithoutSolution/TestProject/Program.cs b/test/TestAssets/TestProjects/MultipleTestProjectsWithoutSolution/TestProject/Program.cs new file mode 100644 index 000000000000..b8efb9c72be9 --- /dev/null +++ b/test/TestAssets/TestProjects/MultipleTestProjectsWithoutSolution/TestProject/Program.cs @@ -0,0 +1,38 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => []; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + context.Complete(); + await Task.CompletedTask; + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/MultipleTestProjectsWithoutSolution/TestProject/TestProject.csproj b/test/TestAssets/TestProjects/MultipleTestProjectsWithoutSolution/TestProject/TestProject.csproj new file mode 100644 index 000000000000..35acc103cb62 --- /dev/null +++ b/test/TestAssets/TestProjects/MultipleTestProjectsWithoutSolution/TestProject/TestProject.csproj @@ -0,0 +1,20 @@ + + + + + net8.0 + Exe + + enable + enable + true + + false + false + true + + + + + + diff --git a/test/TestAssets/TestProjects/ProjectSolutionForMultipleTFMs/ProjectSolutionForMultipleTFMs.sln b/test/TestAssets/TestProjects/ProjectSolutionForMultipleTFMs/ProjectSolutionForMultipleTFMs.sln new file mode 100644 index 000000000000..5ba28415f8a0 --- /dev/null +++ b/test/TestAssets/TestProjects/ProjectSolutionForMultipleTFMs/ProjectSolutionForMultipleTFMs.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35322.30 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProjectWithNet9", "TestProjectWithNet9\TestProjectWithNet9.csproj", "{EF95A0BF-ADC3-4B37-B1EB-0436D24D615F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProjectWithNet8", "TestProjectWithNet8\TestProjectWithNet8.csproj", "{4B9F47C4-06A0-4A41-9F8D-CEC199C49CAA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EF95A0BF-ADC3-4B37-B1EB-0436D24D615F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF95A0BF-ADC3-4B37-B1EB-0436D24D615F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF95A0BF-ADC3-4B37-B1EB-0436D24D615F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF95A0BF-ADC3-4B37-B1EB-0436D24D615F}.Release|Any CPU.Build.0 = Release|Any CPU + {4B9F47C4-06A0-4A41-9F8D-CEC199C49CAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4B9F47C4-06A0-4A41-9F8D-CEC199C49CAA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4B9F47C4-06A0-4A41-9F8D-CEC199C49CAA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4B9F47C4-06A0-4A41-9F8D-CEC199C49CAA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/test/TestAssets/TestProjects/ProjectSolutionForMultipleTFMs/TestProjectWithNet8/Program.cs b/test/TestAssets/TestProjects/ProjectSolutionForMultipleTFMs/TestProjectWithNet8/Program.cs new file mode 100644 index 000000000000..d32c5029df4e --- /dev/null +++ b/test/TestAssets/TestProjects/ProjectSolutionForMultipleTFMs/TestProjectWithNet8/Program.cs @@ -0,0 +1,62 @@ +using Microsoft.Testing.Extensions; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); +testApplicationBuilder.AddTrxReportProvider(); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => new[] { + typeof(TestNodeUpdateMessage) + }; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test1", + DisplayName = "Test1", + Properties = new PropertyBag(new PassedTestNodeStateProperty("OK")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test2", + DisplayName = "Test2", + Properties = new PropertyBag(new SkippedTestNodeStateProperty("OK skipped!")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test3", + DisplayName = "Test3", + Properties = new PropertyBag(new FailedTestNodeStateProperty(new Exception("this is a failed test"), "not OK")), + })); + + context.Complete(); + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/ProjectSolutionForMultipleTFMs/TestProjectWithNet8/TestProjectWithNet8.csproj b/test/TestAssets/TestProjects/ProjectSolutionForMultipleTFMs/TestProjectWithNet8/TestProjectWithNet8.csproj new file mode 100644 index 000000000000..9e3c61d9c8be --- /dev/null +++ b/test/TestAssets/TestProjects/ProjectSolutionForMultipleTFMs/TestProjectWithNet8/TestProjectWithNet8.csproj @@ -0,0 +1,21 @@ + + + + + net8.0 + Exe + + enable + enable + true + + false + false + true + + + + + + + diff --git a/test/TestAssets/TestProjects/ProjectSolutionForMultipleTFMs/TestProjectWithNet9/Program.cs b/test/TestAssets/TestProjects/ProjectSolutionForMultipleTFMs/TestProjectWithNet9/Program.cs new file mode 100644 index 000000000000..dd865efbf0b1 --- /dev/null +++ b/test/TestAssets/TestProjects/ProjectSolutionForMultipleTFMs/TestProjectWithNet9/Program.cs @@ -0,0 +1,53 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => new[] { + typeof(TestNodeUpdateMessage) + }; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test1", + DisplayName = "Test1", + Properties = new PropertyBag(new PassedTestNodeStateProperty("OK")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test2", + DisplayName = "Test2", + Properties = new PropertyBag(new SkippedTestNodeStateProperty("OK skipped!")), + })); + + context.Complete(); + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/ProjectSolutionForMultipleTFMs/TestProjectWithNet9/TestProjectWithNet9.csproj b/test/TestAssets/TestProjects/ProjectSolutionForMultipleTFMs/TestProjectWithNet9/TestProjectWithNet9.csproj new file mode 100644 index 000000000000..c6644242edfd --- /dev/null +++ b/test/TestAssets/TestProjects/ProjectSolutionForMultipleTFMs/TestProjectWithNet9/TestProjectWithNet9.csproj @@ -0,0 +1,20 @@ + + + + + net9.0 + Exe + + enable + enable + true + + false + false + true + + + + + + diff --git a/test/TestAssets/TestProjects/TestProjectFileAndSolutionFile/MultipleTestProjectSolution.sln b/test/TestAssets/TestProjects/TestProjectFileAndSolutionFile/MultipleTestProjectSolution.sln new file mode 100644 index 000000000000..4be44315b612 --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectFileAndSolutionFile/MultipleTestProjectSolution.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35322.30 main +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject", "TestProject.csproj", "{C6D846BA-EFD0-44A5-A07C-6C50D264F63E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C6D846BA-EFD0-44A5-A07C-6C50D264F63E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C6D846BA-EFD0-44A5-A07C-6C50D264F63E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C6D846BA-EFD0-44A5-A07C-6C50D264F63E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C6D846BA-EFD0-44A5-A07C-6C50D264F63E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/test/TestAssets/TestProjects/TestProjectFileAndSolutionFile/Program.cs b/test/TestAssets/TestProjects/TestProjectFileAndSolutionFile/Program.cs new file mode 100644 index 000000000000..b8efb9c72be9 --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectFileAndSolutionFile/Program.cs @@ -0,0 +1,38 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => []; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + context.Complete(); + await Task.CompletedTask; + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/TestProjectFileAndSolutionFile/TestProject.csproj b/test/TestAssets/TestProjects/TestProjectFileAndSolutionFile/TestProject.csproj new file mode 100644 index 000000000000..35acc103cb62 --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectFileAndSolutionFile/TestProject.csproj @@ -0,0 +1,20 @@ + + + + + net8.0 + Exe + + enable + enable + true + + false + false + true + + + + + + diff --git a/test/TestAssets/TestProjects/TestProjectSolution/TestProject/Program.cs b/test/TestAssets/TestProjects/TestProjectSolution/TestProject/Program.cs new file mode 100644 index 000000000000..b8efb9c72be9 --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectSolution/TestProject/Program.cs @@ -0,0 +1,38 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => []; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + context.Complete(); + await Task.CompletedTask; + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/TestProjectSolution/TestProject/TestProject.csproj b/test/TestAssets/TestProjects/TestProjectSolution/TestProject/TestProject.csproj new file mode 100644 index 000000000000..19a9d61c98f1 --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectSolution/TestProject/TestProject.csproj @@ -0,0 +1,20 @@ + + + + + net8.0 + Exe + + enable + enable + true + + false + false + true + + + + + + diff --git a/test/TestAssets/TestProjects/TestProjectSolution/TestProjectSolution.sln b/test/TestAssets/TestProjects/TestProjectSolution/TestProjectSolution.sln new file mode 100644 index 000000000000..15ee9bf00d5d --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectSolution/TestProjectSolution.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35322.30 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject", "TestProject\TestProject.csproj", "{A3CB990F-B603-4911-A669-87C60B41DF10}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A3CB990F-B603-4911-A669-87C60B41DF10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A3CB990F-B603-4911-A669-87C60B41DF10}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A3CB990F-B603-4911-A669-87C60B41DF10}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A3CB990F-B603-4911-A669-87C60B41DF10}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/test/TestAssets/TestProjects/TestProjectSolution/global.json b/test/TestAssets/TestProjects/TestProjectSolution/global.json new file mode 100644 index 000000000000..daa3ae1f149d --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectSolution/global.json @@ -0,0 +1,7 @@ +{ + "test" : { + "runner": { + "name": "MicrosoftTestingPlatform" + } + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/TestProjectSolutionWithCodeCoverage/TestProject/Program.cs b/test/TestAssets/TestProjects/TestProjectSolutionWithCodeCoverage/TestProject/Program.cs new file mode 100644 index 000000000000..07fc4f4a8c25 --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectSolutionWithCodeCoverage/TestProject/Program.cs @@ -0,0 +1,15 @@ +namespace TestProject +{ + internal class Program + { + public static async global::System.Threading.Tasks.Task Main(string[] args) + { + Microsoft.Testing.Platform.Builder.ITestApplicationBuilder builder = await global::Microsoft.Testing.Platform.Builder.TestApplication.CreateBuilderAsync(args); + builder.AddSelfRegisteredExtensions(args); + using (global::Microsoft.Testing.Platform.Builder.ITestApplication app = await builder.BuildAsync()) + { + return await app.RunAsync(); + } + } + } +} diff --git a/test/TestAssets/TestProjects/TestProjectSolutionWithCodeCoverage/TestProject/Test1.cs b/test/TestAssets/TestProjects/TestProjectSolutionWithCodeCoverage/TestProject/Test1.cs new file mode 100644 index 000000000000..8bbbb841f05f --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectSolutionWithCodeCoverage/TestProject/Test1.cs @@ -0,0 +1,20 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace TestProject +{ + [TestClass] + public sealed class Test1 + { + [TestMethod] + public void TestMethod1() + { + Assert.AreEqual(1, 1); + } + + [TestMethod] + public void TestMethod2() + { + Assert.AreEqual(1, 2); + } + } +} diff --git a/test/TestAssets/TestProjects/TestProjectSolutionWithCodeCoverage/TestProject/TestProject.csproj b/test/TestAssets/TestProjects/TestProjectSolutionWithCodeCoverage/TestProject/TestProject.csproj new file mode 100644 index 000000000000..4b6416d5323e --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectSolutionWithCodeCoverage/TestProject/TestProject.csproj @@ -0,0 +1,13 @@ + + + + + net8 + False + 17.12.6 + + + + + + \ No newline at end of file diff --git a/test/TestAssets/TestProjects/TestProjectSolutionWithCodeCoverage/TestProjectSolutionWithCodeCoverage.sln b/test/TestAssets/TestProjects/TestProjectSolutionWithCodeCoverage/TestProjectSolutionWithCodeCoverage.sln new file mode 100644 index 000000000000..13471530b1e4 --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectSolutionWithCodeCoverage/TestProjectSolutionWithCodeCoverage.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35415.258 main +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject", "TestProject\TestProject.csproj", "{44DB7F03-7556-4EBA-BF32-C538B3A05742}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {44DB7F03-7556-4EBA-BF32-C538B3A05742}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {44DB7F03-7556-4EBA-BF32-C538B3A05742}.Debug|Any CPU.Build.0 = Debug|Any CPU + {44DB7F03-7556-4EBA-BF32-C538B3A05742}.Release|Any CPU.ActiveCfg = Release|Any CPU + {44DB7F03-7556-4EBA-BF32-C538B3A05742}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/test/TestAssets/TestProjects/TestProjectSolutionWithTestsAndArtifacts/TestProject/Program.cs b/test/TestAssets/TestProjects/TestProjectSolutionWithTestsAndArtifacts/TestProject/Program.cs new file mode 100644 index 000000000000..db1b1ca08669 --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectSolutionWithTestsAndArtifacts/TestProject/Program.cs @@ -0,0 +1,103 @@ +//See https://aka.ms/new-console-template for more information + +//Opt -out telemetry + +using Microsoft.Testing.Extensions; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +Environment.SetEnvironmentVariable("DOTNET_CLI_TELEMETRY_OPTOUT", "1"); + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +// Enable Trx +testApplicationBuilder.AddTrxReportProvider(); +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => new[] { + typeof(TestNodeUpdateMessage), + typeof(SessionFileArtifact), + typeof(TestNodeFileArtifact), + typeof(FileArtifact), }; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test1", + DisplayName = "Test1", + Properties = new PropertyBag(new PassedTestNodeStateProperty("OK")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test1", + DisplayName = "Test1", + Properties = new PropertyBag(new SkippedTestNodeStateProperty("OK skipped!")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test2", + DisplayName = "Test2", + Properties = new PropertyBag(new FailedTestNodeStateProperty(new Exception("this is a failed test"), "not OK")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test3", + DisplayName = "Test3", + Properties = new PropertyBag(new TimeoutTestNodeStateProperty(new Exception("this is a timeout exception"), "not OK")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Tes43", + DisplayName = "Test4", + Properties = new PropertyBag(new ErrorTestNodeStateProperty(new Exception("this is an exception"), "not OK")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test5", + DisplayName = "Test5", + Properties = new PropertyBag(new CancelledTestNodeStateProperty(new Exception("this is a cancelled exception"), "not OK")), + })); + + await context.MessageBus.PublishAsync(this, new FileArtifact(new FileInfo("file.txt"), "file", "file description")); + + await context.MessageBus.PublishAsync(this, new SessionFileArtifact(context.Request.Session.SessionUid, new FileInfo("sessionFile.txt"), "sessionFile", "description")); + + await context.MessageBus.PublishAsync(this, new TestNodeFileArtifact(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test6 id", + DisplayName = "Test6", + Properties = new PropertyBag(new PassedTestNodeStateProperty("OK")), + }, new FileInfo("testNodeFile.txt"), "testNodeFile", "description")); + + context.Complete(); + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/TestProjectSolutionWithTestsAndArtifacts/TestProject/TestProject.csproj b/test/TestAssets/TestProjects/TestProjectSolutionWithTestsAndArtifacts/TestProject/TestProject.csproj new file mode 100644 index 000000000000..9e3c61d9c8be --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectSolutionWithTestsAndArtifacts/TestProject/TestProject.csproj @@ -0,0 +1,21 @@ + + + + + net8.0 + Exe + + enable + enable + true + + false + false + true + + + + + + + diff --git a/test/TestAssets/TestProjects/TestProjectSolutionWithTestsAndArtifacts/TestProjectSolutionWithTestsAndArtifacts.sln b/test/TestAssets/TestProjects/TestProjectSolutionWithTestsAndArtifacts/TestProjectSolutionWithTestsAndArtifacts.sln new file mode 100644 index 000000000000..f14170863d5a --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectSolutionWithTestsAndArtifacts/TestProjectSolutionWithTestsAndArtifacts.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35415.258 main +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject", "TestProject\TestProject.csproj", "{7DF6A00E-4EBA-4845-95FF-9C567E6687F6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7DF6A00E-4EBA-4845-95FF-9C567E6687F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7DF6A00E-4EBA-4845-95FF-9C567E6687F6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7DF6A00E-4EBA-4845-95FF-9C567E6687F6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7DF6A00E-4EBA-4845-95FF-9C567E6687F6}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/test/TestAssets/TestProjects/TestProjectWithClassLibrary/ClassLibrary/Class1.cs b/test/TestAssets/TestProjects/TestProjectWithClassLibrary/ClassLibrary/Class1.cs new file mode 100644 index 000000000000..534e4f348652 --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectWithClassLibrary/ClassLibrary/Class1.cs @@ -0,0 +1,6 @@ +namespace ClassLibrary +{ + public class Class1 + { + } +} diff --git a/test/TestAssets/TestProjects/TestProjectWithClassLibrary/ClassLibrary/ClassLibrary.csproj b/test/TestAssets/TestProjects/TestProjectWithClassLibrary/ClassLibrary/ClassLibrary.csproj new file mode 100644 index 000000000000..fa71b7ae6a34 --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectWithClassLibrary/ClassLibrary/ClassLibrary.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/test/TestAssets/TestProjects/TestProjectWithClassLibrary/TestProject/Program.cs b/test/TestAssets/TestProjects/TestProjectWithClassLibrary/TestProject/Program.cs new file mode 100644 index 000000000000..28a9001705af --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectWithClassLibrary/TestProject/Program.cs @@ -0,0 +1,46 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => new[] { + typeof(TestNodeUpdateMessage) + }; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test0", + DisplayName = "Test0", + Properties = new PropertyBag(new PassedTestNodeStateProperty("OK")), + })); + + context.Complete(); + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/TestProjectWithClassLibrary/TestProject/TestProject.csproj b/test/TestAssets/TestProjects/TestProjectWithClassLibrary/TestProject/TestProject.csproj new file mode 100644 index 000000000000..628b99807474 --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectWithClassLibrary/TestProject/TestProject.csproj @@ -0,0 +1,21 @@ + + + + + Exe + net8.0 + enable + enable + true + true + + + + + + + + + + + diff --git a/test/TestAssets/TestProjects/TestProjectWithClassLibrary/TestProjectWithClassLibrary.sln b/test/TestAssets/TestProjects/TestProjectWithClassLibrary/TestProjectWithClassLibrary.sln new file mode 100644 index 000000000000..c042974108d5 --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectWithClassLibrary/TestProjectWithClassLibrary.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.35712.36 main +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject", "TestProject\TestProject.csproj", "{4868FD55-C56D-445C-9F83-A063302EC78F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibrary", "ClassLibrary\ClassLibrary.csproj", "{729A12AB-4D79-4756-A776-C7BB63D30A4F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4868FD55-C56D-445C-9F83-A063302EC78F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4868FD55-C56D-445C-9F83-A063302EC78F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4868FD55-C56D-445C-9F83-A063302EC78F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4868FD55-C56D-445C-9F83-A063302EC78F}.Release|Any CPU.Build.0 = Release|Any CPU + {729A12AB-4D79-4756-A776-C7BB63D30A4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {729A12AB-4D79-4756-A776-C7BB63D30A4F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {729A12AB-4D79-4756-A776-C7BB63D30A4F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {729A12AB-4D79-4756-A776-C7BB63D30A4F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E2F590CA-47E0-4145-924B-3E398540BA45} + EndGlobalSection +EndGlobal diff --git a/test/TestAssets/TestProjects/TestProjectWithDiscoveredTests/Program.cs b/test/TestAssets/TestProjects/TestProjectWithDiscoveredTests/Program.cs new file mode 100644 index 000000000000..444f5a1db21b --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectWithDiscoveredTests/Program.cs @@ -0,0 +1,46 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => new[] { + typeof(TestNodeUpdateMessage) + }; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test0", + DisplayName = "Test0", + Properties = new PropertyBag(new DiscoveredTestNodeStateProperty()), + })); + + context.Complete(); + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/TestProjectWithDiscoveredTests/TestProject.csproj b/test/TestAssets/TestProjects/TestProjectWithDiscoveredTests/TestProject.csproj new file mode 100644 index 000000000000..19a9d61c98f1 --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectWithDiscoveredTests/TestProject.csproj @@ -0,0 +1,20 @@ + + + + + net8.0 + Exe + + enable + enable + true + + false + false + true + + + + + + diff --git a/test/TestAssets/TestProjects/TestProjectWithMultipleTFMsSolution/TestProject/Program.cs b/test/TestAssets/TestProjects/TestProjectWithMultipleTFMsSolution/TestProject/Program.cs new file mode 100644 index 000000000000..99cfc02234a4 --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectWithMultipleTFMsSolution/TestProject/Program.cs @@ -0,0 +1,90 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +namespace TestProjectWithNetFM +{ + internal class Program + { + public static async Task Main(string[] args) + { + // To attach to the children + ITestApplicationBuilder testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + + ITestApplication testApplication = await testApplicationBuilder.BuildAsync(); + return await testApplication.RunAsync(); + } + } + + public class DummyTestAdapter : ITestFramework, IDataProducer + { + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => new[] { + typeof(TestNodeUpdateMessage) + }; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test0", + DisplayName = "Test0", + Properties = new PropertyBag(new PassedTestNodeStateProperty("OK")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test1", + DisplayName = "Test1", + Properties = new PropertyBag(new SkippedTestNodeStateProperty("Skipped!")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test2", + DisplayName = "Test2", + Properties = new PropertyBag(new FailedTestNodeStateProperty(new Exception("this is a failed test"), "")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test3", + DisplayName = "Test3", + Properties = new PropertyBag(new TimeoutTestNodeStateProperty(new Exception("this is a timeout exception"), "")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test4", + DisplayName = "Test4", + Properties = new PropertyBag(new ErrorTestNodeStateProperty(new Exception("this is an exception"), "")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test5", + DisplayName = "Test5", + Properties = new PropertyBag(new CancelledTestNodeStateProperty(new Exception("this is a cancelled exception"), "")), + })); + + context.Complete(); + } + } +} diff --git a/test/TestAssets/TestProjects/TestProjectWithMultipleTFMsSolution/TestProject/TestProject.csproj b/test/TestAssets/TestProjects/TestProjectWithMultipleTFMsSolution/TestProject/TestProject.csproj new file mode 100644 index 000000000000..d7de2c97ff87 --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectWithMultipleTFMsSolution/TestProject/TestProject.csproj @@ -0,0 +1,20 @@ + + + + + Exe + net8.0;net9.0 + enable + enable + false + true + latest + false + true + + + + + + + \ No newline at end of file diff --git a/test/TestAssets/TestProjects/TestProjectWithMultipleTFMsSolution/TestProjectWithMultipleTFMsSolution.sln b/test/TestAssets/TestProjects/TestProjectWithMultipleTFMsSolution/TestProjectWithMultipleTFMsSolution.sln new file mode 100644 index 000000000000..62931ef6461b --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectWithMultipleTFMsSolution/TestProjectWithMultipleTFMsSolution.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35505.181 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject", "TestProject\TestProject.csproj", "{D2E321F2-3513-99DE-C37E-6D48D15F404D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D2E321F2-3513-99DE-C37E-6D48D15F404D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D2E321F2-3513-99DE-C37E-6D48D15F404D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D2E321F2-3513-99DE-C37E-6D48D15F404D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D2E321F2-3513-99DE-C37E-6D48D15F404D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3D7914D1-7D03-4FFB-8D0F-7FFA6B6BA6A0} + EndGlobalSection +EndGlobal diff --git a/test/TestAssets/TestProjects/TestProjectWithTests/Program.cs b/test/TestAssets/TestProjects/TestProjectWithTests/Program.cs new file mode 100644 index 000000000000..e7d85d197aea --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectWithTests/Program.cs @@ -0,0 +1,53 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => new[] { + typeof(TestNodeUpdateMessage) + }; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test0", + DisplayName = "Test0", + Properties = new PropertyBag(new PassedTestNodeStateProperty("OK")), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test1", + DisplayName = "Test1", + Properties = new PropertyBag(new SkippedTestNodeStateProperty("OK skipped!")), + })); + + context.Complete(); + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/TestProjectWithTests/TestProject.csproj b/test/TestAssets/TestProjects/TestProjectWithTests/TestProject.csproj new file mode 100644 index 000000000000..19a9d61c98f1 --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectWithTests/TestProject.csproj @@ -0,0 +1,20 @@ + + + + + net8.0 + Exe + + enable + enable + true + + false + false + true + + + + + + diff --git a/test/dotnet-test.Tests/GivenDotnetTestBuildsAndDiscoversTests.cs b/test/dotnet-test.Tests/GivenDotnetTestBuildsAndDiscoversTests.cs new file mode 100644 index 000000000000..140a734b9e3a --- /dev/null +++ b/test/dotnet-test.Tests/GivenDotnetTestBuildsAndDiscoversTests.cs @@ -0,0 +1,152 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using dotnet.Tests; +using Microsoft.DotNet.Tools.Common; +using CommandResult = Microsoft.DotNet.Cli.Utils.CommandResult; + +namespace Microsoft.DotNet.Cli.Test.Tests +{ + public class GivenDotnetTestBuildsAndDiscoversTests : SdkTest + { + public GivenDotnetTestBuildsAndDiscoversTests(ITestOutputHelper log) : base(log) + { + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void DiscoverTestProjectWithNoTests_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("TestProjectSolution", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ListTestsOption.Name, TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + Assert.Matches($@"Discovered 0 tests.*{PathUtility.GetDirectorySeparatorChar()}TestProject.dll\s\(net8.0\|[a-zA-Z][0-9]+\)", result.StdOut); + + result.StdOut + .Should().Contain("Discovered 0 tests."); + } + + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void DiscoverMultipleTestProjectsWithNoTests_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("MultipleTestProjectSolution", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ListTestsOption.Name, TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + Assert.Matches($@"Discovered 0 tests.*{PathUtility.GetDirectorySeparatorChar()}TestProject.dll\s\(net8.0\|[a-zA-Z][0-9]+\)", result.StdOut); + Assert.Matches($@"Discovered 0 tests.*{PathUtility.GetDirectorySeparatorChar()}OtherTestProject.dll\s\(net8.0\|[a-zA-Z][0-9]+\)", result.StdOut); + Assert.Matches($@"Discovered 0 tests.*{PathUtility.GetDirectorySeparatorChar()}AnotherTestProject.dll\s\(net8.0\|[a-zA-Z][0-9]+\)", result.StdOut); + Assert.Matches(@"Discovered 0 tests.*", result.StdOut); + } + + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void DiscoverTestProjectWithTests_ShouldReturnZeroAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("TestProjectWithDiscoveredTests", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ListTestsOption.Name, TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + Assert.Matches($@"Discovered 1 tests.*{PathUtility.GetDirectorySeparatorChar()}TestProject.dll\s\(net8.0\|[a-zA-Z][0-9]+\)\s+Test0", result.StdOut); + Assert.Matches(@"Discovered 1 tests.*", result.StdOut); + } + + result.ExitCode.Should().Be(ExitCodes.Success); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void DiscoverMultipleTestProjectsWithTests_ShouldReturnZeroAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("MultiTestProjectSolutionWithDiscoveredTests", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ListTestsOption.Name, TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + Assert.Matches($@"Discovered 2 tests.*{PathUtility.GetDirectorySeparatorChar()}TestProject.dll\s\(net8.0\|[a-zA-Z][0-9]+\)\s+Test0\s+Test2", result.StdOut); + Assert.Matches($@"Discovered 1 tests.*{PathUtility.GetDirectorySeparatorChar()}OtherTestProject.dll\s\(net8.0\|[a-zA-Z][0-9]+\)\s+Test1", result.StdOut); + Assert.Matches(@"Discovered 3 tests.*", result.StdOut); + } + + result.ExitCode.Should().Be(ExitCodes.Success); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void DiscoverProjectWithMSTestMetaPackageAndMultipleTFMsWithTests_ShouldReturnZeroAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("MSTestMetaPackageProjectWithMultipleTFMsSolution", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ListTestsOption.Name, TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + Assert.Matches($@"Discovered 3 tests.*{PathUtility.GetDirectorySeparatorChar()}TestProject.dll\s\(net8.0\|[a-zA-Z][0-9]+\)\s+TestMethod1\s+TestMethod2\s+TestMethod3", result.StdOut); + Assert.Matches($@"Discovered 2 tests.*{PathUtility.GetDirectorySeparatorChar()}TestProject.dll\s\(net9.0\|[a-zA-Z][0-9]+\)\s+TestMethod1\s+TestMethod3", result.StdOut); + } + + result.ExitCode.Should().Be(ExitCodes.Success); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void DiscoverTestProjectsWithHybridModeTestRunners_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("HybridTestRunnerTestProjects", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ListTestsOption.Name, TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + result.StdOut.Should().Contain("Test application(s) that support VSTest are not supported."); + } + + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + } +} diff --git a/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsHelp.cs b/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsHelp.cs new file mode 100644 index 000000000000..774632f30ec3 --- /dev/null +++ b/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsHelp.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using dotnet.Tests; +using CommandResult = Microsoft.DotNet.Cli.Utils.CommandResult; + +namespace Microsoft.DotNet.Cli.Test.Tests +{ + public class GivenDotnetTestBuildsAndRunsHelp : SdkTest + { + public GivenDotnetTestBuildsAndRunsHelp(ITestOutputHelper log) : base(log) + { + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunHelpOnTestProject_ShouldReturnZeroAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("TestProjectSolutionWithTestsAndArtifacts", Guid.NewGuid().ToString()).WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(CliConstants.HelpOptionKey, TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + Assert.Matches(@"Extension options:\s+--[\s\S]*", result.StdOut); + Assert.Matches(@"Options:\s+--[\s\S]*", result.StdOut); + } + + result.ExitCode.Should().Be(ExitCodes.Success); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunHelpOnMultipleTestProjects_ShouldReturnZeroAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("ProjectSolutionForMultipleTFMs", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(CliConstants.HelpOptionKey, TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + Assert.Matches(@"Extension options:\s+--[\s\S]*", result.StdOut); + Assert.Matches(@"Options:\s+--[\s\S]*", result.StdOut); + + string net9ProjectDllRegex = @"\s+.*\\net9\.0\\TestProjectWithNet9\.dll.*\s+--report-trx\s+--report-trx-filename"; + string net48ProjectExeRegex = @"\s+.*\\net4\.8\\TestProjectWithNetFramework\.exe.*\s+--report-trx\s+--report-trx-filename"; + + Assert.Matches(@$"Unavailable extension options:(?:({net9ProjectDllRegex})|({net48ProjectExeRegex}))(?:({net48ProjectExeRegex})|({net9ProjectDllRegex}))", result.StdOut); + } + + result.ExitCode.Should().Be(ExitCodes.Success); + } + } +} diff --git a/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTestBasedOnGlobbingFilter.cs b/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTestBasedOnGlobbingFilter.cs new file mode 100644 index 000000000000..337b423afc3a --- /dev/null +++ b/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTestBasedOnGlobbingFilter.cs @@ -0,0 +1,92 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text.RegularExpressions; +using dotnet.Tests; +using CommandResult = Microsoft.DotNet.Cli.Utils.CommandResult; + +namespace Microsoft.DotNet.Cli.Test.Tests +{ + public class GivenDotnetTestBuildsAndRunsTestBasedOnGlobbingFilter : SdkTest + { + private const string TestApplicationArgsPattern = @".*(Test application arguments).*"; + + public GivenDotnetTestBuildsAndRunsTestBasedOnGlobbingFilter(ITestOutputHelper log) : base(log) + { + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunTestProjectWithFilterOfDll_ShouldReturnZeroAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("TestProjectWithTests", Guid.NewGuid().ToString()) + .WithSource(); + + new BuildCommand(testInstance) + .Execute() + .Should().Pass(); + + var binDirectory = new FileInfo($"{testInstance.Path}/bin").Directory; + var binDirectoryLastWriteTime = binDirectory?.LastWriteTime; + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.TestModulesFilterOption.Name, "**/bin/**/Debug/net8.0/TestProject.dll", + TestingPlatformOptions.ConfigurationOption.Name, configuration); + + // Assert that the bin folder hasn't been modified + Assert.Equal(binDirectoryLastWriteTime, binDirectory?.LastWriteTime); + + if (!TestContext.IsLocalized()) + { + result.StdOut + .Should().Contain("Test run summary: Passed!") + .And.Contain("total: 2") + .And.Contain("succeeded: 1") + .And.Contain("failed: 0") + .And.Contain("skipped: 1"); + } + + result.ExitCode.Should().Be(ExitCodes.Success); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunTestProjectWithFilterOfDllWithRootDirectory_ShouldReturnZeroAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("TestProjectWithTests", Guid.NewGuid().ToString()) + .WithSource(); + + new BuildCommand(testInstance) + .Execute() + .Should().Pass(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .WithTraceOutput() + .Execute(TestingPlatformOptions.TestModulesFilterOption.Name, "**/bin/**/Debug/net8.0/TestProject.dll", + TestingPlatformOptions.TestModulesRootDirectoryOption.Name, testInstance.TestRoot, + TestingPlatformOptions.ConfigurationOption.Name, configuration); + + + var testAppArgs = Regex.Matches(result.StdOut!, TestApplicationArgsPattern); + Assert.Contains($"exec {testInstance.TestRoot}\\bin\\Debug\\net8.0\\TestProject.dll", testAppArgs.FirstOrDefault()?.Value); + + if (!TestContext.IsLocalized()) + { + result.StdOut + .Should().Contain("Test run summary: Passed!") + .And.Contain("total: 2") + .And.Contain("succeeded: 1") + .And.Contain("failed: 0") + .And.Contain("skipped: 1"); + } + + result.ExitCode.Should().Be(ExitCodes.Success); + } + } +} diff --git a/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTests.cs b/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTests.cs new file mode 100644 index 000000000000..275b4d4136f2 --- /dev/null +++ b/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTests.cs @@ -0,0 +1,262 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using dotnet.Tests; +using Microsoft.DotNet.Tools.Common; +using CommandResult = Microsoft.DotNet.Cli.Utils.CommandResult; + +namespace Microsoft.DotNet.Cli.Test.Tests +{ + public class GivenDotnetTestBuildsAndRunsTests : SdkTest + { + public GivenDotnetTestBuildsAndRunsTests(ITestOutputHelper log) : base(log) + { + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunTestProjectWithNoTests_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("TestProjectSolution", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + result.StdOut + .Should().Contain("Test run summary: Zero tests ran") + .And.Contain("total: 0") + .And.Contain("succeeded: 0") + .And.Contain("failed: 0") + .And.Contain("skipped: 0"); + } + + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunMultipleTestProjectsWithNoTests_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("MultipleTestProjectSolution", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + result.StdOut + .Should().Contain("Test run summary: Zero tests ran") + .And.Contain("total: 0") + .And.Contain("succeeded: 0") + .And.Contain("failed: 0") + .And.Contain("skipped: 0"); + } + + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunTestProjectWithTests_ShouldReturnZeroAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("TestProjectWithTests", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + result.StdOut + .Should().Contain("Test run summary: Passed!") + .And.Contain("skipped Test1") + .And.Contain("total: 2") + .And.Contain("succeeded: 1") + .And.Contain("failed: 0") + .And.Contain("skipped: 1"); + } + + result.ExitCode.Should().Be(ExitCodes.Success); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunMultipleTestProjectsWithFailingTests_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("MultiTestProjectSolutionWithTests", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + result.StdOut + .Should().Contain("Test run summary: Failed!") + .And.Contain("total: 5") + .And.Contain("succeeded: 2") + .And.Contain("failed: 1") + .And.Contain("skipped: 2"); + } + + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunMultipleTestProjectsWithDifferentFailures_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("MultiTestProjectSolutionWithDifferentFailures", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute("--minimum-expected-tests 2", + TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + Assert.Matches($@".+{PathUtility.GetDirectorySeparatorChar()}net8\.0{PathUtility.GetDirectorySeparatorChar()}TestProject\.dll\s+\(net8.0\|[a-zA-Z][1-9]+\)\sfailed.*\s+Exit code: 8", result.StdOut); + Assert.Matches($@".+{PathUtility.GetDirectorySeparatorChar()}net8\.0{PathUtility.GetDirectorySeparatorChar()}OtherTestProject\.dll\s+\(net8.0\|[a-zA-Z][1-9]+\)\sfailed.*\s+Exit code: 2", result.StdOut); + Assert.Matches($@".+{PathUtility.GetDirectorySeparatorChar()}net8\.0{PathUtility.GetDirectorySeparatorChar()}AnotherTestProject\.dll\s+\(net8.0\|[a-zA-Z][1-9]+\)\sfailed.*\s+Exit code: 9", result.StdOut); + + result.StdOut + .Should().Contain("Test run summary: Failed!") + .And.Contain("total: 4") + .And.Contain("succeeded: 2") + .And.Contain("failed: 1") + .And.Contain("skipped: 1"); + } + + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunTestProjectsWithHybridModeTestRunners_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("HybridTestRunnerTestProjects", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + result.StdOut.Should().Contain("Test application(s) that support VSTest are not supported."); + } + + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunOnEmptyFolder_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("EmptyFolder", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + result.StdOut.Should().Contain("Specify a project or solution file. The current working directory does not contain a project or solution file."); + } + + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunOnMultipleProjectFoldersWithoutSolutionFile_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("MultipleTestProjectsWithoutSolution", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + result.StdOut.Should().Contain("Specify a project or solution file. The current working directory does not contain a project or solution file."); + } + + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunOnProjectWithSolutionFile_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("TestProjectFileAndSolutionFile", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + result.StdOut.Should().Contain("Specify which project or solution file to use because this folder contains more than one project or solution file."); + } + + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunOnProjectWithClassLibrary_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("TestProjectWithClassLibrary", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + result.StdOut + .Should().Contain("Test run summary: Passed!") + .And.Contain("total: 1") + .And.Contain("succeeded: 1") + .And.Contain("failed: 0") + .And.Contain("skipped: 0"); + } + + result.ExitCode.Should().Be(ExitCodes.Success); + } + } +} diff --git a/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTestsForMultipleTFMs.cs b/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTestsForMultipleTFMs.cs new file mode 100644 index 000000000000..d624e1a09a82 --- /dev/null +++ b/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTestsForMultipleTFMs.cs @@ -0,0 +1,136 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text.RegularExpressions; +using dotnet.Tests; +using Microsoft.DotNet.Tools.Common; +using CommandResult = Microsoft.DotNet.Cli.Utils.CommandResult; + +namespace Microsoft.DotNet.Cli.Test.Tests +{ + public class GivenDotnetTestBuildsAndRunsTestsForMultipleTFMs : SdkTest + { + public GivenDotnetTestBuildsAndRunsTestsForMultipleTFMs(ITestOutputHelper log) : base(log) + { + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunMultipleProjectWithDifferentTFMsWithFailingTests_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("ProjectSolutionForMultipleTFMs", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + MatchCollection net8ProjectMatches = Regex.Matches(result.StdOut!, $@".+{PathUtility.GetDirectorySeparatorChar()}net8\.0{PathUtility.GetDirectorySeparatorChar()}TestProjectWithNet8\.dll\s+\(net8.0\|[a-zA-Z][1-9]+\)\sfailed"); + MatchCollection net9ProjectMatches = Regex.Matches(result.StdOut!, $@".+{PathUtility.GetDirectorySeparatorChar()}net9\.0{PathUtility.GetDirectorySeparatorChar()}TestProjectWithNet9\.dll\s+\(net9.0\|[a-zA-Z][1-9]+\)\spassed"); + + MatchCollection skippedTestsMatches = Regex.Matches(result.StdOut!, "skipped Test2"); + MatchCollection failedTestsMatches = Regex.Matches(result.StdOut!, "failed Test3"); + + Assert.True(net8ProjectMatches.Count > 1); + Assert.True(net9ProjectMatches.Count > 1); + + Assert.Single(failedTestsMatches); + Assert.Multiple(() => Assert.Equal(2, skippedTestsMatches.Count)); + + result.StdOut + .Should().Contain("Test run summary: Failed!") + .And.Contain("total: 5") + .And.Contain("succeeded: 2") + .And.Contain("failed: 1") + .And.Contain("skipped: 2"); + } + + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunProjectWithMultipleTFMsWithFailingTests_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("TestProjectWithMultipleTFMsSolution", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + var net8ProjectMatches = Regex.Matches(result.StdOut!, $@".+{PathUtility.GetDirectorySeparatorChar()}net8\.0{PathUtility.GetDirectorySeparatorChar()}TestProject\.dll\s+\(net8.0\|[a-zA-Z][1-9]+\)\sfailed"); + var net9ProjectMatches = Regex.Matches(result.StdOut!, $@".+{PathUtility.GetDirectorySeparatorChar()}net9\.0{PathUtility.GetDirectorySeparatorChar()}TestProject\.dll\s+\(net9.0\|[a-zA-Z][1-9]+\)\sfailed"); + + MatchCollection skippedTestsMatches = Regex.Matches(result.StdOut!, "skipped Test1"); + MatchCollection failedTestsMatches = Regex.Matches(result.StdOut!, "failed Test2"); + MatchCollection timeoutTestsMatches = Regex.Matches(result.StdOut!, @"failed \(canceled\) Test3"); + MatchCollection errorTestsMatches = Regex.Matches(result.StdOut!, "failed Test4"); + MatchCollection canceledTestsMatches = Regex.Matches(result.StdOut!, @"failed \(canceled\) Test5"); + + Assert.True(net8ProjectMatches.Count > 1); + Assert.True(net9ProjectMatches.Count > 1); + + Assert.Multiple(() => Assert.Equal(2, skippedTestsMatches.Count)); + Assert.Multiple(() => Assert.Equal(2, failedTestsMatches.Count)); + Assert.Multiple(() => Assert.Equal(2, timeoutTestsMatches.Count)); + Assert.Multiple(() => Assert.Equal(2, errorTestsMatches.Count)); + Assert.Multiple(() => Assert.Equal(2, skippedTestsMatches.Count)); + + result.StdOut + .Should().Contain("Test run summary: Failed!") + .And.Contain("total: 12") + .And.Contain("succeeded: 2") + .And.Contain("failed: 8") + .And.Contain("skipped: 2"); + } + + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunProjectWithMSTestMetaPackageAndMultipleTFMsWithFailingTests_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("MSTestMetaPackageProjectWithMultipleTFMsSolution", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + MatchCollection net8ProjectMatches = Regex.Matches(result.StdOut!, $@".+{PathUtility.GetDirectorySeparatorChar()}net8\.0{PathUtility.GetDirectorySeparatorChar()}TestProject\.dll\s+\(net8.0\|[a-zA-Z][1-9]+\)\sfailed"); + MatchCollection net9ProjectMatches = Regex.Matches(result.StdOut!, $@".+{PathUtility.GetDirectorySeparatorChar()}net9\.0{PathUtility.GetDirectorySeparatorChar()}TestProject\.dll\s+\(net9.0\|[a-zA-Z][1-9]+\)\sfailed"); + + MatchCollection failedTestsMatches = Regex.Matches(result.StdOut!, "failed TestMethod3"); + + Assert.True(net8ProjectMatches.Count > 1); + Assert.True(net9ProjectMatches.Count > 1); + + Assert.Multiple(() => Assert.Equal(2, failedTestsMatches.Count)); + + result.StdOut + .Should().Contain("Test run summary: Failed!") + .And.Contain("total: 5") + .And.Contain("succeeded: 3") + .And.Contain("failed: 2") + .And.Contain("skipped: 0"); + } + + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + } +} diff --git a/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTestsWithArtifacts.cs b/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTestsWithArtifacts.cs new file mode 100644 index 000000000000..d1818318bd22 --- /dev/null +++ b/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTestsWithArtifacts.cs @@ -0,0 +1,78 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using dotnet.Tests; +using Microsoft.DotNet.Tools.Common; +using CommandResult = Microsoft.DotNet.Cli.Utils.CommandResult; + +namespace Microsoft.DotNet.Cli.Test.Tests +{ + public class GivenDotnetTestBuildsAndRunsTestsWithArtifacts : SdkTest + { + public GivenDotnetTestBuildsAndRunsTestsWithArtifacts(ITestOutputHelper log) : base(log) + { + } + + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunTestProjectWithFailingTestsAndFileArtifacts_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("TestProjectSolutionWithTestsAndArtifacts", Guid.NewGuid().ToString()).WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + Assert.Matches(@".*Test6.*testNodeFile.txt", result.StdOut); + + result.StdOut + .Should().Contain("In process file artifacts") + .And.Contain("file.txt") + .And.Contain("sessionFile.txt"); + + result.StdOut + .Should().Contain("Test run summary: Failed!") + .And.Contain("total: 6") + .And.Contain("succeeded: 1") + .And.Contain("failed: 4") + .And.Contain("skipped: 1"); + } + + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunTestProjectWithCodeCoverage_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("TestProjectSolutionWithCodeCoverage", Guid.NewGuid().ToString()).WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute("--coverage", TestingPlatformOptions.ConfigurationOption.Name, configuration); + + if (!TestContext.IsLocalized()) + { + string pattern = $@"In\sprocess\sfile\sartifacts\sproduced:\s+.*{PathUtility.GetDirectorySeparatorChar()}TestResults{PathUtility.GetDirectorySeparatorChar()}.*\.coverage"; + + Assert.Matches(pattern, result.StdOut); + + result.StdOut + .Should().Contain("Test run summary: Failed!") + .And.Contain("total: 2") + .And.Contain("succeeded: 1") + .And.Contain("failed: 1") + .And.Contain("skipped: 0"); + } + + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + } +} diff --git a/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTestsWithDifferentOptions.cs b/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTestsWithDifferentOptions.cs new file mode 100644 index 000000000000..48ad4172ae00 --- /dev/null +++ b/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTestsWithDifferentOptions.cs @@ -0,0 +1,298 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text.RegularExpressions; +using dotnet.Tests; +using CommandResult = Microsoft.DotNet.Cli.Utils.CommandResult; + +namespace Microsoft.DotNet.Cli.Test.Tests +{ + public class GivenDotnetTestBuildsAndRunsTestsWithDifferentOptions : SdkTest + { + private const string TestApplicationArgsSeparator = $" {CliConstants.ParametersSeparator} "; + private const string TestApplicationArgsPattern = @".*(Test application arguments).*"; + + public GivenDotnetTestBuildsAndRunsTestsWithDifferentOptions(ITestOutputHelper log) : base(log) + { + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunWithProjectPathWithFailingTests_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("MultiTestProjectSolutionWithTests", Guid.NewGuid().ToString()) + .WithSource(); + + string testProjectPath = $"TestProject{Path.DirectorySeparatorChar}TestProject.csproj"; + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .WithTraceOutput() + .Execute(TestingPlatformOptions.ProjectOption.Name, testProjectPath, + TestingPlatformOptions.ConfigurationOption.Name, configuration); + + var testAppArgs = Regex.Matches(result.StdOut!, TestApplicationArgsPattern); + string fullProjectPath = $"{testInstance.TestRoot}{Path.DirectorySeparatorChar}{testProjectPath}"; + Assert.Contains($"{TestingPlatformOptions.ProjectOption.Name} \"{fullProjectPath}\"", testAppArgs.FirstOrDefault()?.Value.Split(TestApplicationArgsSeparator)[0]); + + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunWithSolutionPathWithFailingTests_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("MultiTestProjectSolutionWithTests", Guid.NewGuid().ToString()) + .WithSource(); + + string testSolutionPath = "MultiTestProjectSolutionWithTests.sln"; + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .WithTraceOutput() + .Execute(TestingPlatformOptions.SolutionOption.Name, testSolutionPath, + TestingPlatformOptions.ConfigurationOption.Name, configuration); + + + var testAppArgs = Regex.Matches(result.StdOut!, TestApplicationArgsPattern) + .Select(match => match.Value.Split(TestApplicationArgsSeparator)[0]) + .ToList(); + + string expectedProjectPath = $"{testInstance.TestRoot}{Path.DirectorySeparatorChar}TestProject{Path.DirectorySeparatorChar}TestProject.csproj"; + string otherExpectedProjectPath = $"{testInstance.TestRoot}{Path.DirectorySeparatorChar}OtherTestProject{Path.DirectorySeparatorChar}OtherTestProject.csproj"; + + bool containsExpectedPath = testAppArgs.Any(arg => arg.Contains(expectedProjectPath) || arg.Contains(otherExpectedProjectPath)); + + Assert.True(containsExpectedPath, + $"Expected either '{expectedProjectPath}' or '{otherExpectedProjectPath}' to be present in the test application arguments."); + + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunWithInvalidProjectExtension_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("MultiTestProjectSolutionWithTests", Guid.NewGuid().ToString()) + .WithSource(); + + string invalidProjectPath = $"TestProject{Path.DirectorySeparatorChar}TestProject.txt"; + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ProjectOption.Name, invalidProjectPath, + TestingPlatformOptions.ConfigurationOption.Name, configuration); + + result.StdOut.Should().Contain($"The provided project file has an invalid extension: {invalidProjectPath}."); + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunWithInvalidSolutionExtension_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("MultiTestProjectSolutionWithTests", Guid.NewGuid().ToString()) + .WithSource(); + + string invalidSolutionPath = "TestProjects.txt"; + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.SolutionOption.Name, invalidSolutionPath, + TestingPlatformOptions.ConfigurationOption.Name, configuration); + + result.StdOut.Should().Contain($"The provided solution file has an invalid extension: {invalidSolutionPath}."); + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunWithBothProjectAndSolutionAndDirectoryOptions_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("MultiTestProjectSolutionWithTests", Guid.NewGuid().ToString()) + .WithSource(); + + string testProjectPath = $"TestProject{Path.DirectorySeparatorChar}TestProject.csproj"; + string testSolutionPath = "MultiTestProjectSolutionWithTests.sln"; + string testDirectoryPath = "TestProjects"; + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ProjectOption.Name, testProjectPath, + TestingPlatformOptions.SolutionOption.Name, testSolutionPath, + TestingPlatformOptions.DirectoryOption.Name, testDirectoryPath, + TestingPlatformOptions.ConfigurationOption.Name, configuration); + + result.StdOut?.Contains("Specify either the project, solution or directory option."); + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunWithBothProjectAndSolutionOptions_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("MultiTestProjectSolutionWithTests", Guid.NewGuid().ToString()) + .WithSource(); + + string testProjectPath = $"TestProject{Path.DirectorySeparatorChar}TestProject.csproj"; + string testSolutionPath = "MultiTestProjectSolutionWithTests.sln"; + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ProjectOption.Name, testProjectPath, + TestingPlatformOptions.SolutionOption.Name, testSolutionPath, + TestingPlatformOptions.ConfigurationOption.Name, configuration); + + result.StdOut?.Contains("Specify either the project or solution option."); + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunWithNonExistentProjectPath_ShouldReturnZeroAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("MultiTestProjectSolutionWithTests", Guid.NewGuid().ToString()) + .WithSource(); + + string testProjectPath = $"TestProject{Path.DirectorySeparatorChar}OtherTestProject.csproj"; + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.ProjectOption.Name, testProjectPath, + TestingPlatformOptions.ConfigurationOption.Name, configuration); + + string fullProjectPath = $"{testInstance.TestRoot}{Path.DirectorySeparatorChar}{testProjectPath}"; + result.StdOut?.Contains($"The provided file path does not exist: {fullProjectPath}."); + + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunWithNonExistentSolutionPath_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("MultiTestProjectSolutionWithTests", Guid.NewGuid().ToString()) + .WithSource(); + + string solutionPath = "Solution.sln"; + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.SolutionOption.Name, solutionPath, + TestingPlatformOptions.ConfigurationOption.Name, configuration); + + string fullSolutionPath = $"{testInstance.TestRoot}{Path.DirectorySeparatorChar}{solutionPath}"; + result.StdOut.Should().Contain($"The provided file path does not exist: {fullSolutionPath}."); + + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunWithNonExistentDirectoryPath_ShouldReturnOneAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("MultiTestProjectSolutionWithTests", Guid.NewGuid().ToString()) + .WithSource(); + + string directoryPath = "Directory"; + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .Execute(TestingPlatformOptions.DirectoryOption.Name, directoryPath, + TestingPlatformOptions.ConfigurationOption.Name, configuration); + + result.StdOut.Should().Contain($"The provided directory path does not exist: {directoryPath}."); + result.ExitCode.Should().Be(ExitCodes.GenericFailure); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunTestProjectSolutionWithConfigurationOption_ShouldReturnZeroAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("TestProjectWithTests", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .WithTraceOutput() + .Execute(TestingPlatformOptions.ConfigurationOption.Name, configuration); + + var testAppArgs = Regex.Matches(result.StdOut!, TestApplicationArgsPattern); + Assert.Contains($"{TestingPlatformOptions.ConfigurationOption.Name} {configuration}", testAppArgs.FirstOrDefault()?.Value.Split(TestApplicationArgsSeparator)[0]); + + result.ExitCode.Should().Be(ExitCodes.Success); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunTestProjectSolutionWithArchOption_ShouldReturnZeroAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("TestProjectWithTests", Guid.NewGuid().ToString()) + .WithSource(); + string arch = "x64"; + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .WithTraceOutput() + .Execute(TestingPlatformOptions.ArchitectureOption.Name, arch, + TestingPlatformOptions.ConfigurationOption.Name, configuration); + + var testAppArgs = Regex.Matches(result.StdOut!, TestApplicationArgsPattern); + Assert.Contains($"{TestingPlatformOptions.ArchitectureOption.Name} {arch}", testAppArgs.FirstOrDefault()?.Value.Split(TestApplicationArgsSeparator)[0]); + + result.ExitCode.Should().Be(ExitCodes.Success); + } + + [InlineData(Constants.Debug)] + [InlineData(Constants.Release)] + [Theory] + public void RunSpecificCSProjRunsWithNoBuildAndNoRestoreOptions_ShouldReturnZeroAsExitCode(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("TestProjectWithTests", Guid.NewGuid().ToString()).WithSource(); + + new BuildCommand(testInstance) + .Execute($"/p:Configuration={configuration}") + .Should().Pass(); + + var binDirectory = new FileInfo($"{testInstance.Path}{Path.DirectorySeparatorChar}bin").Directory; + var binDirectoryLastWriteTime = binDirectory?.LastWriteTime; + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .WithTraceOutput() + .Execute(TestingPlatformOptions.ProjectOption.Name, @"TestProject.csproj", + TestingPlatformOptions.ConfigurationOption.Name, configuration); + + // Assert that the bin folder hasn't been modified + Assert.Equal(binDirectoryLastWriteTime, binDirectory?.LastWriteTime); + + var testAppArgs = Regex.Matches(result.StdOut!, TestApplicationArgsPattern); + Assert.Contains($"{TestingPlatformOptions.NoRestoreOption.Name} {TestingPlatformOptions.NoBuildOption.Name}", testAppArgs.FirstOrDefault()?.Value.Split(TestApplicationArgsSeparator)[0]); + + result.ExitCode.Should().Be(ExitCodes.Success); + } + } +} diff --git a/test/dotnet-test.Tests/GivenDotnetTestsRunsWithDifferentCultures.cs b/test/dotnet-test.Tests/GivenDotnetTestsRunsWithDifferentCultures.cs new file mode 100644 index 000000000000..d12ebf18edf1 --- /dev/null +++ b/test/dotnet-test.Tests/GivenDotnetTestsRunsWithDifferentCultures.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using CommandResult = Microsoft.DotNet.Cli.Utils.CommandResult; + +namespace Microsoft.DotNet.Cli.Test.Tests; + +public class GivenDotnetTestsRunsWithDifferentCultures : SdkTest +{ + public GivenDotnetTestsRunsWithDifferentCultures(ITestOutputHelper log) : base(log) + { + } + + [InlineData("en-US")] + [InlineData("de-DE")] + [Theory] + public void CanRunTestsAgainstProjectInLocale(string locale) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("TestProjectWithTests", Guid.NewGuid().ToString()).WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .WithEnableTestingPlatform() + .WithCulture(locale) + .Execute(); + + result.ExitCode.Should().Be(0); + } +} diff --git a/test/dotnet-test.Tests/MSBuildHandlerTests.cs b/test/dotnet-test.Tests/MSBuildHandlerTests.cs new file mode 100644 index 000000000000..c13144c83357 --- /dev/null +++ b/test/dotnet-test.Tests/MSBuildHandlerTests.cs @@ -0,0 +1,111 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.DotNet.Cli.Test.Tests +{ + public class MSBuildHandlerTests + { + [Fact] + public void IsBinaryLoggerEnabled_ShouldReturnTrue_WhenBinaryLoggerArgumentIsPresent() + { + // Arrange + var args = new List { "--binaryLogger" }; + + // Act + var result = MSBuildHandler.IsBinaryLoggerEnabled(args, out string binLogFileName); + + // Assert + Assert.True(result); + Assert.Equal(CliConstants.BinLogFileName, binLogFileName); + } + + [Fact] + public void IsBinaryLoggerEnabled_ShouldReturnTrue_WhenBinaryLoggerArgumentWithFileNameIsPresent() + { + // Arrange + var args = new List { "--binaryLogger:custom.binlog" }; + + // Act + var result = MSBuildHandler.IsBinaryLoggerEnabled(args, out string binLogFileName); + + // Assert + Assert.True(result); + Assert.Equal("custom.binlog", binLogFileName); + } + + [Fact] + public void IsBinaryLoggerEnabled_ShouldReturnFalse_WhenBinaryLoggerArgumentIsNotPresent() + { + // Arrange + var args = new List { "--someOtherArg" }; + + // Act + var result = MSBuildHandler.IsBinaryLoggerEnabled(args, out string binLogFileName); + + // Assert + Assert.False(result); + Assert.Equal(string.Empty, binLogFileName); + } + + [Fact] + public void IsBinaryLoggerEnabled_ShouldRemoveBinaryLoggerArgumentsFromArgs() + { + // Arrange + var args = new List { "--binaryLogger", "--someOtherArg" }; + + // Act + var result = MSBuildHandler.IsBinaryLoggerEnabled(args, out string binLogFileName); + + // Assert + Assert.True(result); + Assert.Equal(CliConstants.BinLogFileName, binLogFileName); + Assert.DoesNotContain("--binaryLogger", args); + } + + [Fact] + public void IsBinaryLoggerEnabled_ShouldHandleMultipleBinaryLoggerArguments() + { + // Arrange + var args = new List { "--binaryLogger", "--binaryLogger:custom1.binlog", "--binaryLogger:custom2.binlog" }; + + // Act + var result = MSBuildHandler.IsBinaryLoggerEnabled(args, out string binLogFileName); + + // Assert + Assert.True(result); + Assert.Equal("custom2.binlog", binLogFileName); + Assert.DoesNotContain("--binaryLogger", args); + Assert.DoesNotContain("--binaryLogger:custom1.binlog", args); + Assert.DoesNotContain("--binaryLogger:custom2.binlog", args); + } + + [Fact] + public void IsBinaryLoggerEnabled_ShouldHandleInvalidBinaryLoggerArgumentFormat() + { + // Arrange + var args = new List { "--binaryLogger:" }; + + // Act + var result = MSBuildHandler.IsBinaryLoggerEnabled(args, out string binLogFileName); + + // Assert + Assert.True(result); + Assert.Equal(CliConstants.BinLogFileName, binLogFileName); + Assert.DoesNotContain("--binaryLogger:", args); + } + + [Fact] + public void IsBinaryLoggerEnabled_ShouldHandleEmptyBinaryLoggerFilename() + { + // Arrange + var args = new List { "--binaryLogger:" }; + + // Act + var result = MSBuildHandler.IsBinaryLoggerEnabled(args, out string binLogFileName); + + // Assert + Assert.True(result); + Assert.Equal(CliConstants.BinLogFileName, binLogFileName); + } + } +} diff --git a/test/dotnet.Tests/Constants.cs b/test/dotnet.Tests/Constants.cs new file mode 100644 index 000000000000..0617ee3af3cd --- /dev/null +++ b/test/dotnet.Tests/Constants.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace dotnet.Tests +{ + internal static class Constants + { + public const string Debug = "Debug"; + public const string Release = "Release"; + } +} diff --git a/test/dotnet.Tests/dotnet.Tests.csproj b/test/dotnet.Tests/dotnet.Tests.csproj index 4bb8bfdc1df5..fe1345dfcd2d 100644 --- a/test/dotnet.Tests/dotnet.Tests.csproj +++ b/test/dotnet.Tests/dotnet.Tests.csproj @@ -56,6 +56,8 @@ + + PreserveNewest