From e9c2a238d05339599e23e40808ba38c19b8d215c Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Fri, 3 Jan 2025 13:34:41 +0100 Subject: [PATCH 01/36] Use msbuild APIs to load projects --- .../commands/dotnet-test/CliConstants.cs | 18 ++ .../dotnet-test/LocalizableStrings.resx | 12 + .../dotnet-test/MSBuildConnectionHandler.cs | 172 -------------- .../commands/dotnet-test/MSBuildHandler.cs | 220 ++++++++++++++++++ src/Cli/dotnet/commands/dotnet-test/Models.cs | 2 +- .../dotnet-test/SolutionAndProjectUtility.cs | 67 ++++++ .../commands/dotnet-test/TestApplication.cs | 10 +- .../dotnet-test/TestModulesFilterHandler.cs | 2 +- .../dotnet-test/TestingPlatformCommand.cs | 66 ++++-- .../dotnet-test/xlf/LocalizableStrings.cs.xlf | 20 ++ .../dotnet-test/xlf/LocalizableStrings.de.xlf | 20 ++ .../dotnet-test/xlf/LocalizableStrings.es.xlf | 20 ++ .../dotnet-test/xlf/LocalizableStrings.fr.xlf | 20 ++ .../dotnet-test/xlf/LocalizableStrings.it.xlf | 20 ++ .../dotnet-test/xlf/LocalizableStrings.ja.xlf | 20 ++ .../dotnet-test/xlf/LocalizableStrings.ko.xlf | 20 ++ .../dotnet-test/xlf/LocalizableStrings.pl.xlf | 20 ++ .../xlf/LocalizableStrings.pt-BR.xlf | 20 ++ .../dotnet-test/xlf/LocalizableStrings.ru.xlf | 20 ++ .../dotnet-test/xlf/LocalizableStrings.tr.xlf | 20 ++ .../xlf/LocalizableStrings.zh-Hans.xlf | 20 ++ .../xlf/LocalizableStrings.zh-Hant.xlf | 20 ++ 22 files changed, 636 insertions(+), 193 deletions(-) delete mode 100644 src/Cli/dotnet/commands/dotnet-test/MSBuildConnectionHandler.cs create mode 100644 src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs create mode 100644 src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs diff --git a/src/Cli/dotnet/commands/dotnet-test/CliConstants.cs b/src/Cli/dotnet/commands/dotnet-test/CliConstants.cs index 08ba9c16b3dc..8b8c4a33838c 100644 --- a/src/Cli/dotnet/commands/dotnet-test/CliConstants.cs +++ b/src/Cli/dotnet/commands/dotnet-test/CliConstants.cs @@ -15,6 +15,13 @@ internal static class CliConstants public const string MSBuildExeName = "MSBuild.dll"; public const string ParametersSeparator = "--"; + + public const string VSTest = "VSTest"; + public const string MicrosoftTestingPlatform = "MicrosoftTestingPlatform"; + + public const string TestSectionKey = "test"; + + public const string RestoreCommand = "restore"; } internal static class TestStates @@ -50,4 +57,15 @@ internal static class ProtocolConstants { internal const string Version = "1.0.0"; } + + internal static class ProjectProperties + { + internal const string IsTestingPlatformApplication = "IsTestingPlatformApplication"; + internal const string IsTestProject = "IsTestProject"; + internal const string TargetFramework = "TargetFramework"; + internal const string TargetFrameworks = "TargetFrameworks"; + internal const string TargetPath = "TargetPath"; + internal const string ProjectFullPath = "MSBuildProjectFullPath"; + internal const string RunSettingsFilePath = "RunSettingsFilePath"; + } } diff --git a/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx b/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx index 2a6adfc6f413..5cd563a8fbbf 100644 --- a/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx +++ b/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx @@ -326,4 +326,16 @@ Examples: Test application(s) that support VSTest are not supported. + + Test runner not supported: {0}. + + + The provided project file path does not exist: {0}. + + + Specify which project or solution file to use because this folder contains more than one project or solution file. + + + Specify a project or solution file. The current working directory does not contain a project or solution file. + diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildConnectionHandler.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildConnectionHandler.cs deleted file mode 100644 index e8e38728b825..000000000000 --- a/src/Cli/dotnet/commands/dotnet-test/MSBuildConnectionHandler.cs +++ /dev/null @@ -1,172 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Concurrent; -using System.CommandLine; -using System.IO.Pipes; -using Microsoft.DotNet.Cli.Utils; -using Microsoft.DotNet.Tools.Test; - -namespace Microsoft.DotNet.Cli -{ - internal sealed class MSBuildConnectionHandler : IDisposable - { - private List _args; - private readonly TestApplicationActionQueue _actionQueue; - - private readonly PipeNameDescription _pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N")); - private readonly List _namedPipeConnections = new(); - private readonly ConcurrentBag _testApplications = new(); - private bool _areTestingPlatformApplications = true; - - public MSBuildConnectionHandler(List args, TestApplicationActionQueue actionQueue) - { - _args = args; - _actionQueue = actionQueue; - } - - public async Task WaitConnectionAsync(CancellationToken token) - { - VSTestTrace.SafeWriteTrace(() => $"Waiting for connection(s) on pipe = {_pipeNameDescription.Name}"); - - try - { - while (!token.IsCancellationRequested) - { - NamedPipeServer pipeConnection = new(_pipeNameDescription, OnRequest, NamedPipeServerStream.MaxAllowedServerInstances, token, skipUnknownMessages: true); - pipeConnection.RegisterAllSerializers(); - - await pipeConnection.WaitConnectionAsync(token); - - _namedPipeConnections.Add(pipeConnection); - } - } - catch (OperationCanceledException ex) when (ex.CancellationToken == token) - { - // We are exiting - } - catch (Exception ex) - { - if (VSTestTrace.TraceEnabled) - { - VSTestTrace.SafeWriteTrace(() => ex.ToString()); - } - - Environment.FailFast(ex.ToString()); - } - } - - private Task OnRequest(IRequest request) - { - try - { - if (request is not ModuleMessage module) - { - throw new NotSupportedException($"Request '{request.GetType()}' is unsupported."); - } - - // Check if the test app has IsTestingPlatformApplication property set to true - if (bool.TryParse(module.IsTestingPlatformApplication, out bool isTestingPlatformApplication) && isTestingPlatformApplication) - { - var testApp = new TestApplication(new Module(module.DllOrExePath, module.ProjectPath, module.TargetFramework, module.RunSettingsFilePath), _args); - _testApplications.Add(testApp); - } - else // If one test app has IsTestingPlatformApplication set to false, then we will not run any of the test apps - { - _areTestingPlatformApplications = false; - } - } - catch (Exception ex) - { - if (VSTestTrace.TraceEnabled) - { - VSTestTrace.SafeWriteTrace(() => ex.ToString()); - } - - Environment.FailFast(ex.ToString()); - } - - return Task.FromResult((IResponse)VoidResponse.CachedInstance); - } - - public bool EnqueueTestApplications() - { - if (!_areTestingPlatformApplications) - { - return false; - } - - foreach (var testApp in _testApplications) - { - _actionQueue.Enqueue(testApp); - } - return true; - } - - public int RunWithMSBuild(ParseResult parseResult) - { - List msbuildCommandLineArgs = - [ - parseResult.GetValue(TestingPlatformOptions.ProjectOption) ?? string.Empty, - "-t:Restore;_GetTestsProject", - $"-p:GetTestsProjectPipeName={_pipeNameDescription.Name}", - "-verbosity:q", - "-nologo", - ]; - - AddBinLogParameterIfExists(msbuildCommandLineArgs, _args); - AddAdditionalMSBuildParametersIfExist(parseResult, msbuildCommandLineArgs); - - if (VSTestTrace.TraceEnabled) - { - VSTestTrace.SafeWriteTrace(() => $"MSBuild command line arguments: {string.Join(" ", msbuildCommandLineArgs)}"); - } - - ForwardingAppImplementation msBuildForwardingApp = new(GetMSBuildExePath(), msbuildCommandLineArgs); - return msBuildForwardingApp.Execute(); - } - - private static void AddBinLogParameterIfExists(List msbuildCommandLineArgs, List args) - { - var binLog = args.FirstOrDefault(arg => arg.StartsWith("-bl", StringComparison.OrdinalIgnoreCase)); - - if (!string.IsNullOrEmpty(binLog)) - { - msbuildCommandLineArgs.Add(binLog); - - // We remove it from the args list so that it is not passed to the test application - args.Remove(binLog); - } - } - - private static void AddAdditionalMSBuildParametersIfExist(ParseResult parseResult, List parameters) - { - string msBuildParameters = parseResult.GetValue(TestingPlatformOptions.AdditionalMSBuildParametersOption); - - if (!string.IsNullOrEmpty(msBuildParameters)) - { - parameters.AddRange(msBuildParameters.Split(" ", StringSplitOptions.RemoveEmptyEntries)); - } - } - - private static string GetMSBuildExePath() - { - return Path.Combine( - AppContext.BaseDirectory, - CliConstants.MSBuildExeName); - } - - public void Dispose() - { - foreach (var namedPipeServer in _namedPipeConnections) - { - namedPipeServer.Dispose(); - } - - foreach (var testApplication in _testApplications) - { - testApplication.Dispose(); - } - } - } -} diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs new file mode 100644 index 000000000000..7737f0dd35f1 --- /dev/null +++ b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs @@ -0,0 +1,220 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Concurrent; +using Microsoft.Build.Evaluation; +using Microsoft.Build.Execution; +using Microsoft.Build.Framework; +using Microsoft.Build.Logging; + +namespace Microsoft.DotNet.Cli +{ + internal sealed class MSBuildHandler : IDisposable + { + private readonly List _args; + private readonly TestApplicationActionQueue _actionQueue; + private readonly int _degreeOfParallelism; + + private readonly ConcurrentBag _testApplications = new(); + private bool _areTestingPlatformApplications = true; + + private const string BinLogFileName = "msbuild.binlog"; + private static readonly object s_buildLock = new(); + + public MSBuildHandler(List args, TestApplicationActionQueue actionQueue, int degreeOfParallelism) + { + _args = args; + _actionQueue = actionQueue; + _degreeOfParallelism = degreeOfParallelism; + } + + public int RunWithMSBuild(bool allowBinLog) + { + bool solutionOrProjectFileFound = SolutionAndProjectUtility.TryGetSolutionOrProjectFilePath(Directory.GetCurrentDirectory(), out string filePath, out bool isSolution); + + if (!solutionOrProjectFileFound) + { + return 1; + } + + (IEnumerable modules, bool restored) = GetProjectsProperties(filePath, isSolution, allowBinLog); + + InitializeTestApplications(modules); + + return restored ? 0 : 1; + } + + public int RunWithMSBuild(string filePath, bool allowBinLog) + { + (IEnumerable modules, bool restored) = GetProjectsProperties(filePath, false, allowBinLog); + + InitializeTestApplications(modules); + + return restored ? 0 : 1; + } + + private void InitializeTestApplications(IEnumerable modules) + { + foreach (Module module in modules) + { + if (module.IsTestProject && module.IsTestingPlatformApplication) + { + var testApp = new TestApplication(module, _args); + _testApplications.Add(testApp); + } + else // If one test app has IsTestingPlatformApplication set to false, then we will not run any of the test apps + { + _areTestingPlatformApplications = false; + } + } + } + + public bool EnqueueTestApplications() + { + if (!_areTestingPlatformApplications) + { + return false; + } + + foreach (var testApp in _testApplications) + { + _actionQueue.Enqueue(testApp); + } + return true; + } + + private (IEnumerable, bool Restored) GetProjectsProperties(string solutionOrProjectFilePath, bool isSolution, bool allowBinLog) + { + var allProjects = new ConcurrentBag(); + bool restored = true; + + if (isSolution) + { + var projects = SolutionAndProjectUtility.GetProjectsFromSolutionFile(solutionOrProjectFilePath); + ProcessProjectsInParallel(projects, allowBinLog, allProjects, ref restored); + } + else + { + var (relatedProjects, isProjectBuilt) = GetProjectPropertiesInternal(solutionOrProjectFilePath, allowBinLog); + foreach (var relatedProject in relatedProjects) + { + allProjects.Add(relatedProject); + } + + if (!isProjectBuilt) + { + restored = false; + } + } + return (allProjects, restored); + } + + private void ProcessProjectsInParallel(IEnumerable projects, bool allowBinLog, ConcurrentBag allProjects, ref bool restored) + { + bool allProjectsRestored = true; + + Parallel.ForEach(projects, new ParallelOptions { MaxDegreeOfParallelism = _degreeOfParallelism }, project => + { + var (relatedProjects, isRestored) = GetProjectPropertiesInternal(project, allowBinLog); + foreach (var relatedProject in relatedProjects) + { + allProjects.Add(relatedProject); + } + + if (!isRestored) + { + allProjectsRestored = false; + } + }); + + restored = allProjectsRestored; + } + + private static (IEnumerable Modules, bool Restored) GetProjectPropertiesInternal(string projectFilePath, bool allowBinLog) + { + var projectCollection = new ProjectCollection(); + var project = projectCollection.LoadProject(projectFilePath); + var buildResult = RestoreProject(projectFilePath, allowBinLog, projectCollection); + + bool restored = buildResult.OverallResult == BuildResultCode.Success; + + if (!restored) + { + return (Array.Empty(), restored); + } + + return (ExtractModulesFromProject(project), restored); + } + + private static IEnumerable ExtractModulesFromProject(Project project) + { + _ = bool.TryParse(project.GetPropertyValue(ProjectProperties.IsTestingPlatformApplication), out bool isTestingPlatformApplication); + _ = bool.TryParse(project.GetPropertyValue(ProjectProperties.IsTestProject), out bool isTestProject); + string targetFramework = project.GetPropertyValue(ProjectProperties.TargetFramework); + string targetFrameworks = project.GetPropertyValue(ProjectProperties.TargetFrameworks); + string targetPath = project.GetPropertyValue(ProjectProperties.TargetPath); + string projectFullPath = project.GetPropertyValue(ProjectProperties.ProjectFullPath); + string runSettingsFilePath = project.GetPropertyValue(ProjectProperties.RunSettingsFilePath); + + var projects = new List(); + + if (string.IsNullOrEmpty(targetFrameworks)) + { + projects.Add(new Module(targetPath, projectFullPath, targetFramework, runSettingsFilePath, isTestingPlatformApplication, isTestProject)); + } + else + { + var frameworks = targetFrameworks.Split(';', StringSplitOptions.RemoveEmptyEntries); + foreach (var framework in frameworks) + { + project.SetProperty(ProjectProperties.TargetFramework, framework); + project.ReevaluateIfNecessary(); + + projects.Add(new Module(project.GetPropertyValue(ProjectProperties.TargetPath), + projectFullPath, + framework, + runSettingsFilePath, + isTestingPlatformApplication, + isTestProject)); + } + } + + return projects; + } + + private static BuildResult RestoreProject(string projectFilePath, bool allowBinLog, ProjectCollection projectCollection) + { + BuildParameters parameters = new(projectCollection) + { + Loggers = [new ConsoleLogger(LoggerVerbosity.Quiet)] + }; + + if (allowBinLog) + { + parameters.Loggers = parameters.Loggers.Concat([ + new BinaryLogger + { + Parameters = BinLogFileName + } + ]); + } + + var buildRequestData = new BuildRequestData(projectFilePath, new Dictionary(), null, [CliConstants.RestoreCommand], null); + BuildResult buildResult; + lock (s_buildLock) + { + buildResult = BuildManager.DefaultBuildManager.Build(parameters, buildRequestData); + } + + return buildResult; + } + + public void Dispose() + { + foreach (var testApplication in _testApplications) + { + testApplication.Dispose(); + } + } + } +} diff --git a/src/Cli/dotnet/commands/dotnet-test/Models.cs b/src/Cli/dotnet/commands/dotnet-test/Models.cs index 8484f819f38d..9f18391810c6 100644 --- a/src/Cli/dotnet/commands/dotnet-test/Models.cs +++ b/src/Cli/dotnet/commands/dotnet-test/Models.cs @@ -3,7 +3,7 @@ namespace Microsoft.DotNet.Cli { - internal sealed record Module(string? DllOrExePath, string? ProjectPath, string? TargetFramework, string? RunSettingsFilePath); + internal sealed record Module(string? DllOrExePath, string? ProjectPath, string? TargetFramework, string? RunSettingsFilePath, bool IsTestingPlatformApplication, bool IsTestProject); internal sealed record Handshake(Dictionary? Properties); diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs new file mode 100644 index 000000000000..a0d247008c33 --- /dev/null +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -0,0 +1,67 @@ +// 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.Build.Construction; +using Microsoft.DotNet.Tools.Test; + +namespace Microsoft.DotNet.Cli +{ + internal static class SolutionAndProjectUtility + { + public static bool TryGetSolutionOrProjectFilePath(string directory, out string solutionOrProjectFilePath, out bool isSolution) + { + var solutionFiles = GetSolutionFilePaths(directory); + var projectFiles = GetProjectFilePaths(directory); + + solutionOrProjectFilePath = string.Empty; + isSolution = false; + + if (solutionFiles.Length > 0 && projectFiles.Length > 0) + { + VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMultipleProjectOrSolutionFilesErrorMessage); + return false; + } + + if (solutionFiles.Length == 1) + { + solutionOrProjectFilePath = solutionFiles[0]; + isSolution = true; + + return true; + } + + if (projectFiles.Length == 1) + { + solutionOrProjectFilePath = projectFiles[0]; + + return true; + } + + VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdNoProjectOrSolutionFileErrorMessage); + return false; + } + + public static IEnumerable GetProjectsFromSolutionFile(string solutionFilePath) + { + var solutionFile = SolutionFile.Parse(solutionFilePath); + return solutionFile.ProjectsInOrder.Select(project => project.AbsolutePath); + } + + private static string[] GetSolutionFilePaths(string directory) + { + var solutionFiles = Directory.GetFiles(directory, "*.sln*", SearchOption.TopDirectoryOnly) + .Where(f => Regex.IsMatch(f, @"\.(sln|slnx)$")).ToArray(); + + return solutionFiles; + } + + private static string[] GetProjectFilePaths(string directory) + { + var projectFiles = Directory.GetFiles(directory, "*.*proj", SearchOption.TopDirectoryOnly) + .Where(f => Regex.IsMatch(f, @"\.(csproj|vbproj|fsproj)$")).ToArray(); + + return projectFiles; + } + } +} diff --git a/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs b/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs index c727ffd4fe3b..decbbd7d587f 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs @@ -4,6 +4,7 @@ using System.Collections.Concurrent; using System.Diagnostics; using System.IO.Pipes; +using Microsoft.DotNet.Tools.Common; using Microsoft.DotNet.Tools.Test; namespace Microsoft.DotNet.Cli @@ -33,6 +34,9 @@ internal sealed class TestApplication : IDisposable 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) @@ -55,7 +59,7 @@ public async Task RunAsync(bool isFilterMode, bool enableHelp, BuiltInOptio return 1; } - bool isDll = _module.DllOrExePath.EndsWith(".dll"); + bool isDll = _module.DllOrExePath.HasExtension(DLLExtension); ProcessStartInfo processStartInfo = new() { @@ -67,7 +71,7 @@ public async Task RunAsync(bool isFilterMode, bool enableHelp, BuiltInOptio if (!string.IsNullOrEmpty(_module.RunSettingsFilePath)) { - processStartInfo.EnvironmentVariables.Add("TESTINGPLATFORM_VSTESTBRIDGE_RUNSETTINGS_FILE", _module.RunSettingsFilePath); + processStartInfo.EnvironmentVariables.Add(TestingPlatformVsTestBridgeRunSettingsFileEnvVar, _module.RunSettingsFilePath); } _testAppPipeConnectionLoop = Task.Run(async () => await WaitConnectionAsync(_cancellationToken.Token), _cancellationToken.Token); @@ -81,7 +85,7 @@ public async Task RunAsync(bool isFilterMode, bool enableHelp, BuiltInOptio 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) diff --git a/src/Cli/dotnet/commands/dotnet-test/TestModulesFilterHandler.cs b/src/Cli/dotnet/commands/dotnet-test/TestModulesFilterHandler.cs index 7562e0225296..80179fbf5732 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestModulesFilterHandler.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestModulesFilterHandler.cs @@ -50,7 +50,7 @@ public bool RunWithTestModulesFilter(ParseResult parseResult) foreach (string testModule in testModulePaths) { - var testApp = new TestApplication(new Module(testModule, null, null, null), _args); + var testApp = new TestApplication(new Module(testModule, null, null, null, true, true), _args); // Write the test application to the channel _actionQueue.Enqueue(testApp); } diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs index b947031fdca2..b07515a3c9ba 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs @@ -11,12 +11,10 @@ namespace Microsoft.DotNet.Cli internal partial class TestingPlatformCommand : CliCommand, ICustomHelp { private readonly ConcurrentBag _testApplications = []; - private readonly CancellationTokenSource _cancellationToken = new(); - private MSBuildConnectionHandler _msBuildConnectionHandler; + private MSBuildHandler _msBuildConnectionHandler; private TestModulesFilterHandler _testModulesFilterHandler; private TestApplicationActionQueue _actionQueue; - private Task _namedPipeConnectionLoop; private List _args; public TestingPlatformCommand(string name, string description = null) : base(name, description) @@ -78,10 +76,9 @@ public int Run(ParseResult parseResult) }); } - _args = new List(parseResult.UnmatchedTokens); - _msBuildConnectionHandler = new(_args, _actionQueue); + _args = [.. parseResult.UnmatchedTokens]; + _msBuildConnectionHandler = new(_args, _actionQueue, degreeOfParallelism); _testModulesFilterHandler = new(_args, _actionQueue); - _namedPipeConnectionLoop = Task.Run(async () => await _msBuildConnectionHandler.WaitConnectionAsync(_cancellationToken.Token)); if (parseResult.HasOption(TestingPlatformOptions.TestModulesFilterOption)) { @@ -92,15 +89,13 @@ public int Run(ParseResult parseResult) } else { - // If no filter was provided, MSBuild will get the test project paths - var msbuildResult = _msBuildConnectionHandler.RunWithMSBuild(parseResult); - if (msbuildResult != 0) + bool allowBinLog = BinLogEnabled(_args); + if (!RunMSBuild(parseResult, allowBinLog)) { - VSTestTrace.SafeWriteTrace(() => $"MSBuild task _GetTestsProject didn't execute properly with exit code: {msbuildResult}."); return ExitCodes.GenericFailure; } - // If not all test projects have IsTestingPlatformApplication set to true, we will simply return + // If not all test projects have IsTestProject and IsTestingPlatformApplication proprties set to true, we will simply return if (!_msBuildConnectionHandler.EnqueueTestApplications()) { VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdUnsupportedVSTestTestApplicationsDescription); @@ -111,8 +106,6 @@ public int Run(ParseResult parseResult) _actionQueue.EnqueueCompleted(); hasFailed = _actionQueue.WaitAllActions(); // Above line will block till we have all connections and all GetTestsProject msbuild task complete. - - WaitOnMSBuildHandlerPipeConnectionLoop(); } finally { @@ -123,10 +116,51 @@ public int Run(ParseResult parseResult) return hasFailed ? ExitCodes.GenericFailure : ExitCodes.Success; } - private void WaitOnMSBuildHandlerPipeConnectionLoop() + private bool RunMSBuild(ParseResult parseResult, bool allowBinLog) + { + int msbuildResult; + + if (parseResult.HasOption(TestingPlatformOptions.ProjectOption)) + { + string filePath = parseResult.GetValue(TestingPlatformOptions.ProjectOption); + + if (!File.Exists(filePath)) + { + VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdNonExistentProjectFilePathDescription); + return false; + } + + msbuildResult = _msBuildConnectionHandler.RunWithMSBuild(filePath, allowBinLog); + } + else + { + // If no filter was provided neither the project using --project, + // MSBuild will get the test project paths in the current directory + msbuildResult = _msBuildConnectionHandler.RunWithMSBuild(allowBinLog); + } + + if (msbuildResult != 0) + { + VSTestTrace.SafeWriteTrace(() => $"Get projects properties with msbuild didn't execute properly with exit code: {msbuildResult}."); + return false; + } + + return true; + } + + private static bool BinLogEnabled(List args) { - _cancellationToken.Cancel(); - _namedPipeConnectionLoop.Wait((int)TimeSpan.FromSeconds(30).TotalMilliseconds); + var binLog = args.FirstOrDefault(arg => arg.StartsWith("-bl", StringComparison.OrdinalIgnoreCase)); + + if (!string.IsNullOrEmpty(binLog)) + { + // We remove it from the args list so that it is not passed to the test application + args.Remove(binLog); + + return true; + } + + return false; } private void CleanUp() diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.cs.xlf index 2ad19fb0b1b8..703cbe186334 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.cs.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.cs.xlf @@ -123,16 +123,31 @@ Příklady: Maximální počet testovacích modulů, které je možné spustit paralelně. + + Specify which project or solution file to use because this folder contains more than one project or solution file. + Specify which project or solution file to use because this folder contains more than one project or solution file. + + + + Specify a project or solution file. The current working directory does not contain a project or solution file. + Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not execute an implicit restore. Do not execute an implicit restore. + + The provided project file path does not exist: {0}. + The provided project file path does not exist: {0}. + + Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. @@ -255,6 +270,11 @@ Pokud zadaný adresář neexistuje, bude vytvořen. Message Request type '{0}' is unsupported. {0} - message request type + + Test runner not supported: {0}. + Test runner not supported: {0}. + + Test application(s) that support VSTest are not supported. Test application(s) that support VSTest are not supported. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.de.xlf index 230a26c4cdf8..797d23c7ebf9 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.de.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.de.xlf @@ -123,16 +123,31 @@ Beispiele: Die maximale Anzahl von Testmodulen, die parallel ausgeführt werden können. + + Specify which project or solution file to use because this folder contains more than one project or solution file. + Specify which project or solution file to use because this folder contains more than one project or solution file. + + + + Specify a project or solution file. The current working directory does not contain a project or solution file. + Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not execute an implicit restore. Do not execute an implicit restore. + + The provided project file path does not exist: {0}. + The provided project file path does not exist: {0}. + + Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. @@ -255,6 +270,11 @@ Das angegebene Verzeichnis wird erstellt, wenn es nicht vorhanden ist. Message Request type '{0}' is unsupported. {0} - message request type + + Test runner not supported: {0}. + Test runner not supported: {0}. + + Test application(s) that support VSTest are not supported. Test application(s) that support VSTest are not supported. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.es.xlf index b4504cd41ca0..7251100c775f 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.es.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.es.xlf @@ -125,16 +125,31 @@ Ejemplos: Número máximo de módulos de prueba que se pueden ejecutar en paralelo. + + Specify which project or solution file to use because this folder contains more than one project or solution file. + Specify which project or solution file to use because this folder contains more than one project or solution file. + + + + Specify a project or solution file. The current working directory does not contain a project or solution file. + Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not execute an implicit restore. Do not execute an implicit restore. + + The provided project file path does not exist: {0}. + The provided project file path does not exist: {0}. + + Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. @@ -257,6 +272,11 @@ Si no existe, se creará el directorio especificado. Message Request type '{0}' is unsupported. {0} - message request type + + Test runner not supported: {0}. + Test runner not supported: {0}. + + Test application(s) that support VSTest are not supported. Test application(s) that support VSTest are not supported. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.fr.xlf index 080d51785fda..78d94ff6be5e 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.fr.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.fr.xlf @@ -123,16 +123,31 @@ Exemples : Nombre maximal de modules de test qui peuvent s’exécuter en parallèle. + + Specify which project or solution file to use because this folder contains more than one project or solution file. + Specify which project or solution file to use because this folder contains more than one project or solution file. + + + + Specify a project or solution file. The current working directory does not contain a project or solution file. + Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not execute an implicit restore. Do not execute an implicit restore. + + The provided project file path does not exist: {0}. + The provided project file path does not exist: {0}. + + Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. @@ -255,6 +270,11 @@ Le répertoire spécifié est créé, s'il n'existe pas déjà. Message Request type '{0}' is unsupported. {0} - message request type + + Test runner not supported: {0}. + Test runner not supported: {0}. + + Test application(s) that support VSTest are not supported. Test application(s) that support VSTest are not supported. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.it.xlf index 31ea56d050fe..3c2c08cba064 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.it.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.it.xlf @@ -123,16 +123,31 @@ Esempi: Numero massimo di moduli di test che possono essere eseguiti in parallelo. + + Specify which project or solution file to use because this folder contains more than one project or solution file. + Specify which project or solution file to use because this folder contains more than one project or solution file. + + + + Specify a project or solution file. The current working directory does not contain a project or solution file. + Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not execute an implicit restore. Do not execute an implicit restore. + + The provided project file path does not exist: {0}. + The provided project file path does not exist: {0}. + + Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. @@ -255,6 +270,11 @@ Se non esiste, la directory specificata verrà creata. Message Request type '{0}' is unsupported. {0} - message request type + + Test runner not supported: {0}. + Test runner not supported: {0}. + + Test application(s) that support VSTest are not supported. Test application(s) that support VSTest are not supported. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ja.xlf index cc1420582f48..cc5890fafcea 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ja.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ja.xlf @@ -123,16 +123,31 @@ Examples: 並列で実行できるテスト モジュールの最大数。 + + Specify which project or solution file to use because this folder contains more than one project or solution file. + Specify which project or solution file to use because this folder contains more than one project or solution file. + + + + Specify a project or solution file. The current working directory does not contain a project or solution file. + Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not execute an implicit restore. Do not execute an implicit restore. + + The provided project file path does not exist: {0}. + The provided project file path does not exist: {0}. + + Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. @@ -255,6 +270,11 @@ The specified directory will be created if it does not exist. Message Request type '{0}' is unsupported. {0} - message request type + + Test runner not supported: {0}. + Test runner not supported: {0}. + + Test application(s) that support VSTest are not supported. Test application(s) that support VSTest are not supported. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ko.xlf index 37bb82ef49af..771f56358fc5 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ko.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ko.xlf @@ -123,16 +123,31 @@ Examples: 병렬로 실행할 수 있는 최대 테스트 모듈 수입니다. + + Specify which project or solution file to use because this folder contains more than one project or solution file. + Specify which project or solution file to use because this folder contains more than one project or solution file. + + + + Specify a project or solution file. The current working directory does not contain a project or solution file. + Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not execute an implicit restore. Do not execute an implicit restore. + + The provided project file path does not exist: {0}. + The provided project file path does not exist: {0}. + + Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. @@ -255,6 +270,11 @@ The specified directory will be created if it does not exist. Message Request type '{0}' is unsupported. {0} - message request type + + Test runner not supported: {0}. + Test runner not supported: {0}. + + Test application(s) that support VSTest are not supported. Test application(s) that support VSTest are not supported. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pl.xlf index 8d40d724227b..fb5c9739723f 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pl.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pl.xlf @@ -123,16 +123,31 @@ Przykłady: Maksymalna liczba modułów testowych, które mogą być uruchamiane równolegle. + + Specify which project or solution file to use because this folder contains more than one project or solution file. + Specify which project or solution file to use because this folder contains more than one project or solution file. + + + + Specify a project or solution file. The current working directory does not contain a project or solution file. + Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not execute an implicit restore. Do not execute an implicit restore. + + The provided project file path does not exist: {0}. + The provided project file path does not exist: {0}. + + Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. @@ -255,6 +270,11 @@ Jeśli określony katalog nie istnieje, zostanie utworzony. Message Request type '{0}' is unsupported. {0} - message request type + + Test runner not supported: {0}. + Test runner not supported: {0}. + + Test application(s) that support VSTest are not supported. Test application(s) that support VSTest are not supported. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pt-BR.xlf index 5eaa4668710e..b79ec7fce877 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pt-BR.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pt-BR.xlf @@ -123,16 +123,31 @@ Exemplos: O número máximo de módulos de teste que podem ser executados em paralelo. + + Specify which project or solution file to use because this folder contains more than one project or solution file. + Specify which project or solution file to use because this folder contains more than one project or solution file. + + + + Specify a project or solution file. The current working directory does not contain a project or solution file. + Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not execute an implicit restore. Do not execute an implicit restore. + + The provided project file path does not exist: {0}. + The provided project file path does not exist: {0}. + + Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. @@ -255,6 +270,11 @@ O diretório especificado será criado se ele ainda não existir. Message Request type '{0}' is unsupported. {0} - message request type + + Test runner not supported: {0}. + Test runner not supported: {0}. + + Test application(s) that support VSTest are not supported. Test application(s) that support VSTest are not supported. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ru.xlf index a9669d9915c4..868eebdf8fe9 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ru.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ru.xlf @@ -123,16 +123,31 @@ Examples: Максимальное число тестовых модулей, которые могут выполняться параллельно. + + Specify which project or solution file to use because this folder contains more than one project or solution file. + Specify which project or solution file to use because this folder contains more than one project or solution file. + + + + Specify a project or solution file. The current working directory does not contain a project or solution file. + Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not execute an implicit restore. Do not execute an implicit restore. + + The provided project file path does not exist: {0}. + The provided project file path does not exist: {0}. + + Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. @@ -255,6 +270,11 @@ The specified directory will be created if it does not exist. Message Request type '{0}' is unsupported. {0} - message request type + + Test runner not supported: {0}. + Test runner not supported: {0}. + + Test application(s) that support VSTest are not supported. Test application(s) that support VSTest are not supported. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.tr.xlf index 68bcd480e31b..df133774806c 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.tr.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.tr.xlf @@ -123,16 +123,31 @@ Bu bağımsız değişken, birden çok değişken sağlamak için birden çok ke Paralel olarak çalıştırılabilecek test modüllerinin maksimum sayısı. + + Specify which project or solution file to use because this folder contains more than one project or solution file. + Specify which project or solution file to use because this folder contains more than one project or solution file. + + + + Specify a project or solution file. The current working directory does not contain a project or solution file. + Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not execute an implicit restore. Do not execute an implicit restore. + + The provided project file path does not exist: {0}. + The provided project file path does not exist: {0}. + + Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. @@ -255,6 +270,11 @@ Belirtilen dizin yoksa oluşturulur. Message Request type '{0}' is unsupported. {0} - message request type + + Test runner not supported: {0}. + Test runner not supported: {0}. + + Test application(s) that support VSTest are not supported. Test application(s) that support VSTest are not supported. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hans.xlf index e7ac40be829b..a930b18c3a02 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hans.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hans.xlf @@ -123,16 +123,31 @@ Examples: 可并行运行的测试模块的最大数目。 + + Specify which project or solution file to use because this folder contains more than one project or solution file. + Specify which project or solution file to use because this folder contains more than one project or solution file. + + + + Specify a project or solution file. The current working directory does not contain a project or solution file. + Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not execute an implicit restore. Do not execute an implicit restore. + + The provided project file path does not exist: {0}. + The provided project file path does not exist: {0}. + + Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. @@ -255,6 +270,11 @@ The specified directory will be created if it does not exist. Message Request type '{0}' is unsupported. {0} - message request type + + Test runner not supported: {0}. + Test runner not supported: {0}. + + Test application(s) that support VSTest are not supported. Test application(s) that support VSTest are not supported. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hant.xlf index 222eb321c2c4..d625ff9e7272 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hant.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hant.xlf @@ -123,16 +123,31 @@ Examples: 可平行執行的測試模組數目上限。 + + Specify which project or solution file to use because this folder contains more than one project or solution file. + Specify which project or solution file to use because this folder contains more than one project or solution file. + + + + Specify a project or solution file. The current working directory does not contain a project or solution file. + Specify a project or solution file. The current working directory does not contain a project or solution file. + + Do not execute an implicit restore. Do not execute an implicit restore. + + The provided project file path does not exist: {0}. + The provided project file path does not exist: {0}. + + Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. @@ -255,6 +270,11 @@ The specified directory will be created if it does not exist. Message Request type '{0}' is unsupported. {0} - message request type + + Test runner not supported: {0}. + Test runner not supported: {0}. + + Test application(s) that support VSTest are not supported. Test application(s) that support VSTest are not supported. From e7bcc8a26f2481c017016dbdfd12f35705c1fab4 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Fri, 3 Jan 2025 14:29:04 +0100 Subject: [PATCH 02/36] Add space --- src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs index 7737f0dd35f1..8b4f143ccdeb 100644 --- a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs +++ b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs @@ -150,6 +150,7 @@ private static IEnumerable ExtractModulesFromProject(Project project) { _ = bool.TryParse(project.GetPropertyValue(ProjectProperties.IsTestingPlatformApplication), out bool isTestingPlatformApplication); _ = bool.TryParse(project.GetPropertyValue(ProjectProperties.IsTestProject), out bool isTestProject); + string targetFramework = project.GetPropertyValue(ProjectProperties.TargetFramework); string targetFrameworks = project.GetPropertyValue(ProjectProperties.TargetFrameworks); string targetPath = project.GetPropertyValue(ProjectProperties.TargetPath); From 8d747c73d0972ddda8d5fa37e4bc3ff9452f69c4 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Mon, 6 Jan 2025 11:03:39 +0100 Subject: [PATCH 03/36] Fix formatting --- .../dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index a0d247008c33..cef916eeb61f 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -51,7 +51,7 @@ public static IEnumerable GetProjectsFromSolutionFile(string solutionFil private static string[] GetSolutionFilePaths(string directory) { var solutionFiles = Directory.GetFiles(directory, "*.sln*", SearchOption.TopDirectoryOnly) - .Where(f => Regex.IsMatch(f, @"\.(sln|slnx)$")).ToArray(); + .Where(f => Regex.IsMatch(f, @"\.(sln|slnx)$")).ToArray(); return solutionFiles; } @@ -59,7 +59,7 @@ private static string[] GetSolutionFilePaths(string directory) private static string[] GetProjectFilePaths(string directory) { var projectFiles = Directory.GetFiles(directory, "*.*proj", SearchOption.TopDirectoryOnly) - .Where(f => Regex.IsMatch(f, @"\.(csproj|vbproj|fsproj)$")).ToArray(); + .Where(f => Regex.IsMatch(f, @"\.(csproj|vbproj|fsproj)$")).ToArray(); return projectFiles; } From 2b02833fe18f8987cf49f8345ae2735122f7b738 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Mon, 6 Jan 2025 11:07:03 +0100 Subject: [PATCH 04/36] Add comments --- .../dotnet-test/SolutionAndProjectUtility.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index cef916eeb61f..fab9a06695ca 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -11,33 +11,37 @@ internal static class SolutionAndProjectUtility { public static bool TryGetSolutionOrProjectFilePath(string directory, out string solutionOrProjectFilePath, out bool isSolution) { - var solutionFiles = GetSolutionFilePaths(directory); - var projectFiles = GetProjectFilePaths(directory); - solutionOrProjectFilePath = string.Empty; isSolution = false; + // Get all solution files in the specified directory + var solutionFiles = GetSolutionFilePaths(directory); + // Get all project files in the specified directory + var projectFiles = GetProjectFilePaths(directory); + + // If both solution files and project files are found, return false if (solutionFiles.Length > 0 && projectFiles.Length > 0) { VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMultipleProjectOrSolutionFilesErrorMessage); return false; } + // If exactly one solution file is found, return the solution file path if (solutionFiles.Length == 1) { solutionOrProjectFilePath = solutionFiles[0]; isSolution = true; - return true; } + // If exactly one project file is found, return the project file path if (projectFiles.Length == 1) { solutionOrProjectFilePath = projectFiles[0]; - return true; } + // If no solution or project files are found, return false VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdNoProjectOrSolutionFileErrorMessage); return false; } From f4cce4e3d97c13d1863c24c09cda3ed3606cfd4e Mon Sep 17 00:00:00 2001 From: Mariam Abdullah <122357303+mariam-abdulla@users.noreply.github.com> Date: Mon, 6 Jan 2025 14:26:29 +0100 Subject: [PATCH 05/36] Update src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Amaury Levé --- src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs index b07515a3c9ba..ecf2fe52ed92 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs @@ -141,7 +141,7 @@ private bool RunMSBuild(ParseResult parseResult, bool allowBinLog) if (msbuildResult != 0) { - VSTestTrace.SafeWriteTrace(() => $"Get projects properties with msbuild didn't execute properly with exit code: {msbuildResult}."); + VSTestTrace.SafeWriteTrace(() => $"Get projects properties with MSBuild didn't execute properly with exit code: {msbuildResult}."); return false; } From f07806092a11702480458641617d004b2b45fed1 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Mon, 6 Jan 2025 14:45:24 +0100 Subject: [PATCH 06/36] Replace 0 and 1 with constants --- src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs index 8b4f143ccdeb..db2f965f890f 100644 --- a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs +++ b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs @@ -34,14 +34,14 @@ public int RunWithMSBuild(bool allowBinLog) if (!solutionOrProjectFileFound) { - return 1; + return ExitCodes.GenericFailure; } (IEnumerable modules, bool restored) = GetProjectsProperties(filePath, isSolution, allowBinLog); InitializeTestApplications(modules); - return restored ? 0 : 1; + return restored ? ExitCodes.Success : ExitCodes.GenericFailure; } public int RunWithMSBuild(string filePath, bool allowBinLog) @@ -50,7 +50,7 @@ public int RunWithMSBuild(string filePath, bool allowBinLog) InitializeTestApplications(modules); - return restored ? 0 : 1; + return restored ? ExitCodes.Success : ExitCodes.GenericFailure; } private void InitializeTestApplications(IEnumerable modules) From 771c1f10f255a3ab167f28898c5d4ef8e97275c1 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Mon, 6 Jan 2025 14:51:57 +0100 Subject: [PATCH 07/36] Return from InitializeTestApplications() if _areTestingPlatformApplications is set to false --- src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs index db2f965f890f..94e77f4580be 100644 --- a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs +++ b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs @@ -65,6 +65,7 @@ private void InitializeTestApplications(IEnumerable modules) else // If one test app has IsTestingPlatformApplication set to false, then we will not run any of the test apps { _areTestingPlatformApplications = false; + return; } } } From def3cb81a19aeab2f1cefd167009b40c64b9c1e5 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Mon, 6 Jan 2025 14:56:23 +0100 Subject: [PATCH 08/36] Update method name --- src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs index ecf2fe52ed92..bd7abe3df8e8 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs @@ -89,7 +89,7 @@ public int Run(ParseResult parseResult) } else { - bool allowBinLog = BinLogEnabled(_args); + bool allowBinLog = IsBinLogEnabled(_args); if (!RunMSBuild(parseResult, allowBinLog)) { return ExitCodes.GenericFailure; @@ -148,7 +148,7 @@ private bool RunMSBuild(ParseResult parseResult, bool allowBinLog) return true; } - private static bool BinLogEnabled(List args) + private static bool IsBinLogEnabled(List args) { var binLog = args.FirstOrDefault(arg => arg.StartsWith("-bl", StringComparison.OrdinalIgnoreCase)); From 3b75ca81bc84e08e27c3e2e57858316623f956a6 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Mon, 6 Jan 2025 16:00:33 +0100 Subject: [PATCH 09/36] Use aggregator overload in ProcessProjectsInParallel() --- .../commands/dotnet-test/MSBuildHandler.cs | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs index 94e77f4580be..0037d39413ac 100644 --- a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs +++ b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs @@ -114,19 +114,27 @@ private void ProcessProjectsInParallel(IEnumerable projects, bool allowB { bool allProjectsRestored = true; - Parallel.ForEach(projects, new ParallelOptions { MaxDegreeOfParallelism = _degreeOfParallelism }, project => - { - var (relatedProjects, isRestored) = GetProjectPropertiesInternal(project, allowBinLog); - foreach (var relatedProject in relatedProjects) + Parallel.ForEach( + projects, + new ParallelOptions { MaxDegreeOfParallelism = _degreeOfParallelism }, + () => true, + (project, state, localRestored) => { - allProjects.Add(relatedProject); - } + var (relatedProjects, isRestored) = GetProjectPropertiesInternal(project, allowBinLog); + foreach (var relatedProject in relatedProjects) + { + allProjects.Add(relatedProject); + } - if (!isRestored) + return localRestored && isRestored; + }, + localRestored => { - allProjectsRestored = false; - } - }); + if (!localRestored) + { + allProjectsRestored = false; + } + }); restored = allProjectsRestored; } From 8e3b723228c844a48b3c2bc64d65ea703e1681ef Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Mon, 6 Jan 2025 16:46:00 +0100 Subject: [PATCH 10/36] Apply some comments --- .../dotnet-test/LocalizableStrings.resx | 3 +++ .../dotnet-test/SolutionAndProjectUtility.cs | 22 ++++++++++++++++--- .../dotnet-test/TestingPlatformCommand.cs | 2 +- .../dotnet-test/xlf/LocalizableStrings.cs.xlf | 5 +++++ .../dotnet-test/xlf/LocalizableStrings.de.xlf | 5 +++++ .../dotnet-test/xlf/LocalizableStrings.es.xlf | 5 +++++ .../dotnet-test/xlf/LocalizableStrings.fr.xlf | 5 +++++ .../dotnet-test/xlf/LocalizableStrings.it.xlf | 5 +++++ .../dotnet-test/xlf/LocalizableStrings.ja.xlf | 5 +++++ .../dotnet-test/xlf/LocalizableStrings.ko.xlf | 5 +++++ .../dotnet-test/xlf/LocalizableStrings.pl.xlf | 5 +++++ .../xlf/LocalizableStrings.pt-BR.xlf | 5 +++++ .../dotnet-test/xlf/LocalizableStrings.ru.xlf | 5 +++++ .../dotnet-test/xlf/LocalizableStrings.tr.xlf | 5 +++++ .../xlf/LocalizableStrings.zh-Hans.xlf | 5 +++++ .../xlf/LocalizableStrings.zh-Hant.xlf | 5 +++++ 16 files changed, 88 insertions(+), 4 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx b/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx index 5cd563a8fbbf..92aaef39fd5c 100644 --- a/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx +++ b/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx @@ -338,4 +338,7 @@ Examples: Specify a project or solution file. The current working directory does not contain a project or solution file. + + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index fab9a06695ca..b979a71aecff 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.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.Text.RegularExpressions; using Microsoft.Build.Construction; using Microsoft.DotNet.Tools.Test; @@ -55,17 +54,34 @@ public static IEnumerable GetProjectsFromSolutionFile(string solutionFil private static string[] GetSolutionFilePaths(string directory) { var solutionFiles = Directory.GetFiles(directory, "*.sln*", SearchOption.TopDirectoryOnly) - .Where(f => Regex.IsMatch(f, @"\.(sln|slnx)$")).ToArray(); + .Where(f => IsSolutionFile(f)) + .ToArray(); return solutionFiles; } + private static bool IsSolutionFile(string filePath) + { + var extension = Path.GetExtension(filePath); + return extension.Equals(".sln", StringComparison.OrdinalIgnoreCase) || extension.Equals(".slnx", StringComparison.OrdinalIgnoreCase); + } + private static string[] GetProjectFilePaths(string directory) { var projectFiles = Directory.GetFiles(directory, "*.*proj", SearchOption.TopDirectoryOnly) - .Where(f => Regex.IsMatch(f, @"\.(csproj|vbproj|fsproj)$")).ToArray(); + .Where(f => IsProjectFile(f)) + .ToArray(); return projectFiles; } + + private static bool IsProjectFile(string filePath) + { + var extension = Path.GetExtension(filePath); + return extension.Equals(".csproj", StringComparison.OrdinalIgnoreCase) || + extension.Equals(".vbproj", StringComparison.OrdinalIgnoreCase) || + extension.Equals(".fsproj", StringComparison.OrdinalIgnoreCase) || + extension.Equals(".proj", StringComparison.OrdinalIgnoreCase); + } } } diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs index bd7abe3df8e8..d94bc4311bd7 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs @@ -141,7 +141,7 @@ private bool RunMSBuild(ParseResult parseResult, bool allowBinLog) if (msbuildResult != 0) { - VSTestTrace.SafeWriteTrace(() => $"Get projects properties with MSBuild didn't execute properly with exit code: {msbuildResult}."); + VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMSBuildProjectsPropertiesErrorMessage); return false; } diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.cs.xlf index 703cbe186334..293bf2f6c922 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.cs.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.cs.xlf @@ -118,6 +118,11 @@ Příklady: Invalid test message state '{0}' {0} - test message state + + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + + The max number of test modules that can run in parallel. Maximální počet testovacích modulů, které je možné spustit paralelně. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.de.xlf index 797d23c7ebf9..ffdc27dd1298 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.de.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.de.xlf @@ -118,6 +118,11 @@ Beispiele: Invalid test message state '{0}' {0} - test message state + + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + + The max number of test modules that can run in parallel. Die maximale Anzahl von Testmodulen, die parallel ausgeführt werden können. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.es.xlf index 7251100c775f..880eb9ad38b6 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.es.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.es.xlf @@ -120,6 +120,11 @@ Ejemplos: Invalid test message state '{0}' {0} - test message state + + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + + The max number of test modules that can run in parallel. Número máximo de módulos de prueba que se pueden ejecutar en paralelo. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.fr.xlf index 78d94ff6be5e..809445d7303c 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.fr.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.fr.xlf @@ -118,6 +118,11 @@ Exemples : Invalid test message state '{0}' {0} - test message state + + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + + The max number of test modules that can run in parallel. Nombre maximal de modules de test qui peuvent s’exécuter en parallèle. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.it.xlf index 3c2c08cba064..497ec27cbeb3 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.it.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.it.xlf @@ -118,6 +118,11 @@ Esempi: Invalid test message state '{0}' {0} - test message state + + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + + The max number of test modules that can run in parallel. Numero massimo di moduli di test che possono essere eseguiti in parallelo. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ja.xlf index cc5890fafcea..e569010555c7 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ja.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ja.xlf @@ -118,6 +118,11 @@ Examples: Invalid test message state '{0}' {0} - test message state + + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + + The max number of test modules that can run in parallel. 並列で実行できるテスト モジュールの最大数。 diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ko.xlf index 771f56358fc5..79a632e40db1 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ko.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ko.xlf @@ -118,6 +118,11 @@ Examples: Invalid test message state '{0}' {0} - test message state + + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + + The max number of test modules that can run in parallel. 병렬로 실행할 수 있는 최대 테스트 모듈 수입니다. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pl.xlf index fb5c9739723f..2d5a55fa0b94 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pl.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pl.xlf @@ -118,6 +118,11 @@ Przykłady: Invalid test message state '{0}' {0} - test message state + + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + + The max number of test modules that can run in parallel. Maksymalna liczba modułów testowych, które mogą być uruchamiane równolegle. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pt-BR.xlf index b79ec7fce877..d693799cad63 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pt-BR.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pt-BR.xlf @@ -118,6 +118,11 @@ Exemplos: Invalid test message state '{0}' {0} - test message state + + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + + The max number of test modules that can run in parallel. O número máximo de módulos de teste que podem ser executados em paralelo. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ru.xlf index 868eebdf8fe9..c1d07c22765b 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ru.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ru.xlf @@ -118,6 +118,11 @@ Examples: Invalid test message state '{0}' {0} - test message state + + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + + The max number of test modules that can run in parallel. Максимальное число тестовых модулей, которые могут выполняться параллельно. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.tr.xlf index df133774806c..24501aa00de8 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.tr.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.tr.xlf @@ -118,6 +118,11 @@ Bu bağımsız değişken, birden çok değişken sağlamak için birden çok ke Invalid test message state '{0}' {0} - test message state + + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + + The max number of test modules that can run in parallel. Paralel olarak çalıştırılabilecek test modüllerinin maksimum sayısı. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hans.xlf index a930b18c3a02..379e4b89db5f 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hans.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hans.xlf @@ -118,6 +118,11 @@ Examples: Invalid test message state '{0}' {0} - test message state + + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + + The max number of test modules that can run in parallel. 可并行运行的测试模块的最大数目。 diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hant.xlf index d625ff9e7272..c6c681e48c47 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hant.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hant.xlf @@ -118,6 +118,11 @@ Examples: Invalid test message state '{0}' {0} - test message state + + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + Get projects properties with MSBuild didn't execute properly with exit code: {0}. + + The max number of test modules that can run in parallel. 可平行執行的測試模組數目上限。 From b7b79ef52e15c5a94bf384de91d05ebd292a9bf5 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Tue, 7 Jan 2025 09:37:07 +0100 Subject: [PATCH 11/36] Remove ? --- src/Cli/dotnet/commands/dotnet-test/TestApplication.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs b/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs index decbbd7d587f..1c448328323c 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs @@ -85,7 +85,7 @@ public async Task RunAsync(bool isFilterMode, bool enableHelp, BuiltInOptio 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) From 5349e6ccdec2e38bb48e8a5eebc49910aba78da7 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Tue, 7 Jan 2025 11:25:56 +0100 Subject: [PATCH 12/36] Update GetSolutionFilePaths --- .../commands/dotnet-test/SolutionAndProjectUtility.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index b979a71aecff..7df9802bc4bd 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -53,19 +53,13 @@ public static IEnumerable GetProjectsFromSolutionFile(string solutionFil private static string[] GetSolutionFilePaths(string directory) { - var solutionFiles = Directory.GetFiles(directory, "*.sln*", SearchOption.TopDirectoryOnly) - .Where(f => IsSolutionFile(f)) + var solutionFiles = Directory.GetFiles(directory, "*.sln", SearchOption.TopDirectoryOnly) + .Concat(Directory.GetFiles(directory, "*.slnx", SearchOption.TopDirectoryOnly)) .ToArray(); return solutionFiles; } - private static bool IsSolutionFile(string filePath) - { - var extension = Path.GetExtension(filePath); - return extension.Equals(".sln", StringComparison.OrdinalIgnoreCase) || extension.Equals(".slnx", StringComparison.OrdinalIgnoreCase); - } - private static string[] GetProjectFilePaths(string directory) { var projectFiles = Directory.GetFiles(directory, "*.*proj", SearchOption.TopDirectoryOnly) From cbc3389c48dde8c073290e3f5002bf26d585d7f3 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Tue, 7 Jan 2025 14:43:58 +0100 Subject: [PATCH 13/36] Use Microsoft.VisualStudio.SolutionPersistence namespace to parse solution files --- .../commands/dotnet-test/MSBuildHandler.cs | 15 ++++--- .../dotnet-test/SolutionAndProjectUtility.cs | 45 ++++++++++++++++--- .../commands/dotnet-test/TestCommandParser.cs | 2 +- .../TestingPlatformCommand.Help.cs | 4 +- .../dotnet-test/TestingPlatformCommand.cs | 21 ++++----- 5 files changed, 60 insertions(+), 27 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs index 0037d39413ac..0771182d9808 100644 --- a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs +++ b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs @@ -19,6 +19,7 @@ internal sealed class MSBuildHandler : IDisposable private bool _areTestingPlatformApplications = true; private const string BinLogFileName = "msbuild.binlog"; + private const string Separator = ";"; private static readonly object s_buildLock = new(); public MSBuildHandler(List args, TestApplicationActionQueue actionQueue, int degreeOfParallelism) @@ -28,7 +29,7 @@ public MSBuildHandler(List args, TestApplicationActionQueue actionQueue, _degreeOfParallelism = degreeOfParallelism; } - public int RunWithMSBuild(bool allowBinLog) + public async Task RunWithMSBuild(bool allowBinLog) { bool solutionOrProjectFileFound = SolutionAndProjectUtility.TryGetSolutionOrProjectFilePath(Directory.GetCurrentDirectory(), out string filePath, out bool isSolution); @@ -37,16 +38,16 @@ public int RunWithMSBuild(bool allowBinLog) return ExitCodes.GenericFailure; } - (IEnumerable modules, bool restored) = GetProjectsProperties(filePath, isSolution, allowBinLog); + (IEnumerable modules, bool restored) = await GetProjectsProperties(filePath, isSolution, allowBinLog); InitializeTestApplications(modules); return restored ? ExitCodes.Success : ExitCodes.GenericFailure; } - public int RunWithMSBuild(string filePath, bool allowBinLog) + public async Task RunWithMSBuild(string filePath, bool allowBinLog) { - (IEnumerable modules, bool restored) = GetProjectsProperties(filePath, false, allowBinLog); + (IEnumerable modules, bool restored) = await GetProjectsProperties(filePath, false, allowBinLog); InitializeTestApplications(modules); @@ -84,14 +85,14 @@ public bool EnqueueTestApplications() return true; } - private (IEnumerable, bool Restored) GetProjectsProperties(string solutionOrProjectFilePath, bool isSolution, bool allowBinLog) + private async Task<(IEnumerable, bool Restored)> GetProjectsProperties(string solutionOrProjectFilePath, bool isSolution, bool allowBinLog) { var allProjects = new ConcurrentBag(); bool restored = true; if (isSolution) { - var projects = SolutionAndProjectUtility.GetProjectsFromSolutionFile(solutionOrProjectFilePath); + var projects = await SolutionAndProjectUtility.ParseSolution(solutionOrProjectFilePath); ProcessProjectsInParallel(projects, allowBinLog, allProjects, ref restored); } else @@ -174,7 +175,7 @@ private static IEnumerable ExtractModulesFromProject(Project project) } else { - var frameworks = targetFrameworks.Split(';', StringSplitOptions.RemoveEmptyEntries); + var frameworks = targetFrameworks.Split(Separator, StringSplitOptions.RemoveEmptyEntries); foreach (var framework in frameworks) { project.SetProperty(ProjectProperties.TargetFramework, framework); diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index 7df9802bc4bd..7acfbf84ebe2 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -1,8 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.Build.Construction; using Microsoft.DotNet.Tools.Test; +using Microsoft.VisualStudio.SolutionPersistence.Model; +using Microsoft.VisualStudio.SolutionPersistence.Serializer; namespace Microsoft.DotNet.Cli { @@ -45,17 +46,47 @@ public static bool TryGetSolutionOrProjectFilePath(string directory, out string return false; } - public static IEnumerable GetProjectsFromSolutionFile(string solutionFilePath) + public static async Task> ParseSolution(string solutionFilePath) { - var solutionFile = SolutionFile.Parse(solutionFilePath); - return solutionFile.ProjectsInOrder.Select(project => project.AbsolutePath); + if (string.IsNullOrEmpty(solutionFilePath)) + { + VSTestTrace.SafeWriteTrace(() => $"Solution file path cannot be null or empty: {solutionFilePath}"); + return []; + } + + var projectsPaths = new List(); + SolutionModel solution = null; + + try + { + using var stream = new FileStream(solutionFilePath, FileMode.Open, FileAccess.Read); + string extension = Path.GetExtension(solutionFilePath); + + solution = extension.Equals(".sln", StringComparison.OrdinalIgnoreCase) + ? await SolutionSerializers.SlnFileV12.OpenAsync(stream, CancellationToken.None) + : extension.Equals(".slnx", StringComparison.OrdinalIgnoreCase) + ? await SolutionSerializers.SlnXml.OpenAsync(stream, CancellationToken.None) + : null; + } + catch (Exception ex) + { + VSTestTrace.SafeWriteTrace(() => $"Failed to parse solution file '{solutionFilePath}': {ex.Message}"); + return []; + } + + if (solution is not null) + { + projectsPaths = [.. solution.SolutionProjects.Select(project => project.FilePath)]; + } + + return projectsPaths; } private static string[] GetSolutionFilePaths(string directory) { - var solutionFiles = Directory.GetFiles(directory, "*.sln", SearchOption.TopDirectoryOnly) - .Concat(Directory.GetFiles(directory, "*.slnx", SearchOption.TopDirectoryOnly)) - .ToArray(); + string[] solutionFiles = [ + ..Directory.GetFiles(directory, "*.sln", SearchOption.TopDirectoryOnly), + ..Directory.GetFiles(directory, "*.slnx", SearchOption.TopDirectoryOnly)]; return solutionFiles; } diff --git a/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs b/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs index 5196981af9ae..9c26e21111e2 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs @@ -190,7 +190,7 @@ private static CliCommand ConstructCommand() private static CliCommand GetTestingPlatformCliCommand() { var command = new TestingPlatformCommand("test"); - command.SetAction((parseResult) => command.Run(parseResult)); + command.SetAction(async (parseResult) => await command.Run(parseResult)); command.Options.Add(TestingPlatformOptions.MaxParallelTestModulesOption); command.Options.Add(TestingPlatformOptions.AdditionalMSBuildParametersOption); command.Options.Add(TestingPlatformOptions.TestModulesFilterOption); diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.Help.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.Help.cs index 6499eb074933..838a74530384 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.Help.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.Help.cs @@ -14,11 +14,11 @@ internal partial class TestingPlatformCommand public IEnumerable> CustomHelpLayout() { - yield return (context) => + yield return async (context) => { Console.WriteLine("Waiting for options and extensions..."); - Run(context.ParseResult); + await Run(context.ParseResult); if (_commandLineOptionNameToModuleNames.IsEmpty) { diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs index d94bc4311bd7..03e661cf7421 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs @@ -22,7 +22,7 @@ public TestingPlatformCommand(string name, string description = null) : base(nam TreatUnmatchedTokensAsErrors = false; } - public int Run(ParseResult parseResult) + public async Task Run(ParseResult parseResult) { bool hasFailed = false; try @@ -90,7 +90,8 @@ public int Run(ParseResult parseResult) else { bool allowBinLog = IsBinLogEnabled(_args); - if (!RunMSBuild(parseResult, allowBinLog)) + + if (!await RunMSBuild(parseResult, allowBinLog)) { return ExitCodes.GenericFailure; } @@ -116,9 +117,9 @@ public int Run(ParseResult parseResult) return hasFailed ? ExitCodes.GenericFailure : ExitCodes.Success; } - private bool RunMSBuild(ParseResult parseResult, bool allowBinLog) + private async Task RunMSBuild(ParseResult parseResult, bool allowBinLog) { - int msbuildResult; + int msbuildExitCode; if (parseResult.HasOption(TestingPlatformOptions.ProjectOption)) { @@ -126,22 +127,22 @@ private bool RunMSBuild(ParseResult parseResult, bool allowBinLog) if (!File.Exists(filePath)) { - VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdNonExistentProjectFilePathDescription); + VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdNonExistentProjectFilePathDescription, filePath)); return false; } - msbuildResult = _msBuildConnectionHandler.RunWithMSBuild(filePath, allowBinLog); + msbuildExitCode = await _msBuildConnectionHandler.RunWithMSBuild(filePath, allowBinLog); } else { // If no filter was provided neither the project using --project, // MSBuild will get the test project paths in the current directory - msbuildResult = _msBuildConnectionHandler.RunWithMSBuild(allowBinLog); + msbuildExitCode = await _msBuildConnectionHandler.RunWithMSBuild(allowBinLog); } - if (msbuildResult != 0) + if (msbuildExitCode != ExitCodes.Success) { - VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMSBuildProjectsPropertiesErrorMessage); + VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdMSBuildProjectsPropertiesErrorMessage, msbuildExitCode)); return false; } @@ -272,7 +273,7 @@ private void OnTestProcessExited(object sender, TestProcessExitEventArgs args) return; } - if (args.ExitCode != 0) + if (args.ExitCode != ExitCodes.Success) { VSTestTrace.SafeWriteTrace(() => $"Test Process exited with non-zero exit code: {args.ExitCode}"); } From 9284893c87b681501a8574a272414957bebf14a2 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Tue, 7 Jan 2025 15:46:25 +0100 Subject: [PATCH 14/36] Add GetSlnFileFullPath and GetProjectFileFullPath methods in Extension files --- src/Cli/dotnet/ProjectExtensions.cs | 28 +++++++++ src/Cli/dotnet/SlnFileExtensions.cs | 27 +++++++- .../dotnet-test/SolutionAndProjectUtility.cs | 61 ++++++------------- 3 files changed, 71 insertions(+), 45 deletions(-) diff --git a/src/Cli/dotnet/ProjectExtensions.cs b/src/Cli/dotnet/ProjectExtensions.cs index b5330cfbe244..7a95fc2adeaa 100644 --- a/src/Cli/dotnet/ProjectExtensions.cs +++ b/src/Cli/dotnet/ProjectExtensions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Build.Evaluation; +using Microsoft.DotNet.Cli.Utils; using NuGet.Frameworks; namespace Microsoft.DotNet.Tools.ProjectExtensions @@ -42,5 +43,32 @@ public static IEnumerable GetPropertyCommaSeparatedValues(this Project p .Select((value) => value.Trim()) .Where((value) => !string.IsNullOrEmpty(value)); } + + public static string GetProjectFileFullPath(string projectFileOrDirectory) + { + if (File.Exists(projectFileOrDirectory)) + { + return Path.GetFullPath(projectFileOrDirectory); + } + if (Directory.Exists(projectFileOrDirectory)) + { + string[] files = [ + ..Directory.GetFiles(projectFileOrDirectory, "*.csproj", SearchOption.TopDirectoryOnly), + ..Directory.GetFiles(projectFileOrDirectory, "*.vbproj", SearchOption.TopDirectoryOnly), + ..Directory.GetFiles(projectFileOrDirectory, "*.fsproj", SearchOption.TopDirectoryOnly), + ..Directory.GetFiles(projectFileOrDirectory, "*.proj", SearchOption.TopDirectoryOnly)]; + + if (files.Length == 0) + { + throw new GracefulException(CommonLocalizableStrings.CouldNotFindAnyProjectInDirectory, projectFileOrDirectory); + } + if (files.Length > 1) + { + throw new GracefulException(CommonLocalizableStrings.MoreThanOneProjectInDirectory, projectFileOrDirectory); + } + return Path.GetFullPath(files.Single()); + } + throw new GracefulException(CommonLocalizableStrings.CouldNotFindProjectOrDirectory, projectFileOrDirectory); + } } } diff --git a/src/Cli/dotnet/SlnFileExtensions.cs b/src/Cli/dotnet/SlnFileExtensions.cs index 0a981b9e2429..e35f14fc3842 100644 --- a/src/Cli/dotnet/SlnFileExtensions.cs +++ b/src/Cli/dotnet/SlnFileExtensions.cs @@ -6,7 +6,6 @@ using Microsoft.Build.Execution; using Microsoft.DotNet.Cli.Sln.Internal; using Microsoft.DotNet.Cli.Utils; -using System.Collections.Generic; namespace Microsoft.DotNet.Tools.Common { @@ -89,6 +88,30 @@ public static void AddProject(this SlnFile slnFile, string fullProjectPath, ILis } } + public static string GetSlnFileFullPath(string slnFileOrDirectory) + { + if (File.Exists(slnFileOrDirectory)) + { + return Path.GetFullPath(slnFileOrDirectory); + } + if (Directory.Exists(slnFileOrDirectory)) + { + string[] files = [ + ..Directory.GetFiles(slnFileOrDirectory, "*.sln", SearchOption.TopDirectoryOnly), + ..Directory.GetFiles(slnFileOrDirectory, "*.slnx", SearchOption.TopDirectoryOnly)]; + if (files.Length == 0) + { + throw new GracefulException(CommonLocalizableStrings.CouldNotFindSolutionIn, slnFileOrDirectory); + } + if (files.Length > 1) + { + throw new GracefulException(CommonLocalizableStrings.MoreThanOneSolutionInDirectory, slnFileOrDirectory); + } + return Path.GetFullPath(files.Single()); + } + throw new GracefulException(CommonLocalizableStrings.CouldNotFindSolutionOrDirectory, slnFileOrDirectory); + } + private static bool AreBuildConfigurationsApplicable(string projectTypeGuid) { return !projectTypeGuid.Equals(ProjectTypeGuids.SharedProjectGuid, StringComparison.OrdinalIgnoreCase); @@ -271,7 +294,7 @@ private static void AddSolutionFolders(this SlnFile slnFile, SlnProject slnProje else { - if(HasDuplicateNameForSameValueOfNestedProjects(nestedProjectsSection, dir, parentDirGuid, slnFile.Projects)) + if (HasDuplicateNameForSameValueOfNestedProjects(nestedProjectsSection, dir, parentDirGuid, slnFile.Projects)) { throw new GracefulException(CommonLocalizableStrings.SolutionFolderAlreadyContainsProject, slnFile.FullPath, slnProject.Name, slnFile.Projects.FirstOrDefault(p => p.Id == parentDirGuid).Name); } diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index 7acfbf84ebe2..9be05384806b 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.DotNet.Tools.Common; +using Microsoft.DotNet.Tools.ProjectExtensions; using Microsoft.DotNet.Tools.Test; using Microsoft.VisualStudio.SolutionPersistence.Model; using Microsoft.VisualStudio.SolutionPersistence.Serializer; @@ -14,35 +16,35 @@ public static bool TryGetSolutionOrProjectFilePath(string directory, out string solutionOrProjectFilePath = string.Empty; isSolution = false; - // Get all solution files in the specified directory - var solutionFiles = GetSolutionFilePaths(directory); - // Get all project files in the specified directory - var projectFiles = GetProjectFilePaths(directory); + // Get solution file in the specified directory + var solutionFile = SlnFileExtensions.GetSlnFileFullPath(directory); + // Get project file in the specified directory + var projectFile = ProjectExtensions.GetProjectFileFullPath(directory); - // If both solution files and project files are found, return false - if (solutionFiles.Length > 0 && projectFiles.Length > 0) + if (string.IsNullOrEmpty(solutionFile) && string.IsNullOrEmpty(projectFile)) { - VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMultipleProjectOrSolutionFilesErrorMessage); + // If no solution or project files are found, return false + VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdNoProjectOrSolutionFileErrorMessage); return false; } - // If exactly one solution file is found, return the solution file path - if (solutionFiles.Length == 1) + // If the solution file is found, return the solution file path + if (!string.IsNullOrEmpty(solutionFile)) { - solutionOrProjectFilePath = solutionFiles[0]; + solutionOrProjectFilePath = solutionFile; isSolution = true; return true; } - // If exactly one project file is found, return the project file path - if (projectFiles.Length == 1) + // If the project file is found, return the project file path + if (projectFile.Length == 1) { - solutionOrProjectFilePath = projectFiles[0]; + solutionOrProjectFilePath = projectFile; return true; } - // If no solution or project files are found, return false - VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdNoProjectOrSolutionFileErrorMessage); + // If both solution file and project file are found, return false + VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMultipleProjectOrSolutionFilesErrorMessage); return false; } @@ -76,37 +78,10 @@ public static async Task> ParseSolution(string solutionFileP if (solution is not null) { - projectsPaths = [.. solution.SolutionProjects.Select(project => project.FilePath)]; + projectsPaths = [.. solution.SolutionProjects.Select(project => Path.GetFullPath(project.FilePath))]; } return projectsPaths; } - - private static string[] GetSolutionFilePaths(string directory) - { - string[] solutionFiles = [ - ..Directory.GetFiles(directory, "*.sln", SearchOption.TopDirectoryOnly), - ..Directory.GetFiles(directory, "*.slnx", SearchOption.TopDirectoryOnly)]; - - return solutionFiles; - } - - private static string[] GetProjectFilePaths(string directory) - { - var projectFiles = Directory.GetFiles(directory, "*.*proj", SearchOption.TopDirectoryOnly) - .Where(f => IsProjectFile(f)) - .ToArray(); - - return projectFiles; - } - - private static bool IsProjectFile(string filePath) - { - var extension = Path.GetExtension(filePath); - return extension.Equals(".csproj", StringComparison.OrdinalIgnoreCase) || - extension.Equals(".vbproj", StringComparison.OrdinalIgnoreCase) || - extension.Equals(".fsproj", StringComparison.OrdinalIgnoreCase) || - extension.Equals(".proj", StringComparison.OrdinalIgnoreCase); - } } } From 6b42f484b2f9b5c29ecf974fa10b846ece84172b Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Tue, 7 Jan 2025 16:43:45 +0100 Subject: [PATCH 15/36] Refactor --- src/Cli/dotnet/ProjectExtensions.cs | 28 ----- src/Cli/dotnet/SlnFileExtensions.cs | 24 ----- .../commands/dotnet-test/MSBuildHandler.cs | 4 +- .../dotnet-test/SolutionAndProjectUtility.cs | 101 +++++++++++++----- 4 files changed, 75 insertions(+), 82 deletions(-) diff --git a/src/Cli/dotnet/ProjectExtensions.cs b/src/Cli/dotnet/ProjectExtensions.cs index 7a95fc2adeaa..b5330cfbe244 100644 --- a/src/Cli/dotnet/ProjectExtensions.cs +++ b/src/Cli/dotnet/ProjectExtensions.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Build.Evaluation; -using Microsoft.DotNet.Cli.Utils; using NuGet.Frameworks; namespace Microsoft.DotNet.Tools.ProjectExtensions @@ -43,32 +42,5 @@ public static IEnumerable GetPropertyCommaSeparatedValues(this Project p .Select((value) => value.Trim()) .Where((value) => !string.IsNullOrEmpty(value)); } - - public static string GetProjectFileFullPath(string projectFileOrDirectory) - { - if (File.Exists(projectFileOrDirectory)) - { - return Path.GetFullPath(projectFileOrDirectory); - } - if (Directory.Exists(projectFileOrDirectory)) - { - string[] files = [ - ..Directory.GetFiles(projectFileOrDirectory, "*.csproj", SearchOption.TopDirectoryOnly), - ..Directory.GetFiles(projectFileOrDirectory, "*.vbproj", SearchOption.TopDirectoryOnly), - ..Directory.GetFiles(projectFileOrDirectory, "*.fsproj", SearchOption.TopDirectoryOnly), - ..Directory.GetFiles(projectFileOrDirectory, "*.proj", SearchOption.TopDirectoryOnly)]; - - if (files.Length == 0) - { - throw new GracefulException(CommonLocalizableStrings.CouldNotFindAnyProjectInDirectory, projectFileOrDirectory); - } - if (files.Length > 1) - { - throw new GracefulException(CommonLocalizableStrings.MoreThanOneProjectInDirectory, projectFileOrDirectory); - } - return Path.GetFullPath(files.Single()); - } - throw new GracefulException(CommonLocalizableStrings.CouldNotFindProjectOrDirectory, projectFileOrDirectory); - } } } diff --git a/src/Cli/dotnet/SlnFileExtensions.cs b/src/Cli/dotnet/SlnFileExtensions.cs index e35f14fc3842..22b5cc48c5a8 100644 --- a/src/Cli/dotnet/SlnFileExtensions.cs +++ b/src/Cli/dotnet/SlnFileExtensions.cs @@ -88,30 +88,6 @@ public static void AddProject(this SlnFile slnFile, string fullProjectPath, ILis } } - public static string GetSlnFileFullPath(string slnFileOrDirectory) - { - if (File.Exists(slnFileOrDirectory)) - { - return Path.GetFullPath(slnFileOrDirectory); - } - if (Directory.Exists(slnFileOrDirectory)) - { - string[] files = [ - ..Directory.GetFiles(slnFileOrDirectory, "*.sln", SearchOption.TopDirectoryOnly), - ..Directory.GetFiles(slnFileOrDirectory, "*.slnx", SearchOption.TopDirectoryOnly)]; - if (files.Length == 0) - { - throw new GracefulException(CommonLocalizableStrings.CouldNotFindSolutionIn, slnFileOrDirectory); - } - if (files.Length > 1) - { - throw new GracefulException(CommonLocalizableStrings.MoreThanOneSolutionInDirectory, slnFileOrDirectory); - } - return Path.GetFullPath(files.Single()); - } - throw new GracefulException(CommonLocalizableStrings.CouldNotFindSolutionOrDirectory, slnFileOrDirectory); - } - private static bool AreBuildConfigurationsApplicable(string projectTypeGuid) { return !projectTypeGuid.Equals(ProjectTypeGuids.SharedProjectGuid, StringComparison.OrdinalIgnoreCase); diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs index 0771182d9808..a4aa24e8a543 100644 --- a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs +++ b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs @@ -31,14 +31,14 @@ public MSBuildHandler(List args, TestApplicationActionQueue actionQueue, public async Task RunWithMSBuild(bool allowBinLog) { - bool solutionOrProjectFileFound = SolutionAndProjectUtility.TryGetSolutionOrProjectFilePath(Directory.GetCurrentDirectory(), out string filePath, out bool isSolution); + bool solutionOrProjectFileFound = SolutionAndProjectUtility.TryGetProjectOrSolutionFilePath(Directory.GetCurrentDirectory(), out string projectOrSolutionFilePath, out bool isSolution); if (!solutionOrProjectFileFound) { return ExitCodes.GenericFailure; } - (IEnumerable modules, bool restored) = await GetProjectsProperties(filePath, isSolution, allowBinLog); + (IEnumerable modules, bool restored) = await GetProjectsProperties(projectOrSolutionFilePath, isSolution, allowBinLog); InitializeTestApplications(modules); diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index 9be05384806b..6808b8300141 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -1,8 +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 Microsoft.DotNet.Tools.Common; -using Microsoft.DotNet.Tools.ProjectExtensions; +using Microsoft.DotNet.Tools; using Microsoft.DotNet.Tools.Test; using Microsoft.VisualStudio.SolutionPersistence.Model; using Microsoft.VisualStudio.SolutionPersistence.Serializer; @@ -11,43 +10,89 @@ namespace Microsoft.DotNet.Cli { internal static class SolutionAndProjectUtility { - public static bool TryGetSolutionOrProjectFilePath(string directory, out string solutionOrProjectFilePath, out bool isSolution) + public static bool TryGetProjectOrSolutionFilePath(string directory, out string projectOrSolutionFilePath, out bool isSolution) { - solutionOrProjectFilePath = string.Empty; + projectOrSolutionFilePath = string.Empty; isSolution = false; - // Get solution file in the specified directory - var solutionFile = SlnFileExtensions.GetSlnFileFullPath(directory); - // Get project file in the specified directory - var projectFile = ProjectExtensions.GetProjectFileFullPath(directory); - - if (string.IsNullOrEmpty(solutionFile) && string.IsNullOrEmpty(projectFile)) + if (Directory.Exists(directory)) { - // If no solution or project files are found, return false - VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdNoProjectOrSolutionFileErrorMessage); - return false; - } + string[] possibleSolutionPaths = [ + ..Directory.GetFiles(directory, "*.sln", SearchOption.TopDirectoryOnly), + ..Directory.GetFiles(directory, "*.slnx", SearchOption.TopDirectoryOnly)]; - // If the solution file is found, return the solution file path - if (!string.IsNullOrEmpty(solutionFile)) - { - solutionOrProjectFilePath = solutionFile; - isSolution = true; - return true; - } + // If more than a single sln file is found, an error is thrown since we can't determine which one to choose. + if (possibleSolutionPaths.Count() > 1) + { + VSTestTrace.SafeWriteTrace(() => string.Format(CommonLocalizableStrings.MoreThanOneSolutionInDirectory, directory)); + return false; + } + // If a single solution is found, use it. + else if (possibleSolutionPaths.Count() == 1) + { + // Get project file paths to check if there are any projects in the directory + string[] possibleProjectPaths = GetProjectFilePaths(directory); - // If the project file is found, return the project file path - if (projectFile.Length == 1) - { - solutionOrProjectFilePath = projectFile; - return true; + if (possibleProjectPaths.Count() == 0) + { + projectOrSolutionFilePath = possibleSolutionPaths[0]; + isSolution = true; + return true; + } + else // If both solution and project files are found, return false + { + VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMultipleProjectOrSolutionFilesErrorMessage); + return false; + } + } + // If no solutions are found, look for a project file + else + { + string[] possibleProjectPath = GetProjectFilePaths(directory); + + // No projects found throws an error that no sln nor projects were found + if (possibleProjectPath.Count() == 0) + { + VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdNoProjectOrSolutionFileErrorMessage); + return false; + } + // A single project found, use it + else if (possibleProjectPath.Count() == 1) + { + projectOrSolutionFilePath = possibleProjectPath[0]; + return true; + } + // More than one project found. Not sure which one to choose + else + { + VSTestTrace.SafeWriteTrace(() => string.Format(CommonLocalizableStrings.MoreThanOneProjectInDirectory, directory)); + return false; + } + } } - // If both solution file and project file are found, return false - VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMultipleProjectOrSolutionFilesErrorMessage); return false; } + + private static string[] GetProjectFilePaths(string directory) + { + var projectFiles = Directory.GetFiles(directory, "*.*proj", SearchOption.TopDirectoryOnly) + .Where(f => IsProjectFile(f)) + .ToArray(); + + return projectFiles; + } + + private static bool IsProjectFile(string filePath) + { + var extension = Path.GetExtension(filePath); + return extension.Equals(".csproj", StringComparison.OrdinalIgnoreCase) || + extension.Equals(".vbproj", StringComparison.OrdinalIgnoreCase) || + extension.Equals(".fsproj", StringComparison.OrdinalIgnoreCase) || + extension.Equals(".proj", StringComparison.OrdinalIgnoreCase); + } + public static async Task> ParseSolution(string solutionFilePath) { if (string.IsNullOrEmpty(solutionFilePath)) From 51c74243266dc7670150eb0890b877dd5ddfac72 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Tue, 7 Jan 2025 16:49:34 +0100 Subject: [PATCH 16/36] Update ParseSolution to use GetSerializerByMoniker --- .../dotnet-test/SolutionAndProjectUtility.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index 6808b8300141..abe33a3abac8 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -3,6 +3,7 @@ using Microsoft.DotNet.Tools; using Microsoft.DotNet.Tools.Test; +using Microsoft.VisualStudio.SolutionPersistence; using Microsoft.VisualStudio.SolutionPersistence.Model; using Microsoft.VisualStudio.SolutionPersistence.Serializer; @@ -106,14 +107,9 @@ public static async Task> ParseSolution(string solutionFileP try { - using var stream = new FileStream(solutionFilePath, FileMode.Open, FileAccess.Read); - string extension = Path.GetExtension(solutionFilePath); - - solution = extension.Equals(".sln", StringComparison.OrdinalIgnoreCase) - ? await SolutionSerializers.SlnFileV12.OpenAsync(stream, CancellationToken.None) - : extension.Equals(".slnx", StringComparison.OrdinalIgnoreCase) - ? await SolutionSerializers.SlnXml.OpenAsync(stream, CancellationToken.None) - : null; + solution = SolutionSerializers.GetSerializerByMoniker(solutionFilePath) is ISolutionSerializer serializer + ? await serializer.OpenAsync(solutionFilePath, CancellationToken.None) + : null; } catch (Exception ex) { From 082f26083e0f5bd38630d1500062b58b46aa1d0f Mon Sep 17 00:00:00 2001 From: Mariam Abdullah <122357303+mariam-abdulla@users.noreply.github.com> Date: Wed, 8 Jan 2025 12:31:36 +0100 Subject: [PATCH 17/36] Update src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Amaury Levé --- src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs index 03e661cf7421..36746d21b190 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs @@ -96,7 +96,7 @@ public async Task Run(ParseResult parseResult) return ExitCodes.GenericFailure; } - // If not all test projects have IsTestProject and IsTestingPlatformApplication proprties set to true, we will simply return + // If not all test projects have IsTestProject and IsTestingPlatformApplication properties set to true, we will simply return if (!_msBuildConnectionHandler.EnqueueTestApplications()) { VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdUnsupportedVSTestTestApplicationsDescription); From ae87351e70629d0fbdd7eb18a37f566e3de2046e Mon Sep 17 00:00:00 2001 From: Mariam Abdullah <122357303+mariam-abdulla@users.noreply.github.com> Date: Wed, 8 Jan 2025 12:31:46 +0100 Subject: [PATCH 18/36] Update src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Amaury Levé --- .../dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index abe33a3abac8..11669a8d3f39 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -29,7 +29,7 @@ public static bool TryGetProjectOrSolutionFilePath(string directory, out string return false; } // If a single solution is found, use it. - else if (possibleSolutionPaths.Count() == 1) + else if (possibleSolutionPaths.Length == 1) { // Get project file paths to check if there are any projects in the directory string[] possibleProjectPaths = GetProjectFilePaths(directory); From 3e26cd3f7b10113ec197d9aa85add3c30d062661 Mon Sep 17 00:00:00 2001 From: Mariam Abdullah <122357303+mariam-abdulla@users.noreply.github.com> Date: Wed, 8 Jan 2025 12:31:54 +0100 Subject: [PATCH 19/36] Update src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Amaury Levé --- .../dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index 11669a8d3f39..6ee4908498a7 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -58,7 +58,7 @@ public static bool TryGetProjectOrSolutionFilePath(string directory, out string return false; } // A single project found, use it - else if (possibleProjectPath.Count() == 1) + else if (possibleProjectPath.Length == 1) { projectOrSolutionFilePath = possibleProjectPath[0]; return true; From 31593b005e6a4b0ffa50a68fd8333a1ecf01d2d7 Mon Sep 17 00:00:00 2001 From: Mariam Abdullah <122357303+mariam-abdulla@users.noreply.github.com> Date: Wed, 8 Jan 2025 12:32:04 +0100 Subject: [PATCH 20/36] Update src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Amaury Levé --- .../dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index 6ee4908498a7..bf105eb53e38 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -52,7 +52,7 @@ public static bool TryGetProjectOrSolutionFilePath(string directory, out string string[] possibleProjectPath = GetProjectFilePaths(directory); // No projects found throws an error that no sln nor projects were found - if (possibleProjectPath.Count() == 0) + if (possibleProjectPath.Length == 0) { VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdNoProjectOrSolutionFileErrorMessage); return false; From f7811645906defb7de6ae02d1ef13ed40ed17a90 Mon Sep 17 00:00:00 2001 From: Mariam Abdullah <122357303+mariam-abdulla@users.noreply.github.com> Date: Wed, 8 Jan 2025 12:32:25 +0100 Subject: [PATCH 21/36] Update src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Amaury Levé --- .../dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index bf105eb53e38..206b51a6d9ed 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -34,7 +34,7 @@ public static bool TryGetProjectOrSolutionFilePath(string directory, out string // Get project file paths to check if there are any projects in the directory string[] possibleProjectPaths = GetProjectFilePaths(directory); - if (possibleProjectPaths.Count() == 0) + if (possibleProjectPaths.Length == 0) { projectOrSolutionFilePath = possibleSolutionPaths[0]; isSolution = true; From 3c3d56b205d96d7fd4c0c61e9aead41be51e1caa Mon Sep 17 00:00:00 2001 From: Mariam Abdullah <122357303+mariam-abdulla@users.noreply.github.com> Date: Wed, 8 Jan 2025 13:49:18 +0100 Subject: [PATCH 22/36] Update src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Amaury Levé --- .../dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index 206b51a6d9ed..baf753f457e6 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -23,7 +23,7 @@ public static bool TryGetProjectOrSolutionFilePath(string directory, out string ..Directory.GetFiles(directory, "*.slnx", SearchOption.TopDirectoryOnly)]; // If more than a single sln file is found, an error is thrown since we can't determine which one to choose. - if (possibleSolutionPaths.Count() > 1) + if (possibleSolutionPaths.Length > 1) { VSTestTrace.SafeWriteTrace(() => string.Format(CommonLocalizableStrings.MoreThanOneSolutionInDirectory, directory)); return false; From 55d247df4ba31ae945cf08b288707b265483a05c Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Wed, 8 Jan 2025 13:49:29 +0100 Subject: [PATCH 23/36] Apply comments --- .../dotnet-test/SolutionAndProjectUtility.cs | 90 +++++++++---------- .../dotnet-test/TestingPlatformCommand.cs | 12 +-- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index 206b51a6d9ed..248429a6c44e 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -16,63 +16,63 @@ public static bool TryGetProjectOrSolutionFilePath(string directory, out string projectOrSolutionFilePath = string.Empty; isSolution = false; - if (Directory.Exists(directory)) + if (!Directory.Exists(directory)) { - string[] possibleSolutionPaths = [ - ..Directory.GetFiles(directory, "*.sln", SearchOption.TopDirectoryOnly), + return false; + } + + string[] possibleSolutionPaths = [ + ..Directory.GetFiles(directory, "*.sln", SearchOption.TopDirectoryOnly), ..Directory.GetFiles(directory, "*.slnx", SearchOption.TopDirectoryOnly)]; - // If more than a single sln file is found, an error is thrown since we can't determine which one to choose. - if (possibleSolutionPaths.Count() > 1) + // If more than a single sln file is found, an error is thrown since we can't determine which one to choose. + if (possibleSolutionPaths.Count() > 1) + { + VSTestTrace.SafeWriteTrace(() => string.Format(CommonLocalizableStrings.MoreThanOneSolutionInDirectory, directory)); + return false; + } + // If a single solution is found, use it. + else if (possibleSolutionPaths.Length == 1) + { + // Get project file paths to check if there are any projects in the directory + string[] possibleProjectPaths = GetProjectFilePaths(directory); + + if (possibleProjectPaths.Length == 0) + { + projectOrSolutionFilePath = possibleSolutionPaths[0]; + isSolution = true; + return true; + } + else // If both solution and project files are found, return false + { + VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMultipleProjectOrSolutionFilesErrorMessage); + return false; + } + } + // If no solutions are found, look for a project file + else + { + string[] possibleProjectPath = GetProjectFilePaths(directory); + + // No projects found throws an error that no sln nor projects were found + if (possibleProjectPath.Length == 0) { - VSTestTrace.SafeWriteTrace(() => string.Format(CommonLocalizableStrings.MoreThanOneSolutionInDirectory, directory)); + VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdNoProjectOrSolutionFileErrorMessage); return false; } - // If a single solution is found, use it. - else if (possibleSolutionPaths.Length == 1) + // A single project found, use it + else if (possibleProjectPath.Length == 1) { - // Get project file paths to check if there are any projects in the directory - string[] possibleProjectPaths = GetProjectFilePaths(directory); - - if (possibleProjectPaths.Length == 0) - { - projectOrSolutionFilePath = possibleSolutionPaths[0]; - isSolution = true; - return true; - } - else // If both solution and project files are found, return false - { - VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMultipleProjectOrSolutionFilesErrorMessage); - return false; - } + projectOrSolutionFilePath = possibleProjectPath[0]; + return true; } - // If no solutions are found, look for a project file + // More than one project found. Not sure which one to choose else { - string[] possibleProjectPath = GetProjectFilePaths(directory); - - // No projects found throws an error that no sln nor projects were found - if (possibleProjectPath.Length == 0) - { - VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdNoProjectOrSolutionFileErrorMessage); - return false; - } - // A single project found, use it - else if (possibleProjectPath.Length == 1) - { - projectOrSolutionFilePath = possibleProjectPath[0]; - return true; - } - // More than one project found. Not sure which one to choose - else - { - VSTestTrace.SafeWriteTrace(() => string.Format(CommonLocalizableStrings.MoreThanOneProjectInDirectory, directory)); - return false; - } + VSTestTrace.SafeWriteTrace(() => string.Format(CommonLocalizableStrings.MoreThanOneProjectInDirectory, directory)); + return false; } } - - return false; } diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs index 36746d21b190..9586ae5933e4 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs @@ -12,7 +12,7 @@ internal partial class TestingPlatformCommand : CliCommand, ICustomHelp { private readonly ConcurrentBag _testApplications = []; - private MSBuildHandler _msBuildConnectionHandler; + private MSBuildHandler _msBuildHandler; private TestModulesFilterHandler _testModulesFilterHandler; private TestApplicationActionQueue _actionQueue; private List _args; @@ -77,7 +77,7 @@ public async Task Run(ParseResult parseResult) } _args = [.. parseResult.UnmatchedTokens]; - _msBuildConnectionHandler = new(_args, _actionQueue, degreeOfParallelism); + _msBuildHandler = new(_args, _actionQueue, degreeOfParallelism); _testModulesFilterHandler = new(_args, _actionQueue); if (parseResult.HasOption(TestingPlatformOptions.TestModulesFilterOption)) @@ -97,7 +97,7 @@ public async Task Run(ParseResult parseResult) } // If not all test projects have IsTestProject and IsTestingPlatformApplication properties set to true, we will simply return - if (!_msBuildConnectionHandler.EnqueueTestApplications()) + if (!_msBuildHandler.EnqueueTestApplications()) { VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdUnsupportedVSTestTestApplicationsDescription); return ExitCodes.GenericFailure; @@ -131,13 +131,13 @@ private async Task RunMSBuild(ParseResult parseResult, bool allowBinLog) return false; } - msbuildExitCode = await _msBuildConnectionHandler.RunWithMSBuild(filePath, allowBinLog); + msbuildExitCode = await _msBuildHandler.RunWithMSBuild(filePath, allowBinLog); } else { // If no filter was provided neither the project using --project, // MSBuild will get the test project paths in the current directory - msbuildExitCode = await _msBuildConnectionHandler.RunWithMSBuild(allowBinLog); + msbuildExitCode = await _msBuildHandler.RunWithMSBuild(allowBinLog); } if (msbuildExitCode != ExitCodes.Success) @@ -166,7 +166,7 @@ private static bool IsBinLogEnabled(List args) private void CleanUp() { - _msBuildConnectionHandler.Dispose(); + _msBuildHandler.Dispose(); foreach (var testApplication in _testApplications) { testApplication.Dispose(); From 41323d46c91dcef1de82839490f86498236010c3 Mon Sep 17 00:00:00 2001 From: Mariam Abdullah <122357303+mariam-abdulla@users.noreply.github.com> Date: Wed, 8 Jan 2025 13:53:30 +0100 Subject: [PATCH 24/36] Update src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Amaury Levé --- .../dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index 7667ab930183..f050f135fcf8 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -78,8 +78,8 @@ public static bool TryGetProjectOrSolutionFilePath(string directory, out string private static string[] GetProjectFilePaths(string directory) { - var projectFiles = Directory.GetFiles(directory, "*.*proj", SearchOption.TopDirectoryOnly) - .Where(f => IsProjectFile(f)) + var projectFiles = Directory.EnumerateFiles(directory, "*.*proj", SearchOption.TopDirectoryOnly) + .Where(IsProjectFile) .ToArray(); return projectFiles; From a774524a4c379397ac3c4298394731a351e8e866 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Wed, 8 Jan 2025 14:00:19 +0100 Subject: [PATCH 25/36] Use Lock --- src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs index a4aa24e8a543..13b819b4d075 100644 --- a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs +++ b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs @@ -20,7 +20,7 @@ internal sealed class MSBuildHandler : IDisposable private const string BinLogFileName = "msbuild.binlog"; private const string Separator = ";"; - private static readonly object s_buildLock = new(); + private static readonly Lock buildLock = new(); public MSBuildHandler(List args, TestApplicationActionQueue actionQueue, int degreeOfParallelism) { @@ -212,7 +212,7 @@ private static BuildResult RestoreProject(string projectFilePath, bool allowBinL var buildRequestData = new BuildRequestData(projectFilePath, new Dictionary(), null, [CliConstants.RestoreCommand], null); BuildResult buildResult; - lock (s_buildLock) + lock (buildLock) { buildResult = BuildManager.DefaultBuildManager.Build(parameters, buildRequestData); } From 3f66a694c5b163c4428f30c7f8b89fe4da747e08 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Wed, 8 Jan 2025 16:56:15 +0100 Subject: [PATCH 26/36] Update -bl logic --- .../commands/dotnet-test/MSBuildHandler.cs | 69 +++++++++++++++---- .../dotnet-test/TestingPlatformCommand.cs | 25 ++----- 2 files changed, 60 insertions(+), 34 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs index 13b819b4d075..95a5ab365102 100644 --- a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs +++ b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs @@ -29,7 +29,7 @@ public MSBuildHandler(List args, TestApplicationActionQueue actionQueue, _degreeOfParallelism = degreeOfParallelism; } - public async Task RunWithMSBuild(bool allowBinLog) + public async Task RunWithMSBuild() { bool solutionOrProjectFileFound = SolutionAndProjectUtility.TryGetProjectOrSolutionFilePath(Directory.GetCurrentDirectory(), out string projectOrSolutionFilePath, out bool isSolution); @@ -38,16 +38,16 @@ public async Task RunWithMSBuild(bool allowBinLog) return ExitCodes.GenericFailure; } - (IEnumerable modules, bool restored) = await GetProjectsProperties(projectOrSolutionFilePath, isSolution, allowBinLog); + (IEnumerable modules, bool restored) = await GetProjectsProperties(projectOrSolutionFilePath, isSolution); InitializeTestApplications(modules); return restored ? ExitCodes.Success : ExitCodes.GenericFailure; } - public async Task RunWithMSBuild(string filePath, bool allowBinLog) + public async Task RunWithMSBuild(string filePath) { - (IEnumerable modules, bool restored) = await GetProjectsProperties(filePath, false, allowBinLog); + (IEnumerable modules, bool restored) = await GetProjectsProperties(filePath, false); InitializeTestApplications(modules); @@ -85,7 +85,7 @@ public bool EnqueueTestApplications() return true; } - private async Task<(IEnumerable, bool Restored)> GetProjectsProperties(string solutionOrProjectFilePath, bool isSolution, bool allowBinLog) + private async Task<(IEnumerable, bool Restored)> GetProjectsProperties(string solutionOrProjectFilePath, bool isSolution) { var allProjects = new ConcurrentBag(); bool restored = true; @@ -93,11 +93,13 @@ public bool EnqueueTestApplications() if (isSolution) { var projects = await SolutionAndProjectUtility.ParseSolution(solutionOrProjectFilePath); - ProcessProjectsInParallel(projects, allowBinLog, allProjects, ref restored); + ProcessProjectsInParallel(projects, allProjects, ref restored); } else { - var (relatedProjects, isProjectBuilt) = GetProjectPropertiesInternal(solutionOrProjectFilePath, allowBinLog); + bool allowBinLog = IsBinaryLoggerEnabled(_args, out string binLogFileName); + + var (relatedProjects, isProjectBuilt) = GetProjectPropertiesInternal(solutionOrProjectFilePath, allowBinLog, binLogFileName); foreach (var relatedProject in relatedProjects) { allProjects.Add(relatedProject); @@ -111,9 +113,10 @@ public bool EnqueueTestApplications() return (allProjects, restored); } - private void ProcessProjectsInParallel(IEnumerable projects, bool allowBinLog, ConcurrentBag allProjects, ref bool restored) + private void ProcessProjectsInParallel(IEnumerable projects, ConcurrentBag allProjects, ref bool restored) { bool allProjectsRestored = true; + bool allowBinLog = IsBinaryLoggerEnabled(_args, out string binLogFileName); Parallel.ForEach( projects, @@ -121,7 +124,7 @@ private void ProcessProjectsInParallel(IEnumerable projects, bool allowB () => true, (project, state, localRestored) => { - var (relatedProjects, isRestored) = GetProjectPropertiesInternal(project, allowBinLog); + var (relatedProjects, isRestored) = GetProjectPropertiesInternal(project, allowBinLog, binLogFileName); foreach (var relatedProject in relatedProjects) { allProjects.Add(relatedProject); @@ -140,11 +143,11 @@ private void ProcessProjectsInParallel(IEnumerable projects, bool allowB restored = allProjectsRestored; } - private static (IEnumerable Modules, bool Restored) GetProjectPropertiesInternal(string projectFilePath, bool allowBinLog) + private static (IEnumerable Modules, bool Restored) GetProjectPropertiesInternal(string projectFilePath, bool allowBinLog, string binLogFileName) { var projectCollection = new ProjectCollection(); var project = projectCollection.LoadProject(projectFilePath); - var buildResult = RestoreProject(projectFilePath, allowBinLog, projectCollection); + var buildResult = RestoreProject(projectFilePath, projectCollection, allowBinLog, binLogFileName); bool restored = buildResult.OverallResult == BuildResultCode.Success; @@ -193,7 +196,7 @@ private static IEnumerable ExtractModulesFromProject(Project project) return projects; } - private static BuildResult RestoreProject(string projectFilePath, bool allowBinLog, ProjectCollection projectCollection) + private static BuildResult RestoreProject(string projectFilePath, ProjectCollection projectCollection, bool allowBinLog, string binLogFileName) { BuildParameters parameters = new(projectCollection) { @@ -205,7 +208,7 @@ private static BuildResult RestoreProject(string projectFilePath, bool allowBinL parameters.Loggers = parameters.Loggers.Concat([ new BinaryLogger { - Parameters = BinLogFileName + Parameters = binLogFileName } ]); } @@ -220,6 +223,46 @@ private static BuildResult RestoreProject(string projectFilePath, bool allowBinL return buildResult; } + private static bool IsBinaryLoggerEnabled(List args, out string binLogFileName) + { + binLogFileName = BinLogFileName; + + var binLogArgs = new List(); + var otherArgs = new List(); + + foreach (var arg in args) + { + if (arg.StartsWith("/bl:") || arg.Equals("/bl") + || arg.StartsWith("--binaryLogger:") || arg.Equals("--binaryLogger") + || arg.StartsWith("-bl:") || arg.Equals("-bl")) + { + binLogArgs.Add(arg); + + } + else + { + otherArgs.Add(arg); + } + } + + if (binLogArgs.Count > 0) + { + // Remove all BinLog args from the list of args + args.RemoveAll(arg => binLogArgs.Contains(arg)); + + // Get BinLog filename + var binLogArg = binLogArgs.LastOrDefault(); + + if (binLogArg.Contains(':')) + { + binLogFileName = binLogArg.Split(':')[1]; + } + return true; + } + + return false; + } + public void Dispose() { foreach (var testApplication in _testApplications) diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs index 9586ae5933e4..e51f8da017d7 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs @@ -89,9 +89,7 @@ public async Task Run(ParseResult parseResult) } else { - bool allowBinLog = IsBinLogEnabled(_args); - - if (!await RunMSBuild(parseResult, allowBinLog)) + if (!await RunMSBuild(parseResult)) { return ExitCodes.GenericFailure; } @@ -117,7 +115,7 @@ public async Task Run(ParseResult parseResult) return hasFailed ? ExitCodes.GenericFailure : ExitCodes.Success; } - private async Task RunMSBuild(ParseResult parseResult, bool allowBinLog) + private async Task RunMSBuild(ParseResult parseResult) { int msbuildExitCode; @@ -131,13 +129,13 @@ private async Task RunMSBuild(ParseResult parseResult, bool allowBinLog) return false; } - msbuildExitCode = await _msBuildHandler.RunWithMSBuild(filePath, allowBinLog); + msbuildExitCode = await _msBuildHandler.RunWithMSBuild(filePath); } else { // If no filter was provided neither the project using --project, // MSBuild will get the test project paths in the current directory - msbuildExitCode = await _msBuildHandler.RunWithMSBuild(allowBinLog); + msbuildExitCode = await _msBuildHandler.RunWithMSBuild(); } if (msbuildExitCode != ExitCodes.Success) @@ -149,21 +147,6 @@ private async Task RunMSBuild(ParseResult parseResult, bool allowBinLog) return true; } - private static bool IsBinLogEnabled(List args) - { - var binLog = args.FirstOrDefault(arg => arg.StartsWith("-bl", StringComparison.OrdinalIgnoreCase)); - - if (!string.IsNullOrEmpty(binLog)) - { - // We remove it from the args list so that it is not passed to the test application - args.Remove(binLog); - - return true; - } - - return false; - } - private void CleanUp() { _msBuildHandler.Dispose(); From 4f2b76934bfd1af935f60bac9f03c3ae855a2fc6 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Wed, 8 Jan 2025 17:00:10 +0100 Subject: [PATCH 27/36] Remove unused var --- src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs index 95a5ab365102..40c2e4639cae 100644 --- a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs +++ b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs @@ -228,7 +228,6 @@ private static bool IsBinaryLoggerEnabled(List args, out string binLogFi binLogFileName = BinLogFileName; var binLogArgs = new List(); - var otherArgs = new List(); foreach (var arg in args) { @@ -239,10 +238,6 @@ private static bool IsBinaryLoggerEnabled(List args, out string binLogFi binLogArgs.Add(arg); } - else - { - otherArgs.Add(arg); - } } if (binLogArgs.Count > 0) From c3dc4f22f4ae0ebb8f33052ebbe27b5230bd3a23 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Thu, 9 Jan 2025 11:16:38 +0100 Subject: [PATCH 28/36] Support --solution --- .../dotnet-test/LocalizableStrings.resx | 12 +++++++ .../commands/dotnet-test/MSBuildHandler.cs | 11 +++++-- .../dotnet-test/SolutionAndProjectUtility.cs | 5 ++- .../commands/dotnet-test/TestCommandParser.cs | 1 + .../dotnet-test/TestingPlatformCommand.cs | 31 ++++++++++++++++++- .../dotnet-test/TestingPlatformOptions.cs | 6 ++++ 6 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx b/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx index 92aaef39fd5c..857101b03e10 100644 --- a/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx +++ b/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx @@ -187,6 +187,9 @@ Defines the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory. + + Defines the path of the solution file to run. If not specified, it defaults to the current directory. + The directory where the test results will be placed. The specified directory will be created if it does not exist. @@ -332,6 +335,9 @@ Examples: The provided project file path does not exist: {0}. + + The provided solution file path does not exist: {0}. + Specify which project or solution file to use because this folder contains more than one project or solution file. @@ -341,4 +347,10 @@ Examples: Get projects properties with MSBuild didn't execute properly with exit code: {0}. + + The provided solution file has an invalid extension: {0}. + + + The provided project file has an invalid extension: {0}. + diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs index a4aa24e8a543..c7d48bb99e6f 100644 --- a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs +++ b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs @@ -45,9 +45,9 @@ public async Task RunWithMSBuild(bool allowBinLog) return restored ? ExitCodes.Success : ExitCodes.GenericFailure; } - public async Task RunWithMSBuild(string filePath, bool allowBinLog) + public async Task RunWithMSBuild(string filePath, bool isSolution, bool allowBinLog) { - (IEnumerable modules, bool restored) = await GetProjectsProperties(filePath, false, allowBinLog); + (IEnumerable modules, bool restored) = await GetProjectsProperties(filePath, isSolution, allowBinLog); InitializeTestApplications(modules); @@ -92,7 +92,12 @@ public bool EnqueueTestApplications() if (isSolution) { - var projects = await SolutionAndProjectUtility.ParseSolution(solutionOrProjectFilePath); + string fileDirectory = Path.GetDirectoryName(solutionOrProjectFilePath); + string rootDirectory = string.IsNullOrEmpty(fileDirectory) + ? Directory.GetCurrentDirectory() + : fileDirectory; + + var projects = await SolutionAndProjectUtility.ParseSolution(solutionOrProjectFilePath, rootDirectory); ProcessProjectsInParallel(projects, allowBinLog, allProjects, ref restored); } else diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index abe33a3abac8..44b4767eff92 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -75,7 +75,6 @@ public static bool TryGetProjectOrSolutionFilePath(string directory, out string return false; } - private static string[] GetProjectFilePaths(string directory) { var projectFiles = Directory.GetFiles(directory, "*.*proj", SearchOption.TopDirectoryOnly) @@ -94,7 +93,7 @@ private static bool IsProjectFile(string filePath) extension.Equals(".proj", StringComparison.OrdinalIgnoreCase); } - public static async Task> ParseSolution(string solutionFilePath) + public static async Task> ParseSolution(string solutionFilePath, string directory) { if (string.IsNullOrEmpty(solutionFilePath)) { @@ -119,7 +118,7 @@ public static async Task> ParseSolution(string solutionFileP if (solution is not null) { - projectsPaths = [.. solution.SolutionProjects.Select(project => Path.GetFullPath(project.FilePath))]; + projectsPaths = [.. solution.SolutionProjects.Select(project => Path.Combine(directory, project.FilePath))]; } return projectsPaths; diff --git a/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs b/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs index 9c26e21111e2..b3c207c745d1 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs @@ -200,6 +200,7 @@ private static CliCommand GetTestingPlatformCliCommand() command.Options.Add(TestingPlatformOptions.ArchitectureOption); command.Options.Add(TestingPlatformOptions.ConfigurationOption); command.Options.Add(TestingPlatformOptions.ProjectOption); + command.Options.Add(TestingPlatformOptions.SolutionOption); return command; } diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs index 03e661cf7421..2b82d0975095 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.CommandLine; +using System.Diagnostics; using Microsoft.DotNet.Tools.Test; using Microsoft.TemplateEngine.Cli.Commands; @@ -119,11 +120,20 @@ public async Task Run(ParseResult parseResult) private async Task RunMSBuild(ParseResult parseResult, bool allowBinLog) { + Debugger.Launch(); + int msbuildExitCode; if (parseResult.HasOption(TestingPlatformOptions.ProjectOption)) { string filePath = parseResult.GetValue(TestingPlatformOptions.ProjectOption); + string[] extensions = [".proj", ".csproj", ".vbproj", ".fsproj"]; + + if (!extensions.Contains(Path.GetExtension(filePath))) + { + VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdInvalidProjectFileExtensionDescription, filePath)); + return false; + } if (!File.Exists(filePath)) { @@ -131,7 +141,26 @@ private async Task RunMSBuild(ParseResult parseResult, bool allowBinLog) return false; } - msbuildExitCode = await _msBuildConnectionHandler.RunWithMSBuild(filePath, allowBinLog); + msbuildExitCode = await _msBuildConnectionHandler.RunWithMSBuild(filePath, isSolution: false, allowBinLog); + } + else if (parseResult.HasOption(TestingPlatformOptions.SolutionOption)) + { + string filePath = parseResult.GetValue(TestingPlatformOptions.SolutionOption); + string[] extensions = [".sln", ".slnx"]; + + if (!extensions.Contains(Path.GetExtension(filePath))) + { + VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdInvalidSolutionFileExtensionDescription, filePath)); + return false; + } + + if (!File.Exists(filePath)) + { + VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdNonExistentSolutionFilePathDescription, filePath)); + return false; + } + + msbuildExitCode = await _msBuildConnectionHandler.RunWithMSBuild(filePath, isSolution: true, allowBinLog); } else { diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformOptions.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformOptions.cs index f894c4cab66f..62ed471dc9ad 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformOptions.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformOptions.cs @@ -57,5 +57,11 @@ internal static class TestingPlatformOptions Description = LocalizableStrings.CmdProjectDescription, Arity = ArgumentArity.ExactlyOne }; + + public static readonly CliOption SolutionOption = new("--solution") + { + Description = LocalizableStrings.CmdSolutionDescription, + Arity = ArgumentArity.ExactlyOne + }; } } From 517c20aa1699e80747aa62989da186266c49092e Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Thu, 9 Jan 2025 15:53:05 +0100 Subject: [PATCH 29/36] refactor code --- .../commands/dotnet-test/BuiltInOptions.cs | 7 - .../commands/dotnet-test/CliConstants.cs | 15 +- .../dotnet-test/LocalizableStrings.resx | 10 +- .../commands/dotnet-test/MSBuildHandler.cs | 80 +++++++- .../dotnet/commands/dotnet-test/Options.cs | 9 + .../dotnet-test/SolutionAndProjectUtility.cs | 90 ++++---- .../commands/dotnet-test/TestApplication.cs | 51 ++--- .../dotnet-test/TestingPlatformCommand.cs | 193 ++++++------------ .../dotnet-test/xlf/LocalizableStrings.cs.xlf | 12 +- .../dotnet-test/xlf/LocalizableStrings.de.xlf | 12 +- .../dotnet-test/xlf/LocalizableStrings.es.xlf | 12 +- .../dotnet-test/xlf/LocalizableStrings.fr.xlf | 12 +- .../dotnet-test/xlf/LocalizableStrings.it.xlf | 12 +- .../dotnet-test/xlf/LocalizableStrings.ja.xlf | 12 +- .../dotnet-test/xlf/LocalizableStrings.ko.xlf | 12 +- .../dotnet-test/xlf/LocalizableStrings.pl.xlf | 12 +- .../xlf/LocalizableStrings.pt-BR.xlf | 12 +- .../dotnet-test/xlf/LocalizableStrings.ru.xlf | 12 +- .../dotnet-test/xlf/LocalizableStrings.tr.xlf | 12 +- .../xlf/LocalizableStrings.zh-Hans.xlf | 12 +- .../xlf/LocalizableStrings.zh-Hant.xlf | 12 +- 21 files changed, 302 insertions(+), 309 deletions(-) delete mode 100644 src/Cli/dotnet/commands/dotnet-test/BuiltInOptions.cs create mode 100644 src/Cli/dotnet/commands/dotnet-test/Options.cs diff --git a/src/Cli/dotnet/commands/dotnet-test/BuiltInOptions.cs b/src/Cli/dotnet/commands/dotnet-test/BuiltInOptions.cs deleted file mode 100644 index f3bc4c1419c5..000000000000 --- a/src/Cli/dotnet/commands/dotnet-test/BuiltInOptions.cs +++ /dev/null @@ -1,7 +0,0 @@ -// 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 -{ - internal record BuiltInOptions(bool HasNoRestore, bool HasNoBuild, string Configuration, string Architecture); -} diff --git a/src/Cli/dotnet/commands/dotnet-test/CliConstants.cs b/src/Cli/dotnet/commands/dotnet-test/CliConstants.cs index 8b8c4a33838c..8f578b5f31dc 100644 --- a/src/Cli/dotnet/commands/dotnet-test/CliConstants.cs +++ b/src/Cli/dotnet/commands/dotnet-test/CliConstants.cs @@ -13,8 +13,9 @@ internal static class CliConstants public const string ServerOptionValue = "dotnettestcli"; - public const string MSBuildExeName = "MSBuild.dll"; public const string ParametersSeparator = "--"; + public const string SemiColon = ";"; + public const string Colon = ":"; public const string VSTest = "VSTest"; public const string MicrosoftTestingPlatform = "MicrosoftTestingPlatform"; @@ -22,6 +23,18 @@ internal static class CliConstants public const string TestSectionKey = "test"; public const string RestoreCommand = "restore"; + + public static readonly string[] ProjectExtensions = { ".proj", ".csproj", ".vbproj", ".fsproj" }; + public static readonly string[] SolutionExtensions = { ".sln", ".slnx" }; + + public const string ProjectExtensionPattern = "*.*proj"; + public const string SolutionExtensionPattern = "*.sln"; + public const string SolutionXExtensionPattern = "*.slnx"; + + public const string BinLogFileName = "msbuild.binlog"; + + public const string TestingPlatformVsTestBridgeRunSettingsFileEnvVar = "TESTINGPLATFORM_VSTESTBRIDGE_RUNSETTINGS_FILE"; + public const string DLLExtension = "dll"; } internal static class TestStates diff --git a/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx b/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx index 857101b03e10..849ff1fe7348 100644 --- a/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx +++ b/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx @@ -332,11 +332,8 @@ Examples: Test runner not supported: {0}. - - The provided project file path does not exist: {0}. - - - The provided solution file path does not exist: {0}. + + The provided file path does not exist: {0}. Specify which project or solution file to use because this folder contains more than one project or solution file. @@ -353,4 +350,7 @@ Examples: The provided project file has an invalid extension: {0}. + + Specify either the project or solution option. + diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs index 47745b0b0773..8a27eaaf1a4d 100644 --- a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs +++ b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs @@ -6,6 +6,7 @@ using Microsoft.Build.Execution; using Microsoft.Build.Framework; using Microsoft.Build.Logging; +using Microsoft.DotNet.Tools.Test; namespace Microsoft.DotNet.Cli { @@ -14,12 +15,8 @@ internal sealed class MSBuildHandler : IDisposable private readonly List _args; private readonly TestApplicationActionQueue _actionQueue; private readonly int _degreeOfParallelism; - private readonly ConcurrentBag _testApplications = new(); private bool _areTestingPlatformApplications = true; - - private const string BinLogFileName = "msbuild.binlog"; - private const string Separator = ";"; private static readonly Lock buildLock = new(); public MSBuildHandler(List args, TestApplicationActionQueue actionQueue, int degreeOfParallelism) @@ -29,9 +26,70 @@ public MSBuildHandler(List args, TestApplicationActionQueue actionQueue, _degreeOfParallelism = degreeOfParallelism; } - public async Task RunWithMSBuild() + public async Task RunMSBuild(BuildPathsOptions buildPathOptions) + { + int msbuildExitCode; + + if (!string.IsNullOrEmpty(buildPathOptions.ProjectPath) && !string.IsNullOrEmpty(buildPathOptions.SolutionPath)) + { + VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdProjectAndSolutionOptionErrorDescription); + return false; + } + + if (!string.IsNullOrEmpty(buildPathOptions.ProjectPath)) + { + if (!ValidateFilePath(buildPathOptions.ProjectPath, CliConstants.ProjectExtensions, LocalizableStrings.CmdInvalidProjectFileExtensionDescription)) + { + return false; + } + + msbuildExitCode = await RunWithMSBuild(buildPathOptions.ProjectPath, isSolution: false); + } + else if (!string.IsNullOrEmpty(buildPathOptions.SolutionPath)) + { + if (!ValidateFilePath(buildPathOptions.SolutionPath, CliConstants.SolutionExtensions, LocalizableStrings.CmdInvalidSolutionFileExtensionDescription)) + { + return false; + } + + msbuildExitCode = await RunWithMSBuild(buildPathOptions.SolutionPath, isSolution: true); + } + else + { + // If no filter was provided neither the project using --project, + // MSBuild will get the test project paths in the current directory + msbuildExitCode = await RunWithMSBuild(Directory.GetCurrentDirectory()); + } + + if (msbuildExitCode != ExitCodes.Success) + { + VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdMSBuildProjectsPropertiesErrorMessage, msbuildExitCode)); + return false; + } + + return true; + } + + private static bool ValidateFilePath(string filePath, string[] validExtensions, string errorMessage) + { + if (!validExtensions.Contains(Path.GetExtension(filePath))) + { + VSTestTrace.SafeWriteTrace(() => string.Format(errorMessage, filePath)); + return false; + } + + if (!File.Exists(filePath)) + { + VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.@CmdNonExistentFileDescription, filePath)); + return false; + } + + return true; + } + + private async Task RunWithMSBuild(string directory) { - bool solutionOrProjectFileFound = SolutionAndProjectUtility.TryGetProjectOrSolutionFilePath(Directory.GetCurrentDirectory(), out string projectOrSolutionFilePath, out bool isSolution); + bool solutionOrProjectFileFound = SolutionAndProjectUtility.TryGetProjectOrSolutionFilePath(directory, out string projectOrSolutionFilePath, out bool isSolution); if (!solutionOrProjectFileFound) { @@ -45,7 +103,7 @@ public async Task RunWithMSBuild() return restored ? ExitCodes.Success : ExitCodes.GenericFailure; } - public async Task RunWithMSBuild(string filePath, bool isSolution) + private async Task RunWithMSBuild(string filePath, bool isSolution) { (IEnumerable modules, bool restored) = await GetProjectsProperties(filePath, isSolution); @@ -183,7 +241,7 @@ private static IEnumerable ExtractModulesFromProject(Project project) } else { - var frameworks = targetFrameworks.Split(Separator, StringSplitOptions.RemoveEmptyEntries); + var frameworks = targetFrameworks.Split(CliConstants.SemiColon, StringSplitOptions.RemoveEmptyEntries); foreach (var framework in frameworks) { project.SetProperty(ProjectProperties.TargetFramework, framework); @@ -230,7 +288,7 @@ private static BuildResult RestoreProject(string projectFilePath, ProjectCollect private static bool IsBinaryLoggerEnabled(List args, out string binLogFileName) { - binLogFileName = BinLogFileName; + binLogFileName = CliConstants.BinLogFileName; var binLogArgs = new List(); @@ -253,9 +311,9 @@ private static bool IsBinaryLoggerEnabled(List args, out string binLogFi // Get BinLog filename var binLogArg = binLogArgs.LastOrDefault(); - if (binLogArg.Contains(':')) + if (binLogArg.Contains(CliConstants.Colon)) { - binLogFileName = binLogArg.Split(':')[1]; + binLogFileName = binLogArg.Split(CliConstants.Colon)[1]; } return true; } diff --git a/src/Cli/dotnet/commands/dotnet-test/Options.cs b/src/Cli/dotnet/commands/dotnet-test/Options.cs new file mode 100644 index 000000000000..3624a157092f --- /dev/null +++ b/src/Cli/dotnet/commands/dotnet-test/Options.cs @@ -0,0 +1,9 @@ +// 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 +{ + internal record BuildConfigurationOptions(bool HasNoRestore, bool HasNoBuild, string Configuration, string Architecture); + + internal record BuildPathsOptions(string ProjectPath, string SolutionPath); +} diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index 5ab0f617267d..18edc41e0bed 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -21,21 +21,17 @@ public static bool TryGetProjectOrSolutionFilePath(string directory, out string return false; } - string[] possibleSolutionPaths = [ - ..Directory.GetFiles(directory, "*.sln", SearchOption.TopDirectoryOnly), - ..Directory.GetFiles(directory, "*.slnx", SearchOption.TopDirectoryOnly)]; + var possibleSolutionPaths = GetSolutionFilePaths(directory); - // 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; - } - // If a single solution is found, use it. - else if (possibleSolutionPaths.Length == 1) - { - // Get project file paths to check if there are any projects in the directory - string[] possibleProjectPaths = GetProjectFilePaths(directory); + if (possibleSolutionPaths.Length > 1) + { + VSTestTrace.SafeWriteTrace(() => string.Format(CommonLocalizableStrings.MoreThanOneSolutionInDirectory, directory)); + return false; + } + + if (possibleSolutionPaths.Length == 1) + { + var possibleProjectPaths = GetProjectFilePaths(directory); if (possibleProjectPaths.Length == 0) { @@ -43,54 +39,46 @@ public static bool TryGetProjectOrSolutionFilePath(string directory, out string isSolution = true; return true; } - else // If both solution and project files are found, return false - { - VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMultipleProjectOrSolutionFilesErrorMessage); - return false; - } + + VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMultipleProjectOrSolutionFilesErrorMessage); + return false; } - // If no solutions are found, look for a project file - else + + var possibleProjectPath = GetProjectFilePaths(directory); + + if (possibleProjectPath.Length == 0) { - string[] possibleProjectPath = GetProjectFilePaths(directory); + VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdNoProjectOrSolutionFileErrorMessage); + return false; + } - // No projects found throws an error that no sln nor projects were found - if (possibleProjectPath.Length == 0) - { - VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdNoProjectOrSolutionFileErrorMessage); - return false; - } - // A single project found, use it - else if (possibleProjectPath.Length == 1) - { - projectOrSolutionFilePath = possibleProjectPath[0]; - return true; - } - // More than one project found. Not sure which one to choose - else - { - VSTestTrace.SafeWriteTrace(() => string.Format(CommonLocalizableStrings.MoreThanOneProjectInDirectory, directory)); - return false; - } + if (possibleProjectPath.Length == 1) + { + projectOrSolutionFilePath = possibleProjectPath[0]; + return true; } + + VSTestTrace.SafeWriteTrace(() => string.Format(CommonLocalizableStrings.MoreThanOneProjectInDirectory, directory)); + + return false; } - private static string[] GetProjectFilePaths(string directory) + private static string[] GetSolutionFilePaths(string directory) { - var projectFiles = Directory.EnumerateFiles(directory, "*.*proj", SearchOption.TopDirectoryOnly) - .Where(IsProjectFile) + return Directory.GetFiles(directory, CliConstants.SolutionExtensionPattern, SearchOption.TopDirectoryOnly) + .Concat(Directory.GetFiles(directory, CliConstants.SolutionXExtensionPattern, SearchOption.TopDirectoryOnly)) .ToArray(); + } - return projectFiles; + private static string[] GetProjectFilePaths(string directory) + { + return [.. Directory.EnumerateFiles(directory, CliConstants.ProjectExtensionPattern, SearchOption.TopDirectoryOnly).Where(IsProjectFile)]; } private static bool IsProjectFile(string filePath) { var extension = Path.GetExtension(filePath); - return extension.Equals(".csproj", StringComparison.OrdinalIgnoreCase) || - extension.Equals(".vbproj", StringComparison.OrdinalIgnoreCase) || - extension.Equals(".fsproj", StringComparison.OrdinalIgnoreCase) || - extension.Equals(".proj", StringComparison.OrdinalIgnoreCase); + return CliConstants.ProjectExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase); } public static async Task> ParseSolution(string solutionFilePath, string directory) @@ -98,7 +86,7 @@ public static async Task> ParseSolution(string solutionFileP if (string.IsNullOrEmpty(solutionFilePath)) { VSTestTrace.SafeWriteTrace(() => $"Solution file path cannot be null or empty: {solutionFilePath}"); - return []; + return Array.Empty(); } var projectsPaths = new List(); @@ -113,12 +101,12 @@ public static async Task> ParseSolution(string solutionFileP catch (Exception ex) { VSTestTrace.SafeWriteTrace(() => $"Failed to parse solution file '{solutionFilePath}': {ex.Message}"); - return []; + return Array.Empty(); } if (solution is not null) { - projectsPaths = [.. solution.SolutionProjects.Select(project => Path.Combine(directory, project.FilePath))]; + projectsPaths.AddRange(solution.SolutionProjects.Select(project => Path.Combine(directory, project.FilePath))); } return projectsPaths; diff --git a/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs b/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs index 1c448328323c..d77ae3467a36 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs @@ -34,9 +34,6 @@ internal sealed class TestApplication : IDisposable 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) @@ -50,38 +47,46 @@ public void AddExecutionId(string executionId) _ = _executionIds.GetOrAdd(executionId, _ => string.Empty); } - public async Task RunAsync(bool isFilterMode, bool enableHelp, BuiltInOptions builtInOptions) + public async Task RunAsync(bool hasFilterMode, bool enableHelp, BuildConfigurationOptions buildConfigurationOptions) { Run?.Invoke(this, EventArgs.Empty); - if (isFilterMode && !ModulePathExists()) + if (hasFilterMode && !ModulePathExists()) { return 1; } - bool isDll = _module.DllOrExePath.HasExtension(DLLExtension); + bool isDll = _module.DllOrExePath.HasExtension(CliConstants.DLLExtension); + var processStartInfo = CreateProcessStartInfo(hasFilterMode, isDll, buildConfigurationOptions, enableHelp); + + _testAppPipeConnectionLoop = Task.Run(async () => await WaitConnectionAsync(_cancellationToken.Token), _cancellationToken.Token); + var result = await StartProcess(processStartInfo); + + WaitOnTestApplicationPipeConnectionLoop(); + + return result; + } + - ProcessStartInfo processStartInfo = new() + private ProcessStartInfo CreateProcessStartInfo(bool hasFilterMode, bool isDll, BuildConfigurationOptions buildConfigurationOptions, bool enableHelp) + { + var processStartInfo = new ProcessStartInfo { - FileName = isFilterMode ? isDll ? Environment.ProcessPath : _module.DllOrExePath : Environment.ProcessPath, - Arguments = isFilterMode ? BuildArgs(isDll) : BuildArgsWithDotnetRun(enableHelp, builtInOptions), + FileName = hasFilterMode ? (isDll ? Environment.ProcessPath : _module.DllOrExePath) : Environment.ProcessPath, + Arguments = hasFilterMode ? BuildArgs(isDll) : BuildArgsWithDotnetRun(enableHelp, buildConfigurationOptions), RedirectStandardOutput = true, RedirectStandardError = true }; if (!string.IsNullOrEmpty(_module.RunSettingsFilePath)) { - processStartInfo.EnvironmentVariables.Add(TestingPlatformVsTestBridgeRunSettingsFileEnvVar, _module.RunSettingsFilePath); + processStartInfo.EnvironmentVariables.Add(CliConstants.TestingPlatformVsTestBridgeRunSettingsFileEnvVar, _module.RunSettingsFilePath); } - _testAppPipeConnectionLoop = Task.Run(async () => await WaitConnectionAsync(_cancellationToken.Token), _cancellationToken.Token); - var result = await StartProcess(processStartInfo); - - WaitOnTestApplicationPipeConnectionLoop(); - - return result; + return processStartInfo; } + private void WaitOnTestApplicationPipeConnectionLoop() { _cancellationToken.Cancel(); @@ -248,30 +253,30 @@ private bool ModulePathExists() return true; } - private string BuildArgsWithDotnetRun(bool hasHelp, BuiltInOptions builtInOptions) + private string BuildArgsWithDotnetRun(bool hasHelp, BuildConfigurationOptions buildConfigurationOptions) { StringBuilder builder = new(); builder.Append($"{CliConstants.DotnetRunCommand} {TestingPlatformOptions.ProjectOption.Name} \"{_module.ProjectPath}\""); - if (builtInOptions.HasNoRestore) + if (buildConfigurationOptions.HasNoRestore) { builder.Append($" {TestingPlatformOptions.NoRestoreOption.Name}"); } - if (builtInOptions.HasNoBuild) + if (buildConfigurationOptions.HasNoBuild) { builder.Append($" {TestingPlatformOptions.NoBuildOption.Name}"); } - if (!string.IsNullOrEmpty(builtInOptions.Architecture)) + if (!string.IsNullOrEmpty(buildConfigurationOptions.Architecture)) { - builder.Append($" {TestingPlatformOptions.ArchitectureOption.Name} {builtInOptions.Architecture}"); + builder.Append($" {TestingPlatformOptions.ArchitectureOption.Name} {buildConfigurationOptions.Architecture}"); } - if (!string.IsNullOrEmpty(builtInOptions.Configuration)) + if (!string.IsNullOrEmpty(buildConfigurationOptions.Configuration)) { - builder.Append($" {TestingPlatformOptions.ConfigurationOption.Name} {builtInOptions.Configuration}"); + builder.Append($" {TestingPlatformOptions.ConfigurationOption.Name} {buildConfigurationOptions.Configuration}"); } if (!string.IsNullOrEmpty(_module.TargetFramework)) diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs index 21dfe045f92e..cb3ec28ae7e0 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs @@ -27,54 +27,9 @@ public async Task Run(ParseResult parseResult) bool hasFailed = false; try { - // User can decide what the degree of parallelism should be - // If not specified, we will default to the number of processors - if (!int.TryParse(parseResult.GetValue(TestingPlatformOptions.MaxParallelTestModulesOption), out int degreeOfParallelism)) - degreeOfParallelism = Environment.ProcessorCount; - - bool filterModeEnabled = parseResult.HasOption(TestingPlatformOptions.TestModulesFilterOption); - - if (filterModeEnabled && parseResult.HasOption(TestingPlatformOptions.ArchitectureOption)) - { - VSTestTrace.SafeWriteTrace(() => $"The --arch option is not supported yet."); - } - - BuiltInOptions builtInOptions = new( - parseResult.HasOption(TestingPlatformOptions.NoRestoreOption), - parseResult.HasOption(TestingPlatformOptions.NoBuildOption), - parseResult.GetValue(TestingPlatformOptions.ConfigurationOption), - parseResult.GetValue(TestingPlatformOptions.ArchitectureOption)); - - if (ContainsHelpOption(parseResult.GetArguments())) - { - _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(filterModeEnabled, enableHelp: true, builtInOptions); - }); - } - else - { - _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); - }); - } + int degreeOfParallelism = GetDegreeOfParallelism(parseResult); + BuildConfigurationOptions buildConfigurationOptions = GetBuildConfigurationOptions(parseResult); + InitializeActionQueue(parseResult, degreeOfParallelism, buildConfigurationOptions); _args = [.. parseResult.UnmatchedTokens]; _msBuildHandler = new(_args, _actionQueue, degreeOfParallelism); @@ -89,12 +44,12 @@ public async Task Run(ParseResult parseResult) } else { - if (!await RunMSBuild(parseResult)) + var buildPathOptions = GetBuildPathOptions(parseResult); + if (!await _msBuildHandler.RunMSBuild(buildPathOptions)) { return ExitCodes.GenericFailure; } - // If not all test projects have IsTestProject and IsTestingPlatformApplication properties set to true, we will simply return if (!_msBuildHandler.EnqueueTestApplications()) { VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdUnsupportedVSTestTestApplicationsDescription); @@ -104,75 +59,74 @@ public async Task Run(ParseResult parseResult) _actionQueue.EnqueueCompleted(); hasFailed = _actionQueue.WaitAllActions(); - // Above line will block till we have all connections and all GetTestsProject msbuild task complete. } finally { - // Clean up everything CleanUp(); } return hasFailed ? ExitCodes.GenericFailure : ExitCodes.Success; } - private async Task RunMSBuild(ParseResult parseResult) + private static int GetDegreeOfParallelism(ParseResult parseResult) { - int msbuildExitCode; - - if (parseResult.HasOption(TestingPlatformOptions.ProjectOption)) - { - string filePath = parseResult.GetValue(TestingPlatformOptions.ProjectOption); - string[] extensions = [".proj", ".csproj", ".vbproj", ".fsproj"]; + if (!int.TryParse(parseResult.GetValue(TestingPlatformOptions.MaxParallelTestModulesOption), out int degreeOfParallelism) || degreeOfParallelism <= 0) + degreeOfParallelism = Environment.ProcessorCount; + return degreeOfParallelism; + } - if (!extensions.Contains(Path.GetExtension(filePath))) - { - VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdInvalidProjectFileExtensionDescription, filePath)); - return false; - } + private static BuildConfigurationOptions GetBuildConfigurationOptions(ParseResult parseResult) + { + return new BuildConfigurationOptions( + parseResult.HasOption(TestingPlatformOptions.NoRestoreOption), + parseResult.HasOption(TestingPlatformOptions.NoBuildOption), + parseResult.GetValue(TestingPlatformOptions.ConfigurationOption), + parseResult.GetValue(TestingPlatformOptions.ArchitectureOption)); + } - if (!File.Exists(filePath)) - { - VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdNonExistentProjectFilePathDescription, filePath)); - return false; - } + private static BuildPathsOptions GetBuildPathOptions(ParseResult parseResult) + { + return new BuildPathsOptions( + parseResult.GetValue(TestingPlatformOptions.ProjectOption), + parseResult.GetValue(TestingPlatformOptions.SolutionOption)); + } - msbuildExitCode = await _msBuildHandler.RunWithMSBuild(filePath, isSolution: false); - } - else if (parseResult.HasOption(TestingPlatformOptions.SolutionOption)) + private void InitializeActionQueue(ParseResult parseResult, int degreeOfParallelism, BuildConfigurationOptions buildConfigurationOptions) + { + if (ContainsHelpOption(parseResult.GetArguments())) { - string filePath = parseResult.GetValue(TestingPlatformOptions.SolutionOption); - string[] extensions = [".sln", ".slnx"]; - - if (!extensions.Contains(Path.GetExtension(filePath))) - { - VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdInvalidSolutionFileExtensionDescription, filePath)); - return false; - } - - if (!File.Exists(filePath)) + _actionQueue = new(degreeOfParallelism, async (TestApplication testApp) => { - VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdNonExistentSolutionFilePathDescription, filePath)); - return false; - } + testApp.HelpRequested += OnHelpRequested; + testApp.ErrorReceived += OnErrorReceived; + testApp.TestProcessExited += OnTestProcessExited; + testApp.Run += OnTestApplicationRun; + testApp.ExecutionIdReceived += OnExecutionIdReceived; - msbuildExitCode = await _msBuildHandler.RunWithMSBuild(filePath, isSolution: true); + return await testApp.RunAsync(hasFilterMode: true, enableHelp: true, buildConfigurationOptions); + }); } else { - // If no filter was provided neither the project using --project, - // MSBuild will get the test project paths in the current directory - msbuildExitCode = await _msBuildHandler.RunWithMSBuild(); - } - - if (msbuildExitCode != ExitCodes.Success) - { - VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdMSBuildProjectsPropertiesErrorMessage, msbuildExitCode)); - return false; + _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); + }); } - - return true; } + private static bool ContainsHelpOption(IEnumerable args) => args.Contains(CliConstants.HelpOptionKey) || args.Contains(CliConstants.HelpOptionKey.Substring(0, 2)); + private void CleanUp() { _msBuildHandler.Dispose(); @@ -184,14 +138,9 @@ private void CleanUp() private void OnHandshakeReceived(object sender, HandshakeArgs args) { - if (!VSTestTrace.TraceEnabled) - { - return; - } - - var handshake = args.Handshake; + if (!VSTestTrace.TraceEnabled) return; - foreach (var property in handshake.Properties) + foreach (var property in args.Handshake.Properties) { VSTestTrace.SafeWriteTrace(() => $"{property.Key}: {property.Value}"); } @@ -199,15 +148,10 @@ private void OnHandshakeReceived(object sender, HandshakeArgs args) private void OnDiscoveredTestsReceived(object sender, DiscoveredTestEventArgs args) { - if (!VSTestTrace.TraceEnabled) - { - return; - } - - var discoveredTestMessages = args.DiscoveredTests; + if (!VSTestTrace.TraceEnabled) return; VSTestTrace.SafeWriteTrace(() => $"DiscoveredTests Execution Id: {args.ExecutionId}"); - foreach (DiscoveredTest discoveredTestMessage in discoveredTestMessages) + foreach (var discoveredTestMessage in args.DiscoveredTests) { VSTestTrace.SafeWriteTrace(() => $"DiscoveredTest: {discoveredTestMessage.Uid}, {discoveredTestMessage.DisplayName}"); } @@ -215,10 +159,7 @@ private void OnDiscoveredTestsReceived(object sender, DiscoveredTestEventArgs ar private void OnTestResultsReceived(object sender, TestResultEventArgs args) { - if (!VSTestTrace.TraceEnabled) - { - return; - } + if (!VSTestTrace.TraceEnabled) return; VSTestTrace.SafeWriteTrace(() => $"TestResults Execution Id: {args.ExecutionId}"); @@ -239,10 +180,7 @@ private void OnTestResultsReceived(object sender, TestResultEventArgs args) private void OnFileArtifactsReceived(object sender, FileArtifactEventArgs args) { - if (!VSTestTrace.TraceEnabled) - { - return; - } + if (!VSTestTrace.TraceEnabled) return; VSTestTrace.SafeWriteTrace(() => $"FileArtifactMessages Execution Id: {args.ExecutionId}"); @@ -256,10 +194,7 @@ private void OnFileArtifactsReceived(object sender, FileArtifactEventArgs args) private void OnSessionEventReceived(object sender, SessionEventArgs args) { - if (!VSTestTrace.TraceEnabled) - { - return; - } + if (!VSTestTrace.TraceEnabled) return; var sessionEvent = args.SessionEvent; VSTestTrace.SafeWriteTrace(() => $"TestSessionEvent: {sessionEvent.SessionType}, {sessionEvent.SessionUid}, {sessionEvent.ExecutionId}"); @@ -267,20 +202,14 @@ private void OnSessionEventReceived(object sender, SessionEventArgs args) private void OnErrorReceived(object sender, ErrorEventArgs args) { - if (!VSTestTrace.TraceEnabled) - { - return; - } + if (!VSTestTrace.TraceEnabled) return; VSTestTrace.SafeWriteTrace(() => args.ErrorMessage); } private void OnTestProcessExited(object sender, TestProcessExitEventArgs args) { - if (!VSTestTrace.TraceEnabled) - { - return; - } + if (!VSTestTrace.TraceEnabled) return; if (args.ExitCode != ExitCodes.Success) { @@ -307,7 +236,5 @@ private void OnTestApplicationRun(object sender, EventArgs args) private void OnExecutionIdReceived(object sender, ExecutionEventArgs args) { } - - private static bool ContainsHelpOption(IEnumerable args) => args.Contains(CliConstants.HelpOptionKey) || args.Contains(CliConstants.HelpOptionKey.Substring(0, 2)); } } diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.cs.xlf index 4a61a3d44a41..0365e0458bd0 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.cs.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.cs.xlf @@ -158,14 +158,14 @@ Příklady: Do not execute an implicit restore. - - The provided project file path does not exist: {0}. - The provided project file path does not exist: {0}. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. - - The provided solution file path does not exist: {0}. - The provided solution file path does not exist: {0}. + + Specify either the project or solution option. + Specify either the project or solution option. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.de.xlf index 25bdf7348b57..f686789b6135 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.de.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.de.xlf @@ -158,14 +158,14 @@ Beispiele: Do not execute an implicit restore. - - The provided project file path does not exist: {0}. - The provided project file path does not exist: {0}. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. - - The provided solution file path does not exist: {0}. - The provided solution file path does not exist: {0}. + + Specify either the project or solution option. + Specify either the project or solution option. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.es.xlf index b56d33d03fa7..d0c2223572d5 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.es.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.es.xlf @@ -160,14 +160,14 @@ Ejemplos: Do not execute an implicit restore. - - The provided project file path does not exist: {0}. - The provided project file path does not exist: {0}. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. - - The provided solution file path does not exist: {0}. - The provided solution file path does not exist: {0}. + + Specify either the project or solution option. + Specify either the project or solution option. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.fr.xlf index 839b95fa6e12..c738d6a56956 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.fr.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.fr.xlf @@ -158,14 +158,14 @@ Exemples : Do not execute an implicit restore. - - The provided project file path does not exist: {0}. - The provided project file path does not exist: {0}. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. - - The provided solution file path does not exist: {0}. - The provided solution file path does not exist: {0}. + + Specify either the project or solution option. + Specify either the project or solution option. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.it.xlf index 2eb6b933b16d..aee1d986b935 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.it.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.it.xlf @@ -158,14 +158,14 @@ Esempi: Do not execute an implicit restore. - - The provided project file path does not exist: {0}. - The provided project file path does not exist: {0}. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. - - The provided solution file path does not exist: {0}. - The provided solution file path does not exist: {0}. + + Specify either the project or solution option. + Specify either the project or solution option. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ja.xlf index 9563d69011e0..3c70d07cf3af 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ja.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ja.xlf @@ -158,14 +158,14 @@ Examples: Do not execute an implicit restore. - - The provided project file path does not exist: {0}. - The provided project file path does not exist: {0}. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. - - The provided solution file path does not exist: {0}. - The provided solution file path does not exist: {0}. + + Specify either the project or solution option. + Specify either the project or solution option. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ko.xlf index 6ce21faed87c..02eb5e91656f 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ko.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ko.xlf @@ -158,14 +158,14 @@ Examples: Do not execute an implicit restore. - - The provided project file path does not exist: {0}. - The provided project file path does not exist: {0}. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. - - The provided solution file path does not exist: {0}. - The provided solution file path does not exist: {0}. + + Specify either the project or solution option. + Specify either the project or solution option. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pl.xlf index 6e906134cd40..009a95303718 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pl.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pl.xlf @@ -158,14 +158,14 @@ Przykłady: Do not execute an implicit restore. - - The provided project file path does not exist: {0}. - The provided project file path does not exist: {0}. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. - - The provided solution file path does not exist: {0}. - The provided solution file path does not exist: {0}. + + Specify either the project or solution option. + Specify either the project or solution option. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pt-BR.xlf index e76dbac9a3fe..bf8075725dca 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pt-BR.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pt-BR.xlf @@ -158,14 +158,14 @@ Exemplos: Do not execute an implicit restore. - - The provided project file path does not exist: {0}. - The provided project file path does not exist: {0}. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. - - The provided solution file path does not exist: {0}. - The provided solution file path does not exist: {0}. + + Specify either the project or solution option. + Specify either the project or solution option. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ru.xlf index 61352fe01d02..9997c9e5ea45 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ru.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ru.xlf @@ -158,14 +158,14 @@ Examples: Do not execute an implicit restore. - - The provided project file path does not exist: {0}. - The provided project file path does not exist: {0}. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. - - The provided solution file path does not exist: {0}. - The provided solution file path does not exist: {0}. + + Specify either the project or solution option. + Specify either the project or solution option. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.tr.xlf index 6c10dcbfb211..8933d9aa4f16 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.tr.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.tr.xlf @@ -158,14 +158,14 @@ Bu bağımsız değişken, birden çok değişken sağlamak için birden çok ke Do not execute an implicit restore. - - The provided project file path does not exist: {0}. - The provided project file path does not exist: {0}. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. - - The provided solution file path does not exist: {0}. - The provided solution file path does not exist: {0}. + + Specify either the project or solution option. + Specify either the project or solution option. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hans.xlf index edc4d0782156..fd2fb9f0e6b5 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hans.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hans.xlf @@ -158,14 +158,14 @@ Examples: Do not execute an implicit restore. - - The provided project file path does not exist: {0}. - The provided project file path does not exist: {0}. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. - - The provided solution file path does not exist: {0}. - The provided solution file path does not exist: {0}. + + Specify either the project or solution option. + Specify either the project or solution option. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hant.xlf index 57d105d531c9..2f85ecffe6ff 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hant.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hant.xlf @@ -158,14 +158,14 @@ Examples: Do not execute an implicit restore. - - The provided project file path does not exist: {0}. - The provided project file path does not exist: {0}. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. - - The provided solution file path does not exist: {0}. - The provided solution file path does not exist: {0}. + + Specify either the project or solution option. + Specify either the project or solution option. From 5d051848ba6ba9725701782b6899f9b2a900820d Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Fri, 10 Jan 2025 10:51:12 +0100 Subject: [PATCH 30/36] Fix bug --- src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs | 2 +- .../commands/dotnet-test/TestingPlatformCommand.Help.cs | 2 +- src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs b/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs index b3c207c745d1..f0c0ce5aeb2b 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs @@ -190,7 +190,7 @@ private static CliCommand ConstructCommand() private static CliCommand GetTestingPlatformCliCommand() { var command = new TestingPlatformCommand("test"); - command.SetAction(async (parseResult) => await command.Run(parseResult)); + command.SetAction(parseResult => command.Run(parseResult)); command.Options.Add(TestingPlatformOptions.MaxParallelTestModulesOption); command.Options.Add(TestingPlatformOptions.AdditionalMSBuildParametersOption); command.Options.Add(TestingPlatformOptions.TestModulesFilterOption); diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.Help.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.Help.cs index 838a74530384..bda096d8a70f 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.Help.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.Help.cs @@ -18,7 +18,7 @@ public IEnumerable> CustomHelpLayout() { Console.WriteLine("Waiting for options and extensions..."); - await Run(context.ParseResult); + Run(context.ParseResult); if (_commandLineOptionNameToModuleNames.IsEmpty) { diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs index cb3ec28ae7e0..d3ce146cf350 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs @@ -22,7 +22,7 @@ public TestingPlatformCommand(string name, string description = null) : base(nam TreatUnmatchedTokensAsErrors = false; } - public async Task Run(ParseResult parseResult) + public int Run(ParseResult parseResult) { bool hasFailed = false; try @@ -45,7 +45,7 @@ public async Task Run(ParseResult parseResult) else { var buildPathOptions = GetBuildPathOptions(parseResult); - if (!await _msBuildHandler.RunMSBuild(buildPathOptions)) + if (!_msBuildHandler.RunMSBuild(buildPathOptions).GetAwaiter().GetResult()) { return ExitCodes.GenericFailure; } From 3bf45f4f5026acfeef35bdccde4bb52cdd6846d6 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Fri, 10 Jan 2025 11:13:00 +0100 Subject: [PATCH 31/36] Remove @ --- .../commands/dotnet-test/MSBuildHandler.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs index a2bbb518c550..088e53c27a9e 100644 --- a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs +++ b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs @@ -84,7 +84,7 @@ private static bool ValidateFilePath(string filePath, string[] validExtensions, if (!File.Exists(filePath)) { - VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.@CmdNonExistentFileDescription, filePath)); + VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdNonExistentFileDescription, filePath)); return false; } @@ -292,8 +292,8 @@ private static BuildResult RestoreProject(string projectFilePath, ProjectCollect private static bool IsBinaryLoggerEnabled(List args, out string binLogFileName) { - binLogFileName = string.Empty; - var binLogArgs = new List(); + binLogFileName = string.Empty; + var binLogArgs = new List(); foreach (var arg in args) { @@ -314,17 +314,17 @@ private static bool IsBinaryLoggerEnabled(List args, out string binLogFi // Get BinLog filename var binLogArg = binLogArgs.LastOrDefault(); - if (binLogArg.Contains(CliConstants.Colon)) - { - var parts = binLogArg.Split(CliConstants.Colon, 2); - binLogFileName = !string.IsNullOrEmpty(parts[1]) ? parts[1] : CliConstants.BinLogFileName; - } - else - { - binLogFileName = CliConstants.BinLogFileName; - } - - return true; + if (binLogArg.Contains(CliConstants.Colon)) + { + var parts = binLogArg.Split(CliConstants.Colon, 2); + binLogFileName = !string.IsNullOrEmpty(parts[1]) ? parts[1] : CliConstants.BinLogFileName; + } + else + { + binLogFileName = CliConstants.BinLogFileName; + } + + return true; } return false; From 223b072c930c700eaa4a30d3bc3c30e74d6cdb6d Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Fri, 10 Jan 2025 11:28:25 +0100 Subject: [PATCH 32/36] Fixes --- .../dotnet-test/SolutionAndProjectUtility.cs | 29 +++++++++---------- .../TestingPlatformCommand.Help.cs | 2 +- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index 4de50927c2ae..93f674202d3a 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -23,15 +23,15 @@ public static bool TryGetProjectOrSolutionFilePath(string directory, out string var possibleSolutionPaths = GetSolutionFilePaths(directory); - // 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; - } + // 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; + } if (possibleSolutionPaths.Length == 1) - { + { var possibleProjectPaths = GetProjectFilePaths(directory); if (possibleProjectPaths.Length == 0) @@ -41,33 +41,30 @@ public static bool TryGetProjectOrSolutionFilePath(string directory, out string return true; } - VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMultipleProjectOrSolutionFilesErrorMessage); - return false; - } + VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMultipleProjectOrSolutionFilesErrorMessage); + return false; } + // If no solutions are found, look for a project file else { string[] possibleProjectPath = GetProjectFilePaths(directory); - var possibleProjectPath = GetProjectFilePaths(directory); - if (possibleProjectPath.Length == 0) { VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdNoProjectOrSolutionFileErrorMessage); return false; } - if (possibleProjectPath.Length == 1) + if (possibleProjectPath.Length == 1) { projectOrSolutionFilePath = possibleProjectPath[0]; return true; } - VSTestTrace.SafeWriteTrace(() => string.Format(CommonLocalizableStrings.MoreThanOneProjectInDirectory, directory)); + VSTestTrace.SafeWriteTrace(() => string.Format(CommonLocalizableStrings.MoreThanOneProjectInDirectory, directory)); - return false; - } + return false; } } diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.Help.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.Help.cs index bda096d8a70f..6499eb074933 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.Help.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.Help.cs @@ -14,7 +14,7 @@ internal partial class TestingPlatformCommand public IEnumerable> CustomHelpLayout() { - yield return async (context) => + yield return (context) => { Console.WriteLine("Waiting for options and extensions..."); From efa4fb4a0ad5aae9a980c5ed6aa777389cccaa83 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Fri, 10 Jan 2025 12:12:01 +0100 Subject: [PATCH 33/36] Apply comments --- .../dotnet-test/SolutionAndProjectUtility.cs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index 93f674202d3a..46139dce9bbc 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -70,28 +70,21 @@ public static bool TryGetProjectOrSolutionFilePath(string directory, out string private static string[] GetSolutionFilePaths(string directory) { - return Directory.GetFiles(directory, CliConstants.SolutionExtensionPattern, SearchOption.TopDirectoryOnly) - .Concat(Directory.GetFiles(directory, CliConstants.SolutionXExtensionPattern, SearchOption.TopDirectoryOnly)) + return Directory.EnumerateFiles(directory, CliConstants.SolutionExtensionPattern, SearchOption.TopDirectoryOnly) + .Concat(Directory.EnumerateFiles(directory, CliConstants.SolutionXExtensionPattern, SearchOption.TopDirectoryOnly)) .ToArray(); } - private static string[] GetProjectFilePaths(string directory) - { - return [.. Directory.EnumerateFiles(directory, CliConstants.ProjectExtensionPattern, SearchOption.TopDirectoryOnly).Where(IsProjectFile)]; - } + private static string[] GetProjectFilePaths(string directory) => [.. Directory.EnumerateFiles(directory, CliConstants.ProjectExtensionPattern, SearchOption.TopDirectoryOnly).Where(IsProjectFile)]; - private static bool IsProjectFile(string filePath) - { - var extension = Path.GetExtension(filePath); - return CliConstants.ProjectExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase); - } + private static bool IsProjectFile(string filePath) => CliConstants.ProjectExtensions.Contains(Path.GetExtension(filePath), StringComparer.OrdinalIgnoreCase); public static async Task> ParseSolution(string solutionFilePath, string directory) { if (string.IsNullOrEmpty(solutionFilePath)) { VSTestTrace.SafeWriteTrace(() => $"Solution file path cannot be null or empty: {solutionFilePath}"); - return Array.Empty(); + return []; } var projectsPaths = new List(); @@ -106,7 +99,7 @@ public static async Task> ParseSolution(string solutionFileP catch (Exception ex) { VSTestTrace.SafeWriteTrace(() => $"Failed to parse solution file '{solutionFilePath}': {ex.Message}"); - return Array.Empty(); + return []; } if (solution is not null) From a36f4715994c0d9c85b07bf4237109f8997381bd Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Fri, 10 Jan 2025 12:14:24 +0100 Subject: [PATCH 34/36] Apply comments --- .../commands/dotnet-test/TestApplication.cs | 4 +-- .../dotnet-test/TestingPlatformCommand.cs | 34 ++++++++----------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs b/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs index 3b2bcbfca5ef..ef1d4b0c7e5b 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs @@ -63,11 +63,11 @@ public async Task RunAsync(bool hasFilterMode, bool enableHelp, BuildConfig var processStartInfo = CreateProcessStartInfo(hasFilterMode, isDll, buildConfigurationOptions, enableHelp); _testAppPipeConnectionLoop = Task.Run(async () => await WaitConnectionAsync(_cancellationToken.Token), _cancellationToken.Token); - var result = await StartProcess(processStartInfo); + var testProcessResult = await StartProcess(processStartInfo); WaitOnTestApplicationPipeConnectionLoop(); - return result; + return testProcessResult; } diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs index d3ce146cf350..105ff590ffd5 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs @@ -75,52 +75,46 @@ private static int GetDegreeOfParallelism(ParseResult parseResult) return degreeOfParallelism; } - private static BuildConfigurationOptions GetBuildConfigurationOptions(ParseResult parseResult) - { - return new BuildConfigurationOptions( - parseResult.HasOption(TestingPlatformOptions.NoRestoreOption), + private static BuildConfigurationOptions GetBuildConfigurationOptions(ParseResult parseResult) => + new(parseResult.HasOption(TestingPlatformOptions.NoRestoreOption), parseResult.HasOption(TestingPlatformOptions.NoBuildOption), parseResult.GetValue(TestingPlatformOptions.ConfigurationOption), parseResult.GetValue(TestingPlatformOptions.ArchitectureOption)); - } - private static BuildPathsOptions GetBuildPathOptions(ParseResult parseResult) - { - return new BuildPathsOptions( - parseResult.GetValue(TestingPlatformOptions.ProjectOption), - parseResult.GetValue(TestingPlatformOptions.SolutionOption)); - } + private static BuildPathsOptions GetBuildPathOptions(ParseResult parseResult) => + new(parseResult.GetValue(TestingPlatformOptions.ProjectOption), + parseResult.GetValue(TestingPlatformOptions.SolutionOption)); private void InitializeActionQueue(ParseResult parseResult, int degreeOfParallelism, BuildConfigurationOptions buildConfigurationOptions) { - if (ContainsHelpOption(parseResult.GetArguments())) + if (!ContainsHelpOption(parseResult.GetArguments())) { _actionQueue = new(degreeOfParallelism, async (TestApplication testApp) => { - testApp.HelpRequested += OnHelpRequested; + 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: true, enableHelp: true, buildConfigurationOptions); + return await testApp.RunAsync(hasFilterMode: false, enableHelp: false, buildConfigurationOptions); }); } else { _actionQueue = new(degreeOfParallelism, async (TestApplication testApp) => { - testApp.HandshakeReceived += OnHandshakeReceived; - testApp.DiscoveredTestsReceived += OnDiscoveredTestsReceived; - testApp.TestResultsReceived += OnTestResultsReceived; - testApp.FileArtifactsReceived += OnFileArtifactsReceived; - testApp.SessionEventReceived += OnSessionEventReceived; + testApp.HelpRequested += OnHelpRequested; testApp.ErrorReceived += OnErrorReceived; testApp.TestProcessExited += OnTestProcessExited; testApp.Run += OnTestApplicationRun; testApp.ExecutionIdReceived += OnExecutionIdReceived; - return await testApp.RunAsync(hasFilterMode: false, enableHelp: false, buildConfigurationOptions); + return await testApp.RunAsync(hasFilterMode: true, enableHelp: true, buildConfigurationOptions); }); } } From 47bced1db3f942f7fdada387c8f7ca33fd758d54 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Fri, 10 Jan 2025 14:47:12 +0100 Subject: [PATCH 35/36] remove unused vars --- src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs index 088e53c27a9e..969883b6f583 100644 --- a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs +++ b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs @@ -19,8 +19,6 @@ internal sealed class MSBuildHandler : IDisposable private readonly ConcurrentBag _testApplications = new(); private bool _areTestingPlatformApplications = true; - private const string BinLogFileName = "msbuild.binlog"; - private const string Separator = ";"; private static readonly Lock buildLock = new(); public MSBuildHandler(List args, TestApplicationActionQueue actionQueue, int degreeOfParallelism) From 9712e053767306372f308bd65caa2ffa135acd1a Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Mon, 13 Jan 2025 12:11:20 +0100 Subject: [PATCH 36/36] Support --directory --- .../dotnet-test/LocalizableStrings.resx | 22 ++++--- .../commands/dotnet-test/MSBuildHandler.cs | 64 ++++++++++++------- .../dotnet/commands/dotnet-test/Options.cs | 2 +- .../dotnet-test/SolutionAndProjectUtility.cs | 8 +-- .../commands/dotnet-test/TestCommandParser.cs | 1 + .../dotnet-test/TestingPlatformCommand.cs | 3 +- .../dotnet-test/TestingPlatformOptions.cs | 6 ++ .../dotnet-test/xlf/LocalizableStrings.cs.xlf | 32 ++++++---- .../dotnet-test/xlf/LocalizableStrings.de.xlf | 32 ++++++---- .../dotnet-test/xlf/LocalizableStrings.es.xlf | 32 ++++++---- .../dotnet-test/xlf/LocalizableStrings.fr.xlf | 32 ++++++---- .../dotnet-test/xlf/LocalizableStrings.it.xlf | 32 ++++++---- .../dotnet-test/xlf/LocalizableStrings.ja.xlf | 32 ++++++---- .../dotnet-test/xlf/LocalizableStrings.ko.xlf | 32 ++++++---- .../dotnet-test/xlf/LocalizableStrings.pl.xlf | 32 ++++++---- .../xlf/LocalizableStrings.pt-BR.xlf | 32 ++++++---- .../dotnet-test/xlf/LocalizableStrings.ru.xlf | 32 ++++++---- .../dotnet-test/xlf/LocalizableStrings.tr.xlf | 32 ++++++---- .../xlf/LocalizableStrings.zh-Hans.xlf | 32 ++++++---- .../xlf/LocalizableStrings.zh-Hant.xlf | 32 ++++++---- 20 files changed, 340 insertions(+), 182 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx b/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx index 849ff1fe7348..1855e09972b5 100644 --- a/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx +++ b/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx @@ -190,6 +190,9 @@ Defines the path of the solution file to run. If not specified, it defaults to the current directory. + + Defines the path of directory to run. If not specified, it defaults to the current directory. + The directory where the test results will be placed. The specified directory will be created if it does not exist. @@ -332,25 +335,28 @@ Examples: Test runner not supported: {0}. - + The provided file path does not exist: {0}. - + + The provided directory path does not exist: {0}. + + Specify which project or solution file to use because this folder contains more than one project or solution file. - + Specify a project or solution file. The current working directory does not contain a project or solution file. - + Get projects properties with MSBuild didn't execute properly with exit code: {0}. - + The provided solution file has an invalid extension: {0}. - + The provided project file has an invalid extension: {0}. - - Specify either the project or solution option. + + Specify either the project, solution or directory option. diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs index 969883b6f583..ed7e7401bc65 100644 --- a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs +++ b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs @@ -30,42 +30,58 @@ public MSBuildHandler(List args, TestApplicationActionQueue actionQueue, public async Task RunMSBuild(BuildPathsOptions buildPathOptions) { - int msbuildExitCode; - - if (!string.IsNullOrEmpty(buildPathOptions.ProjectPath) && !string.IsNullOrEmpty(buildPathOptions.SolutionPath)) + if (!ValidateBuildPathOptions(buildPathOptions)) { - VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdProjectAndSolutionOptionErrorDescription); return false; } + int msbuildExitCode; + if (!string.IsNullOrEmpty(buildPathOptions.ProjectPath)) { - if (!ValidateFilePath(buildPathOptions.ProjectPath, CliConstants.ProjectExtensions, LocalizableStrings.CmdInvalidProjectFileExtensionDescription)) - { - return false; - } - - msbuildExitCode = await RunWithMSBuild(buildPathOptions.ProjectPath, isSolution: false); + msbuildExitCode = await RunBuild(buildPathOptions.ProjectPath, isSolution: false); } else if (!string.IsNullOrEmpty(buildPathOptions.SolutionPath)) { - if (!ValidateFilePath(buildPathOptions.SolutionPath, CliConstants.SolutionExtensions, LocalizableStrings.CmdInvalidSolutionFileExtensionDescription)) - { - return false; - } - - msbuildExitCode = await RunWithMSBuild(buildPathOptions.SolutionPath, isSolution: true); + msbuildExitCode = await RunBuild(buildPathOptions.SolutionPath, isSolution: true); } else { - // If no filter was provided neither the project using --project, - // MSBuild will get the test project paths in the current directory - msbuildExitCode = await RunWithMSBuild(Directory.GetCurrentDirectory()); + msbuildExitCode = await RunBuild(buildPathOptions.DirectoryPath ?? Directory.GetCurrentDirectory()); } if (msbuildExitCode != ExitCodes.Success) { - VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdMSBuildProjectsPropertiesErrorMessage, msbuildExitCode)); + VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdMSBuildProjectsPropertiesErrorDescription, msbuildExitCode)); + return false; + } + + return true; + } + + private bool ValidateBuildPathOptions(BuildPathsOptions buildPathOptions) + { + if ((!string.IsNullOrEmpty(buildPathOptions.ProjectPath) && !string.IsNullOrEmpty(buildPathOptions.SolutionPath)) || + (!string.IsNullOrEmpty(buildPathOptions.ProjectPath) && !string.IsNullOrEmpty(buildPathOptions.DirectoryPath)) || + (!string.IsNullOrEmpty(buildPathOptions.SolutionPath) && !string.IsNullOrEmpty(buildPathOptions.DirectoryPath))) + { + VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMultipleBuildPathOptionsErrorDescription); + return false; + } + + if (!string.IsNullOrEmpty(buildPathOptions.ProjectPath)) + { + return ValidateFilePath(buildPathOptions.ProjectPath, CliConstants.ProjectExtensions, LocalizableStrings.CmdInvalidProjectFileExtensionErrorDescription); + } + + if (!string.IsNullOrEmpty(buildPathOptions.SolutionPath)) + { + return ValidateFilePath(buildPathOptions.SolutionPath, CliConstants.SolutionExtensions, LocalizableStrings.CmdInvalidSolutionFileExtensionErrorDescription); + } + + if (!string.IsNullOrEmpty(buildPathOptions.DirectoryPath) && !Directory.Exists(buildPathOptions.DirectoryPath)) + { + VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdNonExistentDirectoryErrorDescription, Path.GetFullPath(buildPathOptions.DirectoryPath))); return false; } @@ -82,16 +98,16 @@ private static bool ValidateFilePath(string filePath, string[] validExtensions, if (!File.Exists(filePath)) { - VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdNonExistentFileDescription, filePath)); + VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdNonExistentFileErrorDescription, Path.GetFullPath(filePath))); return false; } return true; } - private async Task RunWithMSBuild(string directory) + private async Task RunBuild(string directoryPath) { - bool solutionOrProjectFileFound = SolutionAndProjectUtility.TryGetProjectOrSolutionFilePath(directory, out string projectOrSolutionFilePath, out bool isSolution); + bool solutionOrProjectFileFound = SolutionAndProjectUtility.TryGetProjectOrSolutionFilePath(directoryPath, out string projectOrSolutionFilePath, out bool isSolution); if (!solutionOrProjectFileFound) { @@ -105,7 +121,7 @@ private async Task RunWithMSBuild(string directory) return restored ? ExitCodes.Success : ExitCodes.GenericFailure; } - private async Task RunWithMSBuild(string filePath, bool isSolution) + private async Task RunBuild(string filePath, bool isSolution) { (IEnumerable modules, bool restored) = await GetProjectsProperties(filePath, isSolution); diff --git a/src/Cli/dotnet/commands/dotnet-test/Options.cs b/src/Cli/dotnet/commands/dotnet-test/Options.cs index 3624a157092f..43fdc3696ff9 100644 --- a/src/Cli/dotnet/commands/dotnet-test/Options.cs +++ b/src/Cli/dotnet/commands/dotnet-test/Options.cs @@ -5,5 +5,5 @@ namespace Microsoft.DotNet.Cli { internal record BuildConfigurationOptions(bool HasNoRestore, bool HasNoBuild, string Configuration, string Architecture); - internal record BuildPathsOptions(string ProjectPath, string SolutionPath); + internal record BuildPathsOptions(string ProjectPath, string SolutionPath, string DirectoryPath); } diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index 46139dce9bbc..a3ca1af6b4c6 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -41,18 +41,16 @@ public static bool TryGetProjectOrSolutionFilePath(string directory, out string return true; } - VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMultipleProjectOrSolutionFilesErrorMessage); + VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMultipleProjectOrSolutionFilesErrorDescription); return false; } - - // If no solutions are found, look for a project file - else + else // If no solutions are found, look for a project file { string[] possibleProjectPath = GetProjectFilePaths(directory); if (possibleProjectPath.Length == 0) { - VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdNoProjectOrSolutionFileErrorMessage); + VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdNoProjectOrSolutionFileErrorDescription); return false; } diff --git a/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs b/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs index f0c0ce5aeb2b..42cfdb243f95 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs @@ -201,6 +201,7 @@ private static CliCommand GetTestingPlatformCliCommand() command.Options.Add(TestingPlatformOptions.ConfigurationOption); command.Options.Add(TestingPlatformOptions.ProjectOption); command.Options.Add(TestingPlatformOptions.SolutionOption); + command.Options.Add(TestingPlatformOptions.DirectoryOption); return command; } diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs index 105ff590ffd5..84be6429fcdd 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs @@ -83,7 +83,8 @@ private static BuildConfigurationOptions GetBuildConfigurationOptions(ParseResul private static BuildPathsOptions GetBuildPathOptions(ParseResult parseResult) => new(parseResult.GetValue(TestingPlatformOptions.ProjectOption), - parseResult.GetValue(TestingPlatformOptions.SolutionOption)); + parseResult.GetValue(TestingPlatformOptions.SolutionOption), + parseResult.GetValue(TestingPlatformOptions.DirectoryOption)); private void InitializeActionQueue(ParseResult parseResult, int degreeOfParallelism, BuildConfigurationOptions buildConfigurationOptions) { diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformOptions.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformOptions.cs index 62ed471dc9ad..6e3b6e0e5484 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformOptions.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformOptions.cs @@ -63,5 +63,11 @@ internal static class TestingPlatformOptions Description = LocalizableStrings.CmdSolutionDescription, Arity = ArgumentArity.ExactlyOne }; + + public static readonly CliOption DirectoryOption = new("--directory") + { + Description = LocalizableStrings.CmdDirectoryDescription, + Arity = ArgumentArity.ExactlyOne + }; } } diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.cs.xlf index 0365e0458bd0..a9fbaefb534a 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.cs.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.cs.xlf @@ -83,6 +83,11 @@ Pro MSTest před 2.2.4 se časový limit použije pro všechny testovací příp Defines the build configuration. The default for most projects is Debug, but you can override the build configuration settings in your project. + + Defines the path of directory to run. If not specified, it defaults to the current directory. + Defines the path of directory to run. If not specified, it defaults to the current directory. + + Sets the value of an environment variable. Creates the variable if it does not exist, overrides if it does. @@ -113,12 +118,12 @@ Příklady: NÁZEV="HODNOTA" - + The provided project file has an invalid extension: {0}. The provided project file has an invalid extension: {0}. - + The provided solution file has an invalid extension: {0}. The provided solution file has an invalid extension: {0}. @@ -128,7 +133,7 @@ Příklady: Invalid test message state '{0}' {0} - test message state - + Get projects properties with MSBuild didn't execute properly with exit code: {0}. Get projects properties with MSBuild didn't execute properly with exit code: {0}. @@ -138,7 +143,12 @@ Příklady: Maximální počet testovacích modulů, které je možné spustit paralelně. - + + Specify either the project, solution or directory option. + Specify either the project, solution or directory option. + + + Specify which project or solution file to use because this folder contains more than one project or solution file. Specify which project or solution file to use because this folder contains more than one project or solution file. @@ -148,7 +158,7 @@ Příklady: Spustit testy bez zobrazení nápisu Microsoft Testplatform - + Specify a project or solution file. The current working directory does not contain a project or solution file. Specify a project or solution file. The current working directory does not contain a project or solution file. @@ -158,14 +168,14 @@ Příklady: Do not execute an implicit restore. - - The provided file path does not exist: {0}. - The provided file path does not exist: {0}. + + The provided directory path does not exist: {0}. + The provided directory path does not exist: {0}. - - Specify either the project or solution option. - Specify either the project or solution option. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.de.xlf index f686789b6135..4e49829eae4b 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.de.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.de.xlf @@ -83,6 +83,11 @@ für MSTest vor 2.2.4 wird das Timeout für alle Testfälle verwendet. Defines the build configuration. The default for most projects is Debug, but you can override the build configuration settings in your project. + + Defines the path of directory to run. If not specified, it defaults to the current directory. + Defines the path of directory to run. If not specified, it defaults to the current directory. + + Sets the value of an environment variable. Creates the variable if it does not exist, overrides if it does. @@ -113,12 +118,12 @@ Beispiele: NAME="WERT" - + The provided project file has an invalid extension: {0}. The provided project file has an invalid extension: {0}. - + The provided solution file has an invalid extension: {0}. The provided solution file has an invalid extension: {0}. @@ -128,7 +133,7 @@ Beispiele: Invalid test message state '{0}' {0} - test message state - + Get projects properties with MSBuild didn't execute properly with exit code: {0}. Get projects properties with MSBuild didn't execute properly with exit code: {0}. @@ -138,7 +143,12 @@ Beispiele: Die maximale Anzahl von Testmodulen, die parallel ausgeführt werden können. - + + Specify either the project, solution or directory option. + Specify either the project, solution or directory option. + + + Specify which project or solution file to use because this folder contains more than one project or solution file. Specify which project or solution file to use because this folder contains more than one project or solution file. @@ -148,7 +158,7 @@ Beispiele: Test(s) ohne Anzeige des Microsoft-Testplattformbanners ausführen - + Specify a project or solution file. The current working directory does not contain a project or solution file. Specify a project or solution file. The current working directory does not contain a project or solution file. @@ -158,14 +168,14 @@ Beispiele: Do not execute an implicit restore. - - The provided file path does not exist: {0}. - The provided file path does not exist: {0}. + + The provided directory path does not exist: {0}. + The provided directory path does not exist: {0}. - - Specify either the project or solution option. - Specify either the project or solution option. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.es.xlf index d0c2223572d5..2c8e4e80afcd 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.es.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.es.xlf @@ -85,6 +85,11 @@ Para MSTest antes de 2.2.4, el tiempo de espera se usa para todos los casos de p Defines the build configuration. The default for most projects is Debug, but you can override the build configuration settings in your project. + + Defines the path of directory to run. If not specified, it defaults to the current directory. + Defines the path of directory to run. If not specified, it defaults to the current directory. + + Sets the value of an environment variable. Creates the variable if it does not exist, overrides if it does. @@ -115,12 +120,12 @@ Ejemplos: NAME="VALUE" - + The provided project file has an invalid extension: {0}. The provided project file has an invalid extension: {0}. - + The provided solution file has an invalid extension: {0}. The provided solution file has an invalid extension: {0}. @@ -130,7 +135,7 @@ Ejemplos: Invalid test message state '{0}' {0} - test message state - + Get projects properties with MSBuild didn't execute properly with exit code: {0}. Get projects properties with MSBuild didn't execute properly with exit code: {0}. @@ -140,7 +145,12 @@ Ejemplos: Número máximo de módulos de prueba que se pueden ejecutar en paralelo. - + + Specify either the project, solution or directory option. + Specify either the project, solution or directory option. + + + Specify which project or solution file to use because this folder contains more than one project or solution file. Specify which project or solution file to use because this folder contains more than one project or solution file. @@ -150,7 +160,7 @@ Ejemplos: Ejecutar pruebas, sin mostrar la pancarta de Microsoft Testplatform - + Specify a project or solution file. The current working directory does not contain a project or solution file. Specify a project or solution file. The current working directory does not contain a project or solution file. @@ -160,14 +170,14 @@ Ejemplos: Do not execute an implicit restore. - - The provided file path does not exist: {0}. - The provided file path does not exist: {0}. + + The provided directory path does not exist: {0}. + The provided directory path does not exist: {0}. - - Specify either the project or solution option. - Specify either the project or solution option. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.fr.xlf index c738d6a56956..49049418f185 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.fr.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.fr.xlf @@ -83,6 +83,11 @@ Pour MSTest avant la version 2.2.4, le délai d’expiration est utilisé pour Defines the build configuration. The default for most projects is Debug, but you can override the build configuration settings in your project. + + Defines the path of directory to run. If not specified, it defaults to the current directory. + Defines the path of directory to run. If not specified, it defaults to the current directory. + + Sets the value of an environment variable. Creates the variable if it does not exist, overrides if it does. @@ -113,12 +118,12 @@ Exemples : NAME="VALUE" - + The provided project file has an invalid extension: {0}. The provided project file has an invalid extension: {0}. - + The provided solution file has an invalid extension: {0}. The provided solution file has an invalid extension: {0}. @@ -128,7 +133,7 @@ Exemples : Invalid test message state '{0}' {0} - test message state - + Get projects properties with MSBuild didn't execute properly with exit code: {0}. Get projects properties with MSBuild didn't execute properly with exit code: {0}. @@ -138,7 +143,12 @@ Exemples : Nombre maximal de modules de test qui peuvent s’exécuter en parallèle. - + + Specify either the project, solution or directory option. + Specify either the project, solution or directory option. + + + Specify which project or solution file to use because this folder contains more than one project or solution file. Specify which project or solution file to use because this folder contains more than one project or solution file. @@ -148,7 +158,7 @@ Exemples : Exécute le ou les tests, sans afficher la bannière Microsoft Testplatform - + Specify a project or solution file. The current working directory does not contain a project or solution file. Specify a project or solution file. The current working directory does not contain a project or solution file. @@ -158,14 +168,14 @@ Exemples : Do not execute an implicit restore. - - The provided file path does not exist: {0}. - The provided file path does not exist: {0}. + + The provided directory path does not exist: {0}. + The provided directory path does not exist: {0}. - - Specify either the project or solution option. - Specify either the project or solution option. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.it.xlf index aee1d986b935..3cd092f61652 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.it.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.it.xlf @@ -83,6 +83,11 @@ Per MSTest prima di 2.2.4, il timeout viene usato per tutti i test case.Defines the build configuration. The default for most projects is Debug, but you can override the build configuration settings in your project. + + Defines the path of directory to run. If not specified, it defaults to the current directory. + Defines the path of directory to run. If not specified, it defaults to the current directory. + + Sets the value of an environment variable. Creates the variable if it does not exist, overrides if it does. @@ -113,12 +118,12 @@ Esempi: NAME="VALORE" - + The provided project file has an invalid extension: {0}. The provided project file has an invalid extension: {0}. - + The provided solution file has an invalid extension: {0}. The provided solution file has an invalid extension: {0}. @@ -128,7 +133,7 @@ Esempi: Invalid test message state '{0}' {0} - test message state - + Get projects properties with MSBuild didn't execute properly with exit code: {0}. Get projects properties with MSBuild didn't execute properly with exit code: {0}. @@ -138,7 +143,12 @@ Esempi: Numero massimo di moduli di test che possono essere eseguiti in parallelo. - + + Specify either the project, solution or directory option. + Specify either the project, solution or directory option. + + + Specify which project or solution file to use because this folder contains more than one project or solution file. Specify which project or solution file to use because this folder contains more than one project or solution file. @@ -148,7 +158,7 @@ Esempi: Esegui test senza visualizzare il banner di Microsoft Testplatform - + Specify a project or solution file. The current working directory does not contain a project or solution file. Specify a project or solution file. The current working directory does not contain a project or solution file. @@ -158,14 +168,14 @@ Esempi: Do not execute an implicit restore. - - The provided file path does not exist: {0}. - The provided file path does not exist: {0}. + + The provided directory path does not exist: {0}. + The provided directory path does not exist: {0}. - - Specify either the project or solution option. - Specify either the project or solution option. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ja.xlf index 3c70d07cf3af..5f02efcee8de 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ja.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ja.xlf @@ -83,6 +83,11 @@ MSTest 2.2.4 以前の場合、タイムアウトはすべてのテスト ケー Defines the build configuration. The default for most projects is Debug, but you can override the build configuration settings in your project. + + Defines the path of directory to run. If not specified, it defaults to the current directory. + Defines the path of directory to run. If not specified, it defaults to the current directory. + + Sets the value of an environment variable. Creates the variable if it does not exist, overrides if it does. @@ -113,12 +118,12 @@ Examples: NAME="VALUE" - + The provided project file has an invalid extension: {0}. The provided project file has an invalid extension: {0}. - + The provided solution file has an invalid extension: {0}. The provided solution file has an invalid extension: {0}. @@ -128,7 +133,7 @@ Examples: Invalid test message state '{0}' {0} - test message state - + Get projects properties with MSBuild didn't execute properly with exit code: {0}. Get projects properties with MSBuild didn't execute properly with exit code: {0}. @@ -138,7 +143,12 @@ Examples: 並列で実行できるテスト モジュールの最大数。 - + + Specify either the project, solution or directory option. + Specify either the project, solution or directory option. + + + Specify which project or solution file to use because this folder contains more than one project or solution file. Specify which project or solution file to use because this folder contains more than one project or solution file. @@ -148,7 +158,7 @@ Examples: Microsoft Testplatform バナーを表示せずにテストを実行する - + Specify a project or solution file. The current working directory does not contain a project or solution file. Specify a project or solution file. The current working directory does not contain a project or solution file. @@ -158,14 +168,14 @@ Examples: Do not execute an implicit restore. - - The provided file path does not exist: {0}. - The provided file path does not exist: {0}. + + The provided directory path does not exist: {0}. + The provided directory path does not exist: {0}. - - Specify either the project or solution option. - Specify either the project or solution option. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ko.xlf index 02eb5e91656f..d8b479b39995 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ko.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ko.xlf @@ -83,6 +83,11 @@ For MSTest before 2.2.4, the timeout is used for all testcases. Defines the build configuration. The default for most projects is Debug, but you can override the build configuration settings in your project. + + Defines the path of directory to run. If not specified, it defaults to the current directory. + Defines the path of directory to run. If not specified, it defaults to the current directory. + + Sets the value of an environment variable. Creates the variable if it does not exist, overrides if it does. @@ -113,12 +118,12 @@ Examples: NAME="VALUE" - + The provided project file has an invalid extension: {0}. The provided project file has an invalid extension: {0}. - + The provided solution file has an invalid extension: {0}. The provided solution file has an invalid extension: {0}. @@ -128,7 +133,7 @@ Examples: Invalid test message state '{0}' {0} - test message state - + Get projects properties with MSBuild didn't execute properly with exit code: {0}. Get projects properties with MSBuild didn't execute properly with exit code: {0}. @@ -138,7 +143,12 @@ Examples: 병렬로 실행할 수 있는 최대 테스트 모듈 수입니다. - + + Specify either the project, solution or directory option. + Specify either the project, solution or directory option. + + + Specify which project or solution file to use because this folder contains more than one project or solution file. Specify which project or solution file to use because this folder contains more than one project or solution file. @@ -148,7 +158,7 @@ Examples: Microsoft Testplatform 배너를 표시하지 않고 테스트 실행 - + Specify a project or solution file. The current working directory does not contain a project or solution file. Specify a project or solution file. The current working directory does not contain a project or solution file. @@ -158,14 +168,14 @@ Examples: Do not execute an implicit restore. - - The provided file path does not exist: {0}. - The provided file path does not exist: {0}. + + The provided directory path does not exist: {0}. + The provided directory path does not exist: {0}. - - Specify either the project or solution option. - Specify either the project or solution option. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pl.xlf index 009a95303718..b7a4dabc55f4 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pl.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pl.xlf @@ -83,6 +83,11 @@ W przypadku platformy MSTest przed wersją 2.2.4 limit czasu jest używany dla w Defines the build configuration. The default for most projects is Debug, but you can override the build configuration settings in your project. + + Defines the path of directory to run. If not specified, it defaults to the current directory. + Defines the path of directory to run. If not specified, it defaults to the current directory. + + Sets the value of an environment variable. Creates the variable if it does not exist, overrides if it does. @@ -113,12 +118,12 @@ Przykłady: NAZWA="WARTOŚĆ" - + The provided project file has an invalid extension: {0}. The provided project file has an invalid extension: {0}. - + The provided solution file has an invalid extension: {0}. The provided solution file has an invalid extension: {0}. @@ -128,7 +133,7 @@ Przykłady: Invalid test message state '{0}' {0} - test message state - + Get projects properties with MSBuild didn't execute properly with exit code: {0}. Get projects properties with MSBuild didn't execute properly with exit code: {0}. @@ -138,7 +143,12 @@ Przykłady: Maksymalna liczba modułów testowych, które mogą być uruchamiane równolegle. - + + Specify either the project, solution or directory option. + Specify either the project, solution or directory option. + + + Specify which project or solution file to use because this folder contains more than one project or solution file. Specify which project or solution file to use because this folder contains more than one project or solution file. @@ -148,7 +158,7 @@ Przykłady: Uruchom testy bez wyświetlania baneru platformy testowej firmy Microsoft - + Specify a project or solution file. The current working directory does not contain a project or solution file. Specify a project or solution file. The current working directory does not contain a project or solution file. @@ -158,14 +168,14 @@ Przykłady: Do not execute an implicit restore. - - The provided file path does not exist: {0}. - The provided file path does not exist: {0}. + + The provided directory path does not exist: {0}. + The provided directory path does not exist: {0}. - - Specify either the project or solution option. - Specify either the project or solution option. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pt-BR.xlf index bf8075725dca..17f1821a040c 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pt-BR.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pt-BR.xlf @@ -83,6 +83,11 @@ Para MSTest antes de 2.2.4, o tempo limite é usado para todos os casos de teste Defines the build configuration. The default for most projects is Debug, but you can override the build configuration settings in your project. + + Defines the path of directory to run. If not specified, it defaults to the current directory. + Defines the path of directory to run. If not specified, it defaults to the current directory. + + Sets the value of an environment variable. Creates the variable if it does not exist, overrides if it does. @@ -113,12 +118,12 @@ Exemplos: NAME="VALUE" - + The provided project file has an invalid extension: {0}. The provided project file has an invalid extension: {0}. - + The provided solution file has an invalid extension: {0}. The provided solution file has an invalid extension: {0}. @@ -128,7 +133,7 @@ Exemplos: Invalid test message state '{0}' {0} - test message state - + Get projects properties with MSBuild didn't execute properly with exit code: {0}. Get projects properties with MSBuild didn't execute properly with exit code: {0}. @@ -138,7 +143,12 @@ Exemplos: O número máximo de módulos de teste que podem ser executados em paralelo. - + + Specify either the project, solution or directory option. + Specify either the project, solution or directory option. + + + Specify which project or solution file to use because this folder contains more than one project or solution file. Specify which project or solution file to use because this folder contains more than one project or solution file. @@ -148,7 +158,7 @@ Exemplos: Executar testes, sem exibir a faixa do Microsoft Testplatform - + Specify a project or solution file. The current working directory does not contain a project or solution file. Specify a project or solution file. The current working directory does not contain a project or solution file. @@ -158,14 +168,14 @@ Exemplos: Do not execute an implicit restore. - - The provided file path does not exist: {0}. - The provided file path does not exist: {0}. + + The provided directory path does not exist: {0}. + The provided directory path does not exist: {0}. - - Specify either the project or solution option. - Specify either the project or solution option. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ru.xlf index 9997c9e5ea45..0180857c5c29 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ru.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ru.xlf @@ -83,6 +83,11 @@ For MSTest before 2.2.4, the timeout is used for all testcases. Defines the build configuration. The default for most projects is Debug, but you can override the build configuration settings in your project. + + Defines the path of directory to run. If not specified, it defaults to the current directory. + Defines the path of directory to run. If not specified, it defaults to the current directory. + + Sets the value of an environment variable. Creates the variable if it does not exist, overrides if it does. @@ -113,12 +118,12 @@ Examples: NAME="VALUE" - + The provided project file has an invalid extension: {0}. The provided project file has an invalid extension: {0}. - + The provided solution file has an invalid extension: {0}. The provided solution file has an invalid extension: {0}. @@ -128,7 +133,7 @@ Examples: Invalid test message state '{0}' {0} - test message state - + Get projects properties with MSBuild didn't execute properly with exit code: {0}. Get projects properties with MSBuild didn't execute properly with exit code: {0}. @@ -138,7 +143,12 @@ Examples: Максимальное число тестовых модулей, которые могут выполняться параллельно. - + + Specify either the project, solution or directory option. + Specify either the project, solution or directory option. + + + Specify which project or solution file to use because this folder contains more than one project or solution file. Specify which project or solution file to use because this folder contains more than one project or solution file. @@ -148,7 +158,7 @@ Examples: Запуск тестов без отображения баннера Testplatform Майкрософт - + Specify a project or solution file. The current working directory does not contain a project or solution file. Specify a project or solution file. The current working directory does not contain a project or solution file. @@ -158,14 +168,14 @@ Examples: Do not execute an implicit restore. - - The provided file path does not exist: {0}. - The provided file path does not exist: {0}. + + The provided directory path does not exist: {0}. + The provided directory path does not exist: {0}. - - Specify either the project or solution option. - Specify either the project or solution option. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.tr.xlf index 8933d9aa4f16..2f0659ae58cd 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.tr.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.tr.xlf @@ -83,6 +83,11 @@ Zaman aşımı davranışı veri tabanlı testlerle birlikte kullanıldığında Defines the build configuration. The default for most projects is Debug, but you can override the build configuration settings in your project. + + Defines the path of directory to run. If not specified, it defaults to the current directory. + Defines the path of directory to run. If not specified, it defaults to the current directory. + + Sets the value of an environment variable. Creates the variable if it does not exist, overrides if it does. @@ -113,12 +118,12 @@ Bu bağımsız değişken, birden çok değişken sağlamak için birden çok ke AD="DEĞER" - + The provided project file has an invalid extension: {0}. The provided project file has an invalid extension: {0}. - + The provided solution file has an invalid extension: {0}. The provided solution file has an invalid extension: {0}. @@ -128,7 +133,7 @@ Bu bağımsız değişken, birden çok değişken sağlamak için birden çok ke Invalid test message state '{0}' {0} - test message state - + Get projects properties with MSBuild didn't execute properly with exit code: {0}. Get projects properties with MSBuild didn't execute properly with exit code: {0}. @@ -138,7 +143,12 @@ Bu bağımsız değişken, birden çok değişken sağlamak için birden çok ke Paralel olarak çalıştırılabilecek test modüllerinin maksimum sayısı. - + + Specify either the project, solution or directory option. + Specify either the project, solution or directory option. + + + Specify which project or solution file to use because this folder contains more than one project or solution file. Specify which project or solution file to use because this folder contains more than one project or solution file. @@ -148,7 +158,7 @@ Bu bağımsız değişken, birden çok değişken sağlamak için birden çok ke Testleri Microsoft Testplatform bandını görüntülemeden çalıştır - + Specify a project or solution file. The current working directory does not contain a project or solution file. Specify a project or solution file. The current working directory does not contain a project or solution file. @@ -158,14 +168,14 @@ Bu bağımsız değişken, birden çok değişken sağlamak için birden çok ke Do not execute an implicit restore. - - The provided file path does not exist: {0}. - The provided file path does not exist: {0}. + + The provided directory path does not exist: {0}. + The provided directory path does not exist: {0}. - - Specify either the project or solution option. - Specify either the project or solution option. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hans.xlf index fd2fb9f0e6b5..b178d24834a7 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hans.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hans.xlf @@ -83,6 +83,11 @@ For MSTest before 2.2.4, the timeout is used for all testcases. Defines the build configuration. The default for most projects is Debug, but you can override the build configuration settings in your project. + + Defines the path of directory to run. If not specified, it defaults to the current directory. + Defines the path of directory to run. If not specified, it defaults to the current directory. + + Sets the value of an environment variable. Creates the variable if it does not exist, overrides if it does. @@ -113,12 +118,12 @@ Examples: NAME="VALUE" - + The provided project file has an invalid extension: {0}. The provided project file has an invalid extension: {0}. - + The provided solution file has an invalid extension: {0}. The provided solution file has an invalid extension: {0}. @@ -128,7 +133,7 @@ Examples: Invalid test message state '{0}' {0} - test message state - + Get projects properties with MSBuild didn't execute properly with exit code: {0}. Get projects properties with MSBuild didn't execute properly with exit code: {0}. @@ -138,7 +143,12 @@ Examples: 可并行运行的测试模块的最大数目。 - + + Specify either the project, solution or directory option. + Specify either the project, solution or directory option. + + + Specify which project or solution file to use because this folder contains more than one project or solution file. Specify which project or solution file to use because this folder contains more than one project or solution file. @@ -148,7 +158,7 @@ Examples: 运行测试,而不显示 Microsoft Testplatform 版权标志 - + Specify a project or solution file. The current working directory does not contain a project or solution file. Specify a project or solution file. The current working directory does not contain a project or solution file. @@ -158,14 +168,14 @@ Examples: Do not execute an implicit restore. - - The provided file path does not exist: {0}. - The provided file path does not exist: {0}. + + The provided directory path does not exist: {0}. + The provided directory path does not exist: {0}. - - Specify either the project or solution option. - Specify either the project or solution option. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}. diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hant.xlf index 2f85ecffe6ff..bca91b0748e9 100644 --- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hant.xlf +++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hant.xlf @@ -83,6 +83,11 @@ For MSTest before 2.2.4, the timeout is used for all testcases. Defines the build configuration. The default for most projects is Debug, but you can override the build configuration settings in your project. + + Defines the path of directory to run. If not specified, it defaults to the current directory. + Defines the path of directory to run. If not specified, it defaults to the current directory. + + Sets the value of an environment variable. Creates the variable if it does not exist, overrides if it does. @@ -113,12 +118,12 @@ Examples: NAME="VALUE" - + The provided project file has an invalid extension: {0}. The provided project file has an invalid extension: {0}. - + The provided solution file has an invalid extension: {0}. The provided solution file has an invalid extension: {0}. @@ -128,7 +133,7 @@ Examples: Invalid test message state '{0}' {0} - test message state - + Get projects properties with MSBuild didn't execute properly with exit code: {0}. Get projects properties with MSBuild didn't execute properly with exit code: {0}. @@ -138,7 +143,12 @@ Examples: 可平行執行的測試模組數目上限。 - + + Specify either the project, solution or directory option. + Specify either the project, solution or directory option. + + + Specify which project or solution file to use because this folder contains more than one project or solution file. Specify which project or solution file to use because this folder contains more than one project or solution file. @@ -148,7 +158,7 @@ Examples: 執行測試,但不顯示 Microsoft Testplatform 橫幅 - + Specify a project or solution file. The current working directory does not contain a project or solution file. Specify a project or solution file. The current working directory does not contain a project or solution file. @@ -158,14 +168,14 @@ Examples: Do not execute an implicit restore. - - The provided file path does not exist: {0}. - The provided file path does not exist: {0}. + + The provided directory path does not exist: {0}. + The provided directory path does not exist: {0}. - - Specify either the project or solution option. - Specify either the project or solution option. + + The provided file path does not exist: {0}. + The provided file path does not exist: {0}.