Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/Cli/dotnet/commands/dotnet-test/CliConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ internal static class CliConstants
public const string HelpOptionKey = "--help";
public const string ServerOptionKey = "--server";
public const string DotNetTestPipeOptionKey = "--dotnet-test-pipe";
public const string ProjectOptionKey = "--project";
public const string FrameworkOptionKey = "--framework";

public const string ServerOptionValue = "dotnettestcli";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@

namespace Microsoft.DotNet.Tools.Test;

internal sealed record ModuleMessage(string? DLLPath, string? ProjectPath, string? TargetFramework, string? RunSettingsFilePath) : IRequest;
internal sealed record ModuleMessage(string? DllOrExePath, string? ProjectPath, string? TargetFramework, string? RunSettingsFilePath, string IsTestingPlatformApplication) : IRequest;
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@ public object Deserialize(Stream stream)
string projectPath = ReadString(stream);
string targetFramework = ReadString(stream);
string runSettingsFilePath = ReadString(stream);
return new ModuleMessage(modulePath.Trim(), projectPath.Trim(), targetFramework.Trim(), runSettingsFilePath.Trim());
string isTestingPlatformApplication = ReadString(stream);
return new ModuleMessage(modulePath.Trim(), projectPath.Trim(), targetFramework.Trim(), runSettingsFilePath.Trim(), isTestingPlatformApplication.Trim());
}

public void Serialize(object objectToSerialize, Stream stream)
{
WriteString(stream, ((ModuleMessage)objectToSerialize).DLLPath);
WriteString(stream, ((ModuleMessage)objectToSerialize).DllOrExePath);
WriteString(stream, ((ModuleMessage)objectToSerialize).ProjectPath);
WriteString(stream, ((ModuleMessage)objectToSerialize).TargetFramework);
WriteString(stream, ((ModuleMessage)objectToSerialize).RunSettingsFilePath);
WriteString(stream, ((ModuleMessage)objectToSerialize).IsTestingPlatformApplication);
}
}
}
11 changes: 11 additions & 0 deletions src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -315,4 +315,15 @@ Examples:
<data name="CmdTestModulesRootDirectoryDescription" xml:space="preserve">
<value>The test modules have the specified root directory.</value>
</data>
<data name="CmdUnsupportedMessageRequestTypeException" xml:space="preserve">
<value>Message Request type '{0}' is unsupported.</value>
<comment>{0} - message request type</comment>
</data>
<data name="CmdInvalidTestMessageStateException" xml:space="preserve">
<value>Invalid test message state '{0}'</value>
<comment>{0} - test message state</comment>
</data>
<data name="CmdUnsupportedVSTestTestApplicationsDescription" xml:space="preserve">
<value>Test application(s) that support VSTest are not supported.</value>
</data>
</root>
40 changes: 35 additions & 5 deletions src/Cli/dotnet/commands/dotnet-test/MSBuildConnectionHandler.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Concurrent;
using System.CommandLine;
using System.IO.Pipes;
using Microsoft.DotNet.Cli.Utils;
Expand All @@ -15,6 +16,8 @@ internal sealed class MSBuildConnectionHandler : IDisposable

private readonly PipeNameDescription _pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N"));
private readonly List<NamedPipeServer> _namedPipeConnections = new();
private readonly ConcurrentBag<TestApplication> _testApplications = new();
private bool _areTestingPlatformApplications = true;

public MSBuildConnectionHandler(List<string> args, TestApplicationActionQueue actionQueue)
{
Expand Down Expand Up @@ -62,9 +65,16 @@ private Task<IResponse> OnRequest(IRequest request)
throw new NotSupportedException($"Request '{request.GetType()}' is unsupported.");
}

