Skip to content

Commit 289053b

Browse files
Merge main into darc-main-3179108c-c6b8-45ea-ab29-d26b162530ec
2 parents 3308ce3 + 0c167a0 commit 289053b

File tree

16 files changed

+104
-46
lines changed

16 files changed

+104
-46
lines changed

src/Cli/dotnet/Commands/Test/MSBuildUtility.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public static (IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModul
5353
FacadeLogger? logger = LoggerUtility.DetermineBinlogger([.. buildOptions.MSBuildArgs], dotnetTestVerb);
5454
var collection = new ProjectCollection(globalProperties: CommonRunHelpers.GetGlobalPropertiesFromArgs([.. buildOptions.MSBuildArgs]), logger is null ? null : [logger], toolsetDefinitionLocations: ToolsetDefinitionLocations.Default);
5555

56-
IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModules> projects = SolutionAndProjectUtility.GetProjectProperties(projectFilePath, collection, buildOptions.NoLaunchProfile);
56+
IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModules> projects = SolutionAndProjectUtility.GetProjectProperties(projectFilePath, collection, buildOptions);
5757
logger?.ReallyShutdown();
5858

5959
return (projects, isBuiltOrRestored);
@@ -66,10 +66,17 @@ public static BuildOptions GetBuildOptions(ParseResult parseResult, int degreeOf
6666
var msbuildArgs = parseResult.OptionValuesToBeForwarded(TestCommandParser.GetCommand())
6767
.Concat(binLogArgs);
6868

69+
string? resultsDirectory = parseResult.GetValue(TestingPlatformOptions.ResultsDirectoryOption);
70+
if (resultsDirectory is not null)
71+
{
72+
resultsDirectory = Path.Combine(Directory.GetCurrentDirectory(), resultsDirectory);
73+
}
74+
6975
PathOptions pathOptions = new(
7076
parseResult.GetValue(TestingPlatformOptions.ProjectOption),
7177
parseResult.GetValue(TestingPlatformOptions.SolutionOption),
72-
parseResult.GetValue(TestingPlatformOptions.DirectoryOption));
78+
parseResult.GetValue(TestingPlatformOptions.DirectoryOption),
79+
resultsDirectory);
7380

7481
return new BuildOptions(
7582
pathOptions,
@@ -112,7 +119,7 @@ private static ConcurrentBag<ParallelizableTestModuleGroupWithSequentialInnerMod
112119
new ParallelOptions { MaxDegreeOfParallelism = buildOptions.DegreeOfParallelism },
113120
(project) =>
114121
{
115-
IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModules> projectsMetadata = SolutionAndProjectUtility.GetProjectProperties(project, projectCollection, buildOptions.NoLaunchProfile);
122+
IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModules> projectsMetadata = SolutionAndProjectUtility.GetProjectProperties(project, projectCollection, buildOptions);
116123
foreach (var projectMetadata in projectsMetadata)
117124
{
118125
allProjects.Add(projectMetadata);

src/Cli/dotnet/Commands/Test/Models.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ public bool MoveNext()
112112
}
113113

114114

115-
internal sealed record TestModule(RunProperties RunProperties, string? ProjectFullPath, string? TargetFramework, bool IsTestingPlatformApplication, bool IsTestProject, ProjectLaunchSettingsModel? LaunchSettings, string TargetPath);
115+
internal sealed record TestModule(RunProperties RunProperties, string? ProjectFullPath, string? TargetFramework, bool IsTestingPlatformApplication, bool IsTestProject, ProjectLaunchSettingsModel? LaunchSettings, string TargetPath, string? DotnetRootArchVariableName);
116116

117117
internal sealed record Handshake(Dictionary<byte, string>? Properties);
118118

src/Cli/dotnet/Commands/Test/Options.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace Microsoft.DotNet.Cli.Commands.Test;
55

66
internal record TestOptions(bool HasFilterMode, bool IsHelp);
77

8-
internal record PathOptions(string? ProjectPath, string? SolutionPath, string? DirectoryPath);
8+
internal record PathOptions(string? ProjectPath, string? SolutionPath, string? DirectoryPath, string? ResultsDirectoryPath);
99

1010
internal record BuildOptions(
1111
PathOptions PathOptions,

src/Cli/dotnet/Commands/Test/SolutionAndProjectUtility.cs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.CommandLine;
45
using System.Diagnostics;
56
using Microsoft.Build.Evaluation;
67
using Microsoft.Build.Execution;
@@ -147,7 +148,7 @@ public static string GetRootDirectory(string solutionOrProjectFilePath)
147148
return string.IsNullOrEmpty(fileDirectory) ? Directory.GetCurrentDirectory() : fileDirectory;
148149
}
149150

150-
public static IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModules> GetProjectProperties(string projectFilePath, ProjectCollection projectCollection, bool noLaunchProfile)
151+
public static IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModules> GetProjectProperties(string projectFilePath, ProjectCollection projectCollection, BuildOptions buildOptions)
151152
{
152153
var projects = new List<ParallelizableTestModuleGroupWithSequentialInnerModules>();
153154
ProjectInstance projectInstance = EvaluateProject(projectCollection, projectFilePath, null);
@@ -159,7 +160,7 @@ public static IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModule
159160

160161
if (!string.IsNullOrEmpty(targetFramework) || string.IsNullOrEmpty(targetFrameworks))
161162
{
162-
if (GetModuleFromProject(projectInstance, projectCollection.Loggers, noLaunchProfile) is { } module)
163+
if (GetModuleFromProject(projectInstance, projectCollection.Loggers, buildOptions) is { } module)
163164
{
164165
projects.Add(new ParallelizableTestModuleGroupWithSequentialInnerModules(module));
165166
}
@@ -187,7 +188,7 @@ public static IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModule
187188
projectInstance = EvaluateProject(projectCollection, projectFilePath, framework);
188189
Logger.LogTrace(() => $"Loaded inner project '{Path.GetFileName(projectFilePath)}' has '{ProjectProperties.IsTestingPlatformApplication}' = '{projectInstance.GetPropertyValue(ProjectProperties.IsTestingPlatformApplication)}' (TFM: '{framework}').");
189190

190-
if (GetModuleFromProject(projectInstance, projectCollection.Loggers, noLaunchProfile) is { } module)
191+
if (GetModuleFromProject(projectInstance, projectCollection.Loggers, buildOptions) is { } module)
191192
{
192193
projects.Add(new ParallelizableTestModuleGroupWithSequentialInnerModules(module));
193194
}
@@ -201,7 +202,7 @@ public static IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModule
201202
projectInstance = EvaluateProject(projectCollection, projectFilePath, framework);
202203
Logger.LogTrace(() => $"Loaded inner project '{Path.GetFileName(projectFilePath)}' has '{ProjectProperties.IsTestingPlatformApplication}' = '{projectInstance.GetPropertyValue(ProjectProperties.IsTestingPlatformApplication)}' (TFM: '{framework}').");
203204

204-
if (GetModuleFromProject(projectInstance, projectCollection.Loggers, noLaunchProfile) is { } module)
205+
if (GetModuleFromProject(projectInstance, projectCollection.Loggers, buildOptions) is { } module)
205206
{
206207
innerModules ??= new List<TestModule>();
207208
innerModules.Add(module);
@@ -218,7 +219,7 @@ public static IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModule
218219
return projects;
219220
}
220221

221-
private static TestModule? GetModuleFromProject(ProjectInstance project, ICollection<ILogger>? loggers, bool noLaunchProfile)
222+
private static TestModule? GetModuleFromProject(ProjectInstance project, ICollection<ILogger>? loggers, BuildOptions buildOptions)
222223
{
223224
_ = bool.TryParse(project.GetPropertyValue(ProjectProperties.IsTestProject), out bool isTestProject);
224225
_ = bool.TryParse(project.GetPropertyValue(ProjectProperties.IsTestingPlatformApplication), out bool isTestingPlatformApplication);
@@ -248,9 +249,19 @@ public static IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModule
248249
string projectFullPath = project.GetPropertyValue(ProjectProperties.ProjectFullPath);
249250

250251
// TODO: Support --launch-profile and pass it here.
251-
var launchSettings = TryGetLaunchProfileSettings(Path.GetDirectoryName(projectFullPath)!, Path.GetFileNameWithoutExtension(projectFullPath), project.GetPropertyValue(ProjectProperties.AppDesignerFolder), noLaunchProfile, profileName: null);
252+
var launchSettings = TryGetLaunchProfileSettings(Path.GetDirectoryName(projectFullPath)!, Path.GetFileNameWithoutExtension(projectFullPath), project.GetPropertyValue(ProjectProperties.AppDesignerFolder), buildOptions, profileName: null);
252253

253-
return new TestModule(runProperties, PathUtility.FixFilePath(projectFullPath), targetFramework, isTestingPlatformApplication, isTestProject, launchSettings, project.GetPropertyValue(ProjectProperties.TargetPath));
254+
var rootVariableName = EnvironmentVariableNames.TryGetDotNetRootArchVariableName(
255+
project.GetPropertyValue("RuntimeIdentifier"),
256+
project.GetPropertyValue("DefaultAppHostRuntimeIdentifier"));
257+
258+
if (rootVariableName is not null && Environment.GetEnvironmentVariable(rootVariableName) != null)
259+
{
260+
// If already set, we do not override it.
261+
rootVariableName = null;
262+
}
263+
264+
return new TestModule(runProperties, PathUtility.FixFilePath(projectFullPath), targetFramework, isTestingPlatformApplication, isTestProject, launchSettings, project.GetPropertyValue(ProjectProperties.TargetPath), rootVariableName);
254265

255266
static RunProperties GetRunProperties(ProjectInstance project, ICollection<ILogger>? loggers)
256267
{
@@ -270,9 +281,9 @@ static RunProperties GetRunProperties(ProjectInstance project, ICollection<ILogg
270281
}
271282
}
272283

273-
private static ProjectLaunchSettingsModel? TryGetLaunchProfileSettings(string projectDirectory, string projectNameWithoutExtension, string appDesignerFolder, bool noLaunchProfile, string? profileName)
284+
private static ProjectLaunchSettingsModel? TryGetLaunchProfileSettings(string projectDirectory, string projectNameWithoutExtension, string appDesignerFolder, BuildOptions buildOptions, string? profileName)
274285
{
275-
if (noLaunchProfile)
286+
if (buildOptions.NoLaunchProfile)
276287
{
277288
return null;
278289
}
@@ -299,6 +310,12 @@ static RunProperties GetRunProperties(ProjectInstance project, ICollection<ILogg
299310
return null;
300311
}
301312

313+
// If buildOptions.Verbosity is null, we still want to print the message.
314+
if (buildOptions.Verbosity != VerbosityOptions.quiet)
315+
{
316+
Reporter.Output.WriteLine(string.Format(CliCommandStrings.UsingLaunchSettingsFromMessage, launchSettingsPath));
317+
}
318+
302319
var result = LaunchSettingsManager.TryApplyLaunchSettings(launchSettingsPath, profileName);
303320
if (!result.Success)
304321
{

src/Cli/dotnet/Commands/Test/Terminal/TerminalTestReporter.cs

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -427,28 +427,21 @@ private void AppendExitCodeAndUrl(ITerminal terminal, int? exitCode, bool isRun)
427427
/// <summary>
428428
/// Print a build result summary to the output.
429429
/// </summary>
430-
private static void AppendAssemblyResult(ITerminal terminal, bool succeeded, int countErrors, int countWarnings)
430+
private static void AppendAssemblyResult(ITerminal terminal, TestProgressState state)
431431
{
432-
if (!succeeded)
432+
if (!state.Success)
433433
{
434434
terminal.SetColor(TerminalColor.DarkRed);
435435
// If the build failed, we print one of three red strings.
436-
string text = (countErrors > 0, countWarnings > 0) switch
436+
string text = (state.FailedTests > 0, state.TotalTests == 0) switch
437437
{
438-
(true, true) => string.Format(CultureInfo.CurrentCulture, CliCommandStrings.FailedWithErrorsAndWarnings, countErrors, countWarnings),
439-
(true, _) => string.Format(CultureInfo.CurrentCulture, CliCommandStrings.FailedWithErrors, countErrors),
440-
(false, true) => string.Format(CultureInfo.CurrentCulture, CliCommandStrings.FailedWithWarnings, countWarnings),
441-
_ => CliCommandStrings.FailedLowercase,
438+
(true, _) => string.Format(CultureInfo.CurrentCulture, CliCommandStrings.FailedWithErrors, state.FailedTests),
439+
(false, true) => CliCommandStrings.ZeroTestsRan,
440+
(false, false) => CliCommandStrings.FailedLowercase,
442441
};
443442
terminal.Append(text);
444443
terminal.ResetColor();
445444
}
446-
else if (countWarnings > 0)
447-
{
448-
terminal.SetColor(TerminalColor.DarkYellow);
449-
terminal.Append($"succeeded with {countWarnings} warning(s)");
450-
terminal.ResetColor();
451-
}
452445
else
453446
{
454447
terminal.SetColor(TerminalColor.DarkGreen);
@@ -858,12 +851,10 @@ void AppendOutputWhenPresent(string description, string? output)
858851
private static void AppendAssemblySummary(TestProgressState assemblyRun, ITerminal terminal)
859852
{
860853
terminal.ResetColor();
861-
int failedTests = assemblyRun.FailedTests;
862-
int warnings = 0;
863-
854+
864855
AppendAssemblyLinkTargetFrameworkAndArchitecture(terminal, assemblyRun.Assembly, assemblyRun.TargetFramework, assemblyRun.Architecture);
865856
terminal.Append(' ');
866-
AppendAssemblyResult(terminal, assemblyRun.Success, failedTests, warnings);
857+
AppendAssemblyResult(terminal, assemblyRun);
867858
terminal.Append(' ');
868859
AppendLongDuration(terminal, assemblyRun.Stopwatch.Elapsed);
869860
terminal.AppendLine();

src/Cli/dotnet/Commands/Test/TestApplication.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#nullable disable
55

6+
using System.CommandLine;
67
using System.Diagnostics;
78
using System.IO.Pipes;
89
using Microsoft.DotNet.Cli.Commands.Test.IPC;
@@ -62,7 +63,7 @@ private ProcessStartInfo CreateProcessStartInfo(TestOptions testOptions)
6263
FileName = Module.RunProperties.RunCommand,
6364
Arguments = GetArguments(testOptions),
6465
RedirectStandardOutput = true,
65-
RedirectStandardError = true
66+
RedirectStandardError = true,
6667
};
6768

6869
if (!string.IsNullOrEmpty(Module.RunProperties.RunWorkingDirectory))
@@ -75,7 +76,7 @@ private ProcessStartInfo CreateProcessStartInfo(TestOptions testOptions)
7576
foreach (var entry in Module.LaunchSettings.EnvironmentVariables)
7677
{
7778
string value = Environment.ExpandEnvironmentVariables(entry.Value);
78-
processStartInfo.EnvironmentVariables[entry.Key] = value;
79+
processStartInfo.Environment[entry.Key] = value;
7980
}
8081

8182
if (!_buildOptions.NoLaunchProfileArguments &&
@@ -85,6 +86,11 @@ private ProcessStartInfo CreateProcessStartInfo(TestOptions testOptions)
8586
}
8687
}
8788

89+
if (Module.DotnetRootArchVariableName is not null)
90+
{
91+
processStartInfo.Environment[Module.DotnetRootArchVariableName] = Path.GetDirectoryName(new Muxer().MuxerPath);
92+
}
93+
8894
return processStartInfo;
8995
}
9096

@@ -104,6 +110,11 @@ private string GetArguments(TestOptions testOptions)
104110
builder.Append($" {TestingPlatformOptions.HelpOption.Name}");
105111
}
106112

113+
if (_buildOptions.PathOptions.ResultsDirectoryPath is { } resultsDirectoryPath)
114+
{
115+
builder.Append($" {TestingPlatformOptions.ResultsDirectoryOption.Name} {ArgumentEscaper.EscapeSingleArg(resultsDirectoryPath)}");
116+
}
117+
107118
foreach (var arg in _buildOptions.UnmatchedTokens)
108119
{
109120
builder.Append($" {ArgumentEscaper.EscapeSingleArg(arg)}");

src/Cli/dotnet/Commands/Test/TestCommandParser.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ private static Command GetTestingPlatformCliCommand()
235235
command.Options.Add(TestingPlatformOptions.DirectoryOption);
236236
command.Options.Add(TestingPlatformOptions.TestModulesFilterOption);
237237
command.Options.Add(TestingPlatformOptions.TestModulesRootDirectoryOption);
238+
command.Options.Add(TestingPlatformOptions.ResultsDirectoryOption);
238239
command.Options.Add(TestingPlatformOptions.MaxParallelTestModulesOption);
239240
command.Options.Add(CommonOptions.ArchitectureOption);
240241
command.Options.Add(CommonOptions.PropertiesOption);
@@ -293,6 +294,7 @@ private static Command GetVSTestCliCommand()
293294
command.Options.Add(VerbosityOption);
294295
command.Options.Add(CommonOptions.ArchitectureOption);
295296
command.Options.Add(CommonOptions.OperatingSystemOption);
297+
command.Options.Add(CommonOptions.PropertiesOption);
296298
command.Options.Add(CommonOptions.DisableBuildServersOption);
297299
command.Options.Add(VsTestTargetOption);
298300
command.SetAction(TestCommand.Run);

src/Cli/dotnet/Commands/Test/TestModulesFilterHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public bool RunWithTestModulesFilter(ParseResult parseResult)
5858
? new RunProperties(muxerPath, $@"exec ""{testModule}""", null)
5959
: new RunProperties(testModule, null, null);
6060

61-
var testApp = new ParallelizableTestModuleGroupWithSequentialInnerModules(new TestModule(runProperties, null, null, true, true, null, testModule));
61+
var testApp = new ParallelizableTestModuleGroupWithSequentialInnerModules(new TestModule(runProperties, null, null, true, true, null, testModule, DotnetRootArchVariableName: null));
6262
// Write the test application to the channel
6363
_actionQueue.Enqueue(testApp);
6464
}

src/Cli/dotnet/Commands/Test/TestingPlatformOptions.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ internal static class TestingPlatformOptions
4343
HelpName = CliCommandStrings.CmdRootPathName,
4444
};
4545

46+
public static readonly Option<string> ResultsDirectoryOption = new("--results-directory")
47+
{
48+
Description = CliCommandStrings.CmdResultsDirectoryDescription,
49+
HelpName = CliCommandStrings.CmdPathToResultsDirectory,
50+
Arity = ArgumentArity.ExactlyOne
51+
};
52+
4653
public static readonly Option<string> MaxParallelTestModulesOption = new("--max-parallel-test-modules")
4754
{
4855
Description = CliCommandStrings.CmdMaxParallelTestModulesDescription,

src/Common/EnvironmentVariableNames.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ static class EnvironmentVariableNames
3535
public static string? TryGetDotNetRootVariableName(string runtimeIdentifier, string defaultAppHostRuntimeIdentifier, Version? targetFrameworkVersion)
3636
=> TryGetDotNetRootVariableNameImpl(runtimeIdentifier, defaultAppHostRuntimeIdentifier, targetFrameworkVersion, RuntimeInformation.ProcessArchitecture, Environment.Is64BitProcess);
3737

38-
internal static string? TryGetDotNetRootVariableNameImpl(string runtimeIdentifier, string defaultAppHostRuntimeIdentifier, Version? targetFrameworkVersion, Architecture currentArchitecture, bool is64bit)
38+
internal static string? TryGetDotNetRootVariableNameImpl(string runtimeIdentifier, string defaultAppHostRuntimeIdentifier, Version? targetFrameworkVersion, Architecture currentArchitecture, bool is64bit, bool onlyUseArchSpecific = false)
3939
{
4040
// If the app targets the same architecture as SDK is running on or an unknown architecture, set DOTNET_ROOT, DOTNET_ROOT(x86) for 32-bit, DOTNET_ROOT_arch for TFM 6+.
4141
// If the app targets different architecture from the SDK, do not set DOTNET_ROOT.
4242

4343
if (!TryParseArchitecture(runtimeIdentifier, out var targetArchitecture) && !TryParseArchitecture(defaultAppHostRuntimeIdentifier, out targetArchitecture) ||
4444
targetArchitecture == currentArchitecture)
4545
{
46-
var suffix = targetFrameworkVersion != null && targetFrameworkVersion >= s_version6_0 ?
46+
var suffix = onlyUseArchSpecific || (targetFrameworkVersion != null && targetFrameworkVersion >= s_version6_0) ?
4747
$"_{currentArchitecture.ToString().ToUpperInvariant()}" :
4848
is64bit ? "" : "(x86)";
4949

@@ -53,6 +53,9 @@ static class EnvironmentVariableNames
5353
return null;
5454
}
5555

56+
internal static string? TryGetDotNetRootArchVariableName(string runtimeIdentifier, string defaultAppHostRuntimeIdentifier)
57+
=> TryGetDotNetRootVariableNameImpl(runtimeIdentifier, defaultAppHostRuntimeIdentifier, null, RuntimeInformation.ProcessArchitecture, Environment.Is64BitProcess, onlyUseArchSpecific: true);
58+
5659
internal static bool TryParseArchitecture(string runtimeIdentifier, out Architecture architecture)
5760
{
5861
// RID is [os].[version]-[architecture]-[additional qualifiers]

0 commit comments

Comments
 (0)