Skip to content

Commit 4f73a85

Browse files
authored
Merge branch 'main' into darc-main-78f666ec-96ed-4ba3-ab20-9ab127a356d0
2 parents 2d8949d + a426f3e commit 4f73a85

13 files changed

+81
-113
lines changed

src/Cli/dotnet/Commands/Run/RunCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ static void ValidatePreconditions(ProjectInstance project)
364364

365365
static RunProperties ReadRunPropertiesFromProject(ProjectInstance project, string[] applicationArgs)
366366
{
367-
var runProperties = RunProperties.FromProjectAndApplicationArguments(project, applicationArgs, fallbackToTargetPath: false);
367+
var runProperties = RunProperties.FromProjectAndApplicationArguments(project, applicationArgs);
368368
if (string.IsNullOrEmpty(runProperties.RunCommand))
369369
{
370370
ThrowUnableToRunError(project);

src/Cli/dotnet/Commands/Run/RunProperties.cs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,9 @@ namespace Microsoft.DotNet.Cli.Commands.Run;
88

99
internal record RunProperties(string RunCommand, string? RunArguments, string? RunWorkingDirectory)
1010
{
11-
internal static RunProperties FromProjectAndApplicationArguments(ProjectInstance project, string[] applicationArgs, bool fallbackToTargetPath)
11+
internal static RunProperties FromProjectAndApplicationArguments(ProjectInstance project, string[] applicationArgs)
1212
{
1313
string runProgram = project.GetPropertyValue("RunCommand");
14-
if (fallbackToTargetPath &&
15-
(string.IsNullOrEmpty(runProgram) || !File.Exists(runProgram)))
16-
{
17-
// If we can't find the executable that runCommand is pointing to, we simply use TargetPath instead.
18-
// In this case, we discard everything related to "Run" (i.e, RunWorkingDirectory and RunArguments) and use only TargetPath
19-
runProgram = project.GetPropertyValue("TargetPath");
20-
return new(runProgram, null, null);
21-
}
22-
2314
string runArguments = project.GetPropertyValue("RunArguments");
2415
string runWorkingDirectory = project.GetPropertyValue("RunWorkingDirectory");
2516

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

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/Cli/dotnet/Commands/Test/IPC/NamedPipeServer.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@ public NamedPipeServer(
5353
}
5454

5555
public NamedPipeServer(
56-
PipeNameDescription pipeNameDescription,
57-
Func<IRequest, Task<IResponse>> callback,
58-
int maxNumberOfServerInstances,
59-
CancellationToken cancellationToken,
60-
bool skipUnknownMessages)
56+
PipeNameDescription pipeNameDescription,
57+
Func<IRequest, Task<IResponse>> callback,
58+
int maxNumberOfServerInstances,
59+
CancellationToken cancellationToken,
60+
bool skipUnknownMessages)
6161
{
6262
_namedPipeServerStream = new((PipeName = pipeNameDescription).Name, PipeDirection.InOut, maxNumberOfServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
6363
_callback = callback;

src/Cli/dotnet/Commands/Test/IPC/Serializers/CommandLineOptionMessagesSerializer.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
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-
#if NETCOREAPP
5-
#endif
6-
74
using System.Diagnostics;
85
using Microsoft.DotNet.Cli.Commands.Test.IPC.Models;
96

src/Cli/dotnet/Commands/Test/IPC/Serializers/ModuleMessageSerializer.cs

Lines changed: 0 additions & 30 deletions
This file was deleted.

src/Cli/dotnet/Commands/Test/IPC/Serializers/RegisterSerializers.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ public static void RegisterAllSerializers(this NamedPipeBase namedPipeBase)
2828
{
2929
namedPipeBase.RegisterSerializer(new VoidResponseSerializer(), typeof(VoidResponse));
3030
namedPipeBase.RegisterSerializer(new CommandLineOptionMessagesSerializer(), typeof(CommandLineOptionMessages));
31-
namedPipeBase.RegisterSerializer(new ModuleMessageSerializer(), typeof(ModuleMessage));
3231
namedPipeBase.RegisterSerializer(new DiscoveredTestMessagesSerializer(), typeof(DiscoveredTestMessages));
3332
namedPipeBase.RegisterSerializer(new TestResultMessagesSerializer(), typeof(TestResultMessages));
3433
namedPipeBase.RegisterSerializer(new FileArtifactMessagesSerializer(), typeof(FileArtifactMessages));

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ public bool MoveNext()
111111
}
112112
}
113113

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

116117
internal sealed record Handshake(Dictionary<byte, string>? Properties);
117118

@@ -128,5 +129,3 @@ internal sealed record FlatException(string? ErrorMessage, string? ErrorType, st
128129
internal sealed record FileArtifact(string? FullPath, string? DisplayName, string? Description, string? TestUid, string? TestDisplayName, string? SessionUid);
129130

130131
internal sealed record TestSession(byte? SessionType, string? SessionUid, string? ExecutionId);
131-
132-
internal record MSBuildBuildAndRestoreSettings(string[] Commands, string Configuration, string RuntimeIdentifier, bool AllowBinLog, string BinLogFileName);

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

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,12 +230,27 @@ public static IEnumerable<ParallelizableTestModuleGroupWithSequentialInnerModule
230230

231231
string targetFramework = project.GetPropertyValue(ProjectProperties.TargetFramework);
232232
RunProperties runProperties = GetRunProperties(project, loggers);
233+
234+
// dotnet run throws the same if RunCommand is null or empty.
235+
// In dotnet test, we are additionally checking that RunCommand is not dll.
236+
// In any "default" scenario, RunCommand is never dll.
237+
// If we found it to be dll, that is user explicitly setting RunCommand incorrectly.
238+
if (string.IsNullOrEmpty(runProperties.RunCommand) || runProperties.RunCommand.HasExtension(CliConstants.DLLExtension))
239+
{
240+
throw new GracefulException(
241+
string.Format(
242+
CliCommandStrings.RunCommandExceptionUnableToRun,
243+
"dotnet test",
244+
"OutputType",
245+
project.GetPropertyValue("OutputType")));
246+
}
247+
233248
string projectFullPath = project.GetPropertyValue(ProjectProperties.ProjectFullPath);
234249

235250
// TODO: Support --launch-profile and pass it here.
236251
var launchSettings = TryGetLaunchProfileSettings(Path.GetDirectoryName(projectFullPath)!, project.GetPropertyValue(ProjectProperties.AppDesignerFolder), noLaunchProfile, profileName: null);
237252

238-
return new TestModule(runProperties, PathUtility.FixFilePath(projectFullPath), targetFramework, isTestingPlatformApplication, isTestProject, launchSettings);
253+
return new TestModule(runProperties, PathUtility.FixFilePath(projectFullPath), targetFramework, isTestingPlatformApplication, isTestProject, launchSettings, project.GetPropertyValue(ProjectProperties.TargetPath));
239254

240255
static RunProperties GetRunProperties(ProjectInstance project, ICollection<ILogger>? loggers)
241256
{
@@ -247,12 +262,11 @@ static RunProperties GetRunProperties(ProjectInstance project, ICollection<ILogg
247262
{
248263
if (!project.Build(s_computeRunArgumentsTarget, loggers: null))
249264
{
250-
Logger.LogTrace(() => $"The target {s_computeRunArgumentsTarget} failed to build. Falling back to TargetPath.");
251-
return new RunProperties(project.GetPropertyValue(ProjectProperties.TargetPath), null, null);
265+
throw new GracefulException(CliCommandStrings.RunCommandEvaluationExceptionBuildFailed, s_computeRunArgumentsTarget);
252266
}
253267
}
254268

255-
return RunProperties.FromProjectAndApplicationArguments(project, [], fallbackToTargetPath: true);
269+
return RunProperties.FromProjectAndApplicationArguments(project, []);
256270
}
257271
}
258272

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

Lines changed: 21 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
using Microsoft.DotNet.Cli.Commands.Test.IPC;
99
using Microsoft.DotNet.Cli.Commands.Test.IPC.Models;
1010
using Microsoft.DotNet.Cli.Commands.Test.IPC.Serializers;
11-
using Microsoft.DotNet.Cli.Utils;
1211

1312
namespace Microsoft.DotNet.Cli.Commands.Test;
1413

@@ -54,12 +53,13 @@ public async Task<int> RunAsync(TestOptions testOptions)
5453

5554
private ProcessStartInfo CreateProcessStartInfo(TestOptions testOptions)
5655
{
57-
bool isDll = Module.RunProperties.RunCommand.HasExtension(CliConstants.DLLExtension);
58-
5956
var processStartInfo = new ProcessStartInfo
6057
{
61-
FileName = GetFileName(testOptions, isDll),
62-
Arguments = GetArguments(testOptions, isDll),
58+
// We should get correct RunProperties right away.
59+
// For the case of dotnet test --test-modules path/to/dll, the TestModulesFilterHandler is responsible
60+
// for providing the dotnet muxer as RunCommand, and `exec "path/to/dll"` as RunArguments.
61+
FileName = Module.RunProperties.RunCommand,
62+
Arguments = GetArguments(testOptions),
6363
RedirectStandardOutput = true,
6464
RedirectStandardError = true
6565
};
@@ -87,23 +87,27 @@ private ProcessStartInfo CreateProcessStartInfo(TestOptions testOptions)
8787
return processStartInfo;
8888
}
8989

90-
private string GetFileName(TestOptions testOptions, bool isDll)
91-
=> isDll ? Environment.ProcessPath : Module.RunProperties.RunCommand;
92-
93-
private string GetArguments(TestOptions testOptions, bool isDll)
90+
private string GetArguments(TestOptions testOptions)
9491
{
95-
if (testOptions.HasFilterMode || !isDll || !IsArchitectureSpecified(testOptions))
92+
// Keep RunArguments first.
93+
// In the case of UseAppHost=false, RunArguments is set to `exec $(TargetPath)`:
94+
// https://github.com/dotnet/sdk/blob/333388c31d811701e3b6be74b5434359151424dc/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets#L1411
95+
// So, we keep that first always.
96+
StringBuilder builder = new(Module.RunProperties.RunArguments);
97+
98+
if (testOptions.IsHelp)
9699
{
97-
return BuildArgs(testOptions, isDll);
100+
builder.Append($" {TestingPlatformOptions.HelpOption.Name} ");
98101
}
99102

100-
// If we reach here, that means we have a test project that doesn't produce an executable.
101-
throw new InvalidOperationException($"A Microsoft.Testing.Platform test project should produce an executable. The file '{Module.RunProperties.RunCommand}' is dll.");
102-
}
103+
var args = _buildOptions.UnmatchedTokens;
104+
builder.Append(args.Count != 0
105+
? args.Aggregate((a, b) => $"{a} {b}")
106+
: string.Empty);
103107

104-
private static bool IsArchitectureSpecified(TestOptions testOptions)
105-
{
106-
return !string.IsNullOrEmpty(testOptions.Architecture);
108+
builder.Append($" {CliConstants.ServerOptionKey} {CliConstants.ServerOptionValue} {CliConstants.DotNetTestPipeOptionKey} {_pipeNameDescription.Name}");
109+
110+
return builder.ToString();
107111
}
108112

109113
private void WaitOnTestApplicationPipeConnectionLoop()
@@ -277,35 +281,6 @@ private bool ModulePathExists()
277281
return true;
278282
}
279283

280-
private string BuildArgs(TestOptions testOptions, bool isDll)
281-
{
282-
StringBuilder builder = new();
283-
284-
if (isDll)
285-
{
286-
builder.Append($"exec {Module.RunProperties.RunCommand} ");
287-
}
288-
289-
AppendCommonArgs(builder, testOptions);
290-
291-
return builder.ToString();
292-
}
293-
294-
private void AppendCommonArgs(StringBuilder builder, TestOptions testOptions)
295-
{
296-
if (testOptions.IsHelp)
297-
{
298-
builder.Append($" {TestingPlatformOptions.HelpOption.Name} ");
299-
}
300-
301-
var args = _buildOptions.UnmatchedTokens;
302-
builder.Append(args.Count != 0
303-
? args.Aggregate((a, b) => $"{a} {b}")
304-
: string.Empty);
305-
306-
builder.Append($" {CliConstants.ServerOptionKey} {CliConstants.ServerOptionValue} {CliConstants.DotNetTestPipeOptionKey} {_pipeNameDescription.Name} {Module.RunProperties.RunArguments}");
307-
}
308-
309284
public void OnHandshakeMessage(HandshakeMessage handshakeMessage)
310285
{
311286
HandshakeReceived?.Invoke(this, new HandshakeArgs { Handshake = new Handshake(handshakeMessage.Properties) });

0 commit comments

Comments
 (0)