var testApp = new TestApplication(new Module(module.DLLPath, module.ProjectPath, module.TargetFramework, module.RunSettingsFilePath), _args);
// Write the test application to the channel
_actionQueue.Enqueue(testApp);
// 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)
{
Expand All @@ -79,14 +89,29 @@ private Task<IResponse> OnRequest(IRequest request)
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<string> msbuildCommandLineArgs =
[
parseResult.GetValue(TestingPlatformOptions.ProjectOption) ?? string.Empty,
$"-t:_GetTestsProject",
"-t:Restore;_GetTestsProject",
$"-p:GetTestsProjectPipeName={_pipeNameDescription.Name}",
"-verbosity:q"
"-verbosity:q",
"-nologo",
];

AddBinLogParameterIfExists(msbuildCommandLineArgs, _args);
Expand Down Expand Up @@ -137,6 +162,11 @@ public void Dispose()
{
namedPipeServer.Dispose();
}

foreach (var testApplication in _testApplications)
{
testApplication.Dispose();
}
}
}
}
2 changes: 1 addition & 1 deletion src/Cli/dotnet/commands/dotnet-test/Models.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace Microsoft.DotNet.Cli
{
internal sealed record Module(string? DLLOrExe, string? ProjectPath, string? TargetFramework, string? RunSettingsFilePath);
internal sealed record Module(string? DllOrExePath, string? ProjectPath, string? TargetFramework, string? RunSettingsFilePath);

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

Expand Down
45 changes: 19 additions & 26 deletions src/Cli/dotnet/commands/dotnet-test/TestApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ public async Task<int> RunAsync(bool isFilterMode, bool enableHelp, BuiltInOptio
return 1;
}

bool isDll = _module.DLLOrExe.EndsWith(".dll");
bool isDll = _module.DllOrExePath.EndsWith(".dll");

ProcessStartInfo processStartInfo = new()
{
FileName = isFilterMode ? isDll ? Environment.ProcessPath : _module.DLLOrExe : Environment.ProcessPath,
Arguments = enableHelp ? BuildHelpArgs(isDll) : isFilterMode ? BuildArgs(isDll) : BuildArgsWithDotnetRun(builtInOptions),
FileName = isFilterMode ? isDll ? Environment.ProcessPath : _module.DllOrExePath : Environment.ProcessPath,
Arguments = isFilterMode ? BuildArgs(isDll) : BuildArgsWithDotnetRun(enableHelp, builtInOptions),
RedirectStandardOutput = true,
RedirectStandardError = true
};
Expand All @@ -74,8 +74,10 @@ public async Task<int> RunAsync(bool isFilterMode, bool enableHelp, BuiltInOptio
var result = await StartProcess(processStartInfo);

_namedPipeConnectionLoop.Wait();

return result;
}

private async Task WaitConnectionAsync(CancellationToken token)
{
try
Expand Down Expand Up @@ -145,7 +147,7 @@ private Task<IResponse> OnRequest(IRequest request)

default:
// If it doesn't match any of the above, throw an exception
throw new NotSupportedException($"Request '{request.GetType()}' is unsupported.");
throw new NotSupportedException(string.Format(LocalizableStrings.CmdUnsupportedMessageRequestTypeException, request.GetType()));
}
}
catch (Exception ex)
Expand Down Expand Up @@ -224,19 +226,19 @@ private void StoreOutputAndErrorData(Process process)

private bool ModulePathExists()
{
if (!File.Exists(_module.DLLOrExe))
if (!File.Exists(_module.DllOrExePath))
{
ErrorReceived.Invoke(this, new ErrorEventArgs { ErrorMessage = $"Test module '{_module.DLLOrExe}' not found. Build the test application before or run 'dotnet test'." });
ErrorReceived.Invoke(this, new ErrorEventArgs { ErrorMessage = $"Test module '{_module.DllOrExePath}' not found. Build the test application before or run 'dotnet test'." });
return false;
}
return true;
}

private string BuildArgsWithDotnetRun(BuiltInOptions builtInOptions)
private string BuildArgsWithDotnetRun(bool hasHelp, BuiltInOptions builtInOptions)
{
StringBuilder builder = new();

builder.Append($"{CliConstants.DotnetRunCommand} {CliConstants.ProjectOptionKey} \"{_module.ProjectPath}\"");
builder.Append($"{CliConstants.DotnetRunCommand} {TestingPlatformOptions.ProjectOption.Name} \"{_module.ProjectPath}\"");

if (builtInOptions.HasNoRestore)
{
Expand Down Expand Up @@ -265,6 +267,11 @@ private string BuildArgsWithDotnetRun(BuiltInOptions builtInOptions)

builder.Append($" {CliConstants.ParametersSeparator} ");

if (hasHelp)
{
builder.Append($" {CliConstants.HelpOptionKey} ");
}

builder.Append(_args.Count != 0
? _args.Aggregate((a, b) => $"{a} {b}")
: string.Empty);
Expand All @@ -280,7 +287,7 @@ private string BuildArgs(bool isDll)

if (isDll)
{
builder.Append($"exec {_module.DLLOrExe} ");
builder.Append($"exec {_module.DllOrExePath} ");
}

builder.Append(_args.Count != 0
Expand All @@ -292,26 +299,12 @@ private string BuildArgs(bool isDll)
return builder.ToString();
}

private string BuildHelpArgs(bool isDll)
{
StringBuilder builder = new();

if (isDll)
{
builder.Append($"exec {_module.DLLOrExe} ");
}

builder.Append($" {CliConstants.HelpOptionKey} {CliConstants.ServerOptionKey} {CliConstants.ServerOptionValue} {CliConstants.DotNetTestPipeOptionKey} {_pipeNameDescription.Name}");

return builder.ToString();
}

public void OnHandshakeMessage(HandshakeMessage handshakeMessage)
{
if (handshakeMessage.Properties.TryGetValue(HandshakeMessagePropertyNames.ExecutionId, out string executionId))
{
AddExecutionId(executionId);
ExecutionIdReceived?.Invoke(this, new ExecutionEventArgs { ModulePath = _module.DLLOrExe, ExecutionId = executionId });
ExecutionIdReceived?.Invoke(this, new ExecutionEventArgs { ModulePath = _module.DllOrExePath, ExecutionId = executionId });
}
HandshakeReceived?.Invoke(this, new HandshakeArgs { Handshake = new Handshake(handshakeMessage.Properties) });
}
Expand Down Expand Up @@ -354,9 +347,9 @@ public override string ToString()
{
StringBuilder builder = new();

if (!string.IsNullOrEmpty(_module.DLLOrExe))
if (!string.IsNullOrEmpty(_module.DllOrExePath))
{
builder.Append($"DLL: {_module.DLLOrExe}");
builder.Append($"DLL: {_module.DllOrExePath}");
}

if (!string.IsNullOrEmpty(_module.ProjectPath))
Expand Down
Loading