diff --git a/Directory.Packages.props b/Directory.Packages.props
index 37f12c935a34..56cc3862dd6e 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -63,7 +63,7 @@
-
+
diff --git a/eng/Versions.props b/eng/Versions.props
index f3b1c74614e4..d550850f2213 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -28,7 +28,6 @@
true
6.0.1
true
- 1.6.0-preview.25056.11
30
@@ -161,6 +160,10 @@
17.13.0-preview-25055-01
17.13.0-preview-25055-01
+
+
+ 1.5.0-preview.24604.7
+
10.0.0-preview.24629.1
diff --git a/src/Cli/dotnet/commands/dotnet-test/BuiltInOptions.cs b/src/Cli/dotnet/commands/dotnet-test/BuiltInOptions.cs
index f3bc4c1419c5..0462c6c7486d 100644
--- a/src/Cli/dotnet/commands/dotnet-test/BuiltInOptions.cs
+++ b/src/Cli/dotnet/commands/dotnet-test/BuiltInOptions.cs
@@ -3,5 +3,5 @@
namespace Microsoft.DotNet.Cli
{
- internal record BuiltInOptions(bool HasNoRestore, bool HasNoBuild, string Configuration, string Architecture);
+ internal record BuiltInOptions(bool HasNoRestore, bool HasNoBuild, bool HasListTests, string Configuration, string Architecture);
}
diff --git a/src/Cli/dotnet/commands/dotnet-test/IPC/Models/TestResultMessages.cs b/src/Cli/dotnet/commands/dotnet-test/IPC/Models/TestResultMessages.cs
index 5882717301ff..af9ee626604c 100644
--- a/src/Cli/dotnet/commands/dotnet-test/IPC/Models/TestResultMessages.cs
+++ b/src/Cli/dotnet/commands/dotnet-test/IPC/Models/TestResultMessages.cs
@@ -5,7 +5,9 @@ namespace Microsoft.DotNet.Tools.Test
{
internal sealed record SuccessfulTestResultMessage(string? Uid, string? DisplayName, byte? State, long? Duration, string? Reason, string? StandardOutput, string? ErrorOutput, string? SessionUid);
- internal sealed record FailedTestResultMessage(string? Uid, string? DisplayName, byte? State, long? Duration, string? Reason, string? ErrorMessage, string? ErrorStackTrace, string? StandardOutput, string? ErrorOutput, string? SessionUid);
+ internal sealed record FailedTestResultMessage(string? Uid, string? DisplayName, byte? State, long? Duration, string? Reason, ExceptionMessage[]? Exceptions, string? StandardOutput, string? ErrorOutput, string? SessionUid);
+
+ internal sealed record ExceptionMessage(string? ErrorMessage, string? ErrorType, string? StackTrace);
internal sealed record TestResultMessages(string? ExecutionId, SuccessfulTestResultMessage[] SuccessfulTestMessages, FailedTestResultMessage[] FailedTestMessages) : IRequest;
}
diff --git a/src/Cli/dotnet/commands/dotnet-test/IPC/ObjectFieldIds.cs b/src/Cli/dotnet/commands/dotnet-test/IPC/ObjectFieldIds.cs
index e7f720c96fbb..01d38f3d9ec2 100644
--- a/src/Cli/dotnet/commands/dotnet-test/IPC/ObjectFieldIds.cs
+++ b/src/Cli/dotnet/commands/dotnet-test/IPC/ObjectFieldIds.cs
@@ -85,13 +85,19 @@ internal static class FailedTestResultMessageFieldsId
public const ushort State = 3;
public const ushort Duration = 4;
public const ushort Reason = 5;
- public const ushort ErrorMessage = 6;
- public const ushort ErrorStackTrace = 7;
+ public const ushort ExceptionMessageList = 6;
public const ushort StandardOutput = 8;
public const ushort ErrorOutput = 9;
public const ushort SessionUid = 10;
}
+ internal static class ExceptionMessageFieldsId
+ {
+ public const ushort ErrorMessage = 1;
+ public const ushort ErrorType = 2;
+ public const ushort StackTrace = 3;
+ }
+
internal static class FileArtifactMessagesFieldsId
{
public const int MessagesSerializerId = 7;
diff --git a/src/Cli/dotnet/commands/dotnet-test/IPC/Serializers/TestResultMessagesSerializer.cs b/src/Cli/dotnet/commands/dotnet-test/IPC/Serializers/TestResultMessagesSerializer.cs
index 9726bec16ff7..329c54e23b1c 100644
--- a/src/Cli/dotnet/commands/dotnet-test/IPC/Serializers/TestResultMessagesSerializer.cs
+++ b/src/Cli/dotnet/commands/dotnet-test/IPC/Serializers/TestResultMessagesSerializer.cs
@@ -217,8 +217,8 @@ private static List ReadFailedTestMessagesPayload(Strea
int length = ReadInt(stream);
for (int i = 0; i < length; i++)
{
- string? uid = null, displayName = null, reason = null, sessionUid = null,
- errorMessage = null, errorStackTrace = null, standardOutput = null, errorOutput = null;
+ string? uid = null, displayName = null, reason = null, sessionUid = null, standardOutput = null, errorOutput = null;
+ List exceptionMessages = [];
byte? state = null;
long? duration = null;
@@ -251,13 +251,44 @@ private static List ReadFailedTestMessagesPayload(Strea
reason = ReadStringValue(stream, fieldSize);
break;
- case FailedTestResultMessageFieldsId.ErrorMessage:
- errorMessage = ReadStringValue(stream, fieldSize);
- break;
+ case FailedTestResultMessageFieldsId.ExceptionMessageList:
+ {
+ int length2 = ReadInt(stream);
+ for (int k = 0; k < length2; k++)
+ {
- case FailedTestResultMessageFieldsId.ErrorStackTrace:
- errorStackTrace = ReadStringValue(stream, fieldSize);
- break;
+ int fieldCount2 = ReadShort(stream);
+
+ string? errorMessage = null;
+ string? errorType = null;
+ string? stackTrace = null;
+
+ for (int l = 0; l < fieldCount2; l++)
+ {
+ int fieldId2 = ReadShort(stream);
+ int fieldSize2 = ReadInt(stream);
+
+ switch (fieldId2)
+ {
+ case ExceptionMessageFieldsId.ErrorMessage:
+ errorMessage = ReadStringValue(stream, fieldSize2);
+ break;
+
+ case ExceptionMessageFieldsId.ErrorType:
+ errorType = ReadStringValue(stream, fieldSize2);
+ break;
+
+ case ExceptionMessageFieldsId.StackTrace:
+ stackTrace = ReadStringValue(stream, fieldSize2);
+ break;
+ }
+ }
+
+ exceptionMessages.Add(new ExceptionMessage(errorMessage, errorType, stackTrace));
+ }
+
+ break;
+ }
case FailedTestResultMessageFieldsId.StandardOutput:
standardOutput = ReadStringValue(stream, fieldSize);
@@ -277,7 +308,7 @@ private static List ReadFailedTestMessagesPayload(Strea
}
}
- failedTestResultMessages.Add(new FailedTestResultMessage(uid, displayName, state, duration, reason, errorMessage, errorStackTrace, standardOutput, errorOutput, sessionUid));
+ failedTestResultMessages.Add(new FailedTestResultMessage(uid, displayName, state, duration, reason, exceptionMessages.ToArray(), standardOutput, errorOutput, sessionUid));
}
return failedTestResultMessages;
@@ -354,8 +385,7 @@ private static void WriteFailedTestMessagesPayload(Stream stream, FailedTestResu
WriteField(stream, FailedTestResultMessageFieldsId.State, failedTestResultMessage.State);
WriteField(stream, FailedTestResultMessageFieldsId.Duration, failedTestResultMessage.Duration);
WriteField(stream, FailedTestResultMessageFieldsId.Reason, failedTestResultMessage.Reason);
- WriteField(stream, FailedTestResultMessageFieldsId.ErrorMessage, failedTestResultMessage.ErrorMessage);
- WriteField(stream, FailedTestResultMessageFieldsId.ErrorStackTrace, failedTestResultMessage.ErrorStackTrace);
+ WriteExceptionMessagesPayload(stream, failedTestResultMessage.Exceptions);
WriteField(stream, FailedTestResultMessageFieldsId.StandardOutput, failedTestResultMessage.StandardOutput);
WriteField(stream, FailedTestResultMessageFieldsId.ErrorOutput, failedTestResultMessage.ErrorOutput);
WriteField(stream, FailedTestResultMessageFieldsId.SessionUid, failedTestResultMessage.SessionUid);
@@ -366,6 +396,35 @@ private static void WriteFailedTestMessagesPayload(Stream stream, FailedTestResu
WriteAtPosition(stream, (int)(stream.Position - before), before - sizeof(int));
}
+ private static void WriteExceptionMessagesPayload(Stream stream, ExceptionMessage[]? exceptionMessages)
+ {
+ if (exceptionMessages is null || exceptionMessages.Length == 0)
+ {
+ return;
+ }
+
+ WriteShort(stream, FailedTestResultMessageFieldsId.ExceptionMessageList);
+
+ // We will reserve an int (4 bytes)
+ // so that we fill the size later, once we write the payload
+ WriteInt(stream, 0);
+
+ long before = stream.Position;
+ WriteInt(stream, exceptionMessages.Length);
+ foreach (ExceptionMessage exceptionMessage in exceptionMessages)
+ {
+ WriteShort(stream, GetFieldCount(exceptionMessage));
+
+ WriteField(stream, ExceptionMessageFieldsId.ErrorMessage, exceptionMessage.ErrorMessage);
+ WriteField(stream, ExceptionMessageFieldsId.ErrorType, exceptionMessage.ErrorType);
+ WriteField(stream, ExceptionMessageFieldsId.StackTrace, exceptionMessage.StackTrace);
+ }
+
+ // NOTE: We are able to seek only if we are using a MemoryStream
+ // thus, the seek operation is fast as we are only changing the value of a property
+ WriteAtPosition(stream, (int)(stream.Position - before), before - sizeof(int));
+ }
+
private static ushort GetFieldCount(TestResultMessages testResultMessages) =>
(ushort)((testResultMessages.ExecutionId is null ? 0 : 1) +
(IsNullOrEmpty(testResultMessages.SuccessfulTestMessages) ? 0 : 1) +
@@ -387,10 +446,14 @@ private static ushort GetFieldCount(FailedTestResultMessage failedTestResultMess
(failedTestResultMessage.State is null ? 0 : 1) +
(failedTestResultMessage.Duration is null ? 0 : 1) +
(failedTestResultMessage.Reason is null ? 0 : 1) +
- (failedTestResultMessage.ErrorMessage is null ? 0 : 1) +
- (failedTestResultMessage.ErrorStackTrace is null ? 0 : 1) +
+ (IsNullOrEmpty(failedTestResultMessage.Exceptions) ? 0 : 1) +
(failedTestResultMessage.StandardOutput is null ? 0 : 1) +
(failedTestResultMessage.ErrorOutput is null ? 0 : 1) +
(failedTestResultMessage.SessionUid is null ? 0 : 1));
+
+ private static ushort GetFieldCount(ExceptionMessage exceptionMessage) =>
+ (ushort)((exceptionMessage.ErrorMessage is null ? 0 : 1) +
+ (exceptionMessage.ErrorType is null ? 0 : 1) +
+ (exceptionMessage.StackTrace is null ? 0 : 1));
}
}
diff --git a/src/Cli/dotnet/commands/dotnet-test/Models.cs b/src/Cli/dotnet/commands/dotnet-test/Models.cs
index 8484f819f38d..d022955e8c1c 100644
--- a/src/Cli/dotnet/commands/dotnet-test/Models.cs
+++ b/src/Cli/dotnet/commands/dotnet-test/Models.cs
@@ -13,7 +13,9 @@ internal sealed record DiscoveredTest(string? Uid, string? DisplayName);
internal sealed record SuccessfulTestResult(string? Uid, string? DisplayName, byte? State, long? Duration, string? Reason, string? StandardOutput, string? ErrorOutput, string? SessionUid);
- internal sealed record FailedTestResult(string? Uid, string? DisplayName, byte? State, long? Duration, string? Reason, string? ErrorMessage, string? ErrorStackTrace, string? StandardOutput, string? ErrorOutput, string? SessionUid);
+ internal sealed record FailedTestResult(string? Uid, string? DisplayName, byte? State, long? Duration, string? Reason, FlatException[]? Exceptions, string? StandardOutput, string? ErrorOutput, string? SessionUid);
+
+ internal sealed record FlatException(string? ErrorMessage, string? ErrorType, string? StackTrace);
internal sealed record FileArtifact(string? FullPath, string? DisplayName, string? Description, string? TestUid, string? TestDisplayName, string? SessionUid);
diff --git a/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs b/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs
index c727ffd4fe3b..27d564a4bebd 100644
--- a/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs
+++ b/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs
@@ -260,6 +260,11 @@ private string BuildArgsWithDotnetRun(bool hasHelp, BuiltInOptions builtInOption
builder.Append($" {TestingPlatformOptions.NoBuildOption.Name}");
}
+ if (builtInOptions.HasListTests)
+ {
+ builder.Append($" {TestingPlatformOptions.ListTestsOption.Name}");
+ }
+
if (!string.IsNullOrEmpty(builtInOptions.Architecture))
{
builder.Append($" {TestingPlatformOptions.ArchitectureOption.Name} {builtInOptions.Architecture}");
@@ -339,7 +344,7 @@ internal void OnTestResultMessages(TestResultMessages testResultMessage)
{
ExecutionId = testResultMessage.ExecutionId,
SuccessfulTestResults = testResultMessage.SuccessfulTestMessages.Select(message => new SuccessfulTestResult(message.Uid, message.DisplayName, message.State, message.Duration, message.Reason, message.StandardOutput, message.ErrorOutput, message.SessionUid)).ToArray(),
- FailedTestResults = testResultMessage.FailedTestMessages.Select(message => new FailedTestResult(message.Uid, message.DisplayName, message.State, message.Duration, message.Reason, message.ErrorMessage, message.ErrorStackTrace, message.StandardOutput, message.ErrorOutput, message.SessionUid)).ToArray()
+ FailedTestResults = testResultMessage.FailedTestMessages.Select(message => new FailedTestResult(message.Uid, message.DisplayName, message.State, message.Duration, message.Reason, message.Exceptions.Select(e => new FlatException(e.ErrorMessage, e.ErrorType, e.StackTrace)).ToArray(), message.StandardOutput, message.ErrorOutput, message.SessionUid)).ToArray()
});
}
diff --git a/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs b/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs
index 5196981af9ae..c308cd2185fe 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.ListTestsOption);
return command;
}
diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs
index b947031fdca2..026900ec1e12 100644
--- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs
+++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs
@@ -1,10 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
using System.Collections.Concurrent;
using System.CommandLine;
using Microsoft.DotNet.Tools.Test;
using Microsoft.TemplateEngine.Cli.Commands;
+using Microsoft.Testing.Platform.Helpers;
+using Microsoft.Testing.Platform.OutputDevice;
+using Microsoft.Testing.Platform.OutputDevice.Terminal;
namespace Microsoft.DotNet.Cli
{
@@ -15,9 +19,13 @@ internal partial class TestingPlatformCommand : CliCommand, ICustomHelp
private MSBuildConnectionHandler _msBuildConnectionHandler;
private TestModulesFilterHandler _testModulesFilterHandler;
+ private TerminalTestReporter _output;
private TestApplicationActionQueue _actionQueue;
private Task _namedPipeConnectionLoop;
private List _args;
+ private ConcurrentDictionary _executions = new();
+ private byte _cancelled;
+ private bool _isDiscovery;
public TestingPlatformCommand(string name, string description = null) : base(name, description)
{
@@ -29,6 +37,12 @@ public int Run(ParseResult parseResult)
bool hasFailed = false;
try
{
+ Console.CancelKeyPress += (s, e) =>
+ {
+ _output?.StartCancelling();
+ CompleteRun();
+ };
+
// 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))
@@ -41,12 +55,30 @@ public int Run(ParseResult parseResult)
VSTestTrace.SafeWriteTrace(() => $"The --arch option is not supported yet.");
}
+ if (parseResult.HasOption(TestingPlatformOptions.ListTestsOption))
+ {
+ _isDiscovery = true;
+ }
+
BuiltInOptions builtInOptions = new(
parseResult.HasOption(TestingPlatformOptions.NoRestoreOption),
parseResult.HasOption(TestingPlatformOptions.NoBuildOption),
+ parseResult.HasOption(TestingPlatformOptions.ListTestsOption),
parseResult.GetValue(TestingPlatformOptions.ConfigurationOption),
parseResult.GetValue(TestingPlatformOptions.ArchitectureOption));
+ var console = new SystemConsole();
+ var output = new TerminalTestReporter(console, new TerminalTestReporterOptions()
+ {
+ ShowPassedTests = Environment.GetEnvironmentVariable("SHOW_PASSED") == "1" ? () => true : () => false,
+ ShowProgress = () => Environment.GetEnvironmentVariable("NO_PROGRESS") != "1",
+ UseAnsi = Environment.GetEnvironmentVariable("NO_ANSI") != "1",
+ ShowAssembly = true,
+ ShowAssemblyStartAndComplete = true,
+ });
+ _output = output;
+ _output.TestExecutionStarted(DateTimeOffset.Now, degreeOfParallelism, _isDiscovery);
+
if (ContainsHelpOption(parseResult.GetArguments()))
{
_actionQueue = new(degreeOfParallelism, async (TestApplication testApp) =>
@@ -57,7 +89,9 @@ public int Run(ParseResult parseResult)
testApp.Run += OnTestApplicationRun;
testApp.ExecutionIdReceived += OnExecutionIdReceived;
- return await testApp.RunAsync(filterModeEnabled, enableHelp: true, builtInOptions);
+ var result = await testApp.RunAsync(filterModeEnabled, enableHelp: true, builtInOptions);
+ CompleteRun();
+ return result;
});
}
else
@@ -87,6 +121,7 @@ public int Run(ParseResult parseResult)
{
if (!_testModulesFilterHandler.RunWithTestModulesFilter(parseResult))
{
+ CompleteRun();
return ExitCodes.GenericFailure;
}
}
@@ -97,6 +132,7 @@ public int Run(ParseResult parseResult)
if (msbuildResult != 0)
{
VSTestTrace.SafeWriteTrace(() => $"MSBuild task _GetTestsProject didn't execute properly with exit code: {msbuildResult}.");
+ CompleteRun();
return ExitCodes.GenericFailure;
}
@@ -104,6 +140,7 @@ public int Run(ParseResult parseResult)
if (!_msBuildConnectionHandler.EnqueueTestApplications())
{
VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdUnsupportedVSTestTestApplicationsDescription);
+ CompleteRun();
return ExitCodes.GenericFailure;
}
}
@@ -120,9 +157,18 @@ public int Run(ParseResult parseResult)
CleanUp();
}
+ CompleteRun();
return hasFailed ? ExitCodes.GenericFailure : ExitCodes.Success;
}
+ private void CompleteRun()
+ {
+ if (Interlocked.CompareExchange(ref _cancelled, 1, 0) == 0)
+ {
+ _output?.TestExecutionCompleted(DateTimeOffset.Now);
+ }
+ }
+
private void WaitOnMSBuildHandlerPipeConnectionLoop()
{
_cancellationToken.Cancel();
@@ -140,6 +186,14 @@ private void CleanUp()
private void OnHandshakeReceived(object sender, HandshakeArgs args)
{
+ var testApplication = (TestApplication)sender;
+ var executionId = args.Handshake.Properties[HandshakeMessagePropertyNames.ExecutionId];
+ var arch = args.Handshake.Properties[HandshakeMessagePropertyNames.Architecture]?.ToLower();
+ var tfm = TargetFrameworkParser.GetShortTargetFramework(args.Handshake.Properties[HandshakeMessagePropertyNames.Framework]);
+ (string ModulePath, string TargetFramework, string Architecture, string ExecutionId) appInfo = new(testApplication.Module.DllOrExePath, tfm, arch, executionId);
+ _executions[testApplication] = appInfo;
+ _output.AssemblyRunStarted(appInfo.ModulePath, appInfo.TargetFramework, appInfo.Architecture, appInfo.ExecutionId);
+
if (!VSTestTrace.TraceEnabled)
{
return;
@@ -155,13 +209,22 @@ private void OnHandshakeReceived(object sender, HandshakeArgs args)
private void OnDiscoveredTestsReceived(object sender, DiscoveredTestEventArgs args)
{
+ var testApp = (TestApplication)sender;
+ var appInfo = _executions[testApp];
+
+ foreach (var test in args.DiscoveredTests)
+ {
+ _output.TestDiscovered(appInfo.ModulePath, appInfo.TargetFramework, appInfo.Architecture, appInfo.ExecutionId,
+ test.DisplayName,
+ test.Uid);
+ }
+
if (!VSTestTrace.TraceEnabled)
{
return;
}
var discoveredTestMessages = args.DiscoveredTests;
-
VSTestTrace.SafeWriteTrace(() => $"DiscoveredTests Execution Id: {args.ExecutionId}");
foreach (DiscoveredTest discoveredTestMessage in discoveredTestMessages)
{
@@ -171,6 +234,40 @@ private void OnDiscoveredTestsReceived(object sender, DiscoveredTestEventArgs ar
private void OnTestResultsReceived(object sender, TestResultEventArgs args)
{
+ foreach (var testResult in args.SuccessfulTestResults)
+ {
+ var testApp = (TestApplication)sender;
+ var appInfo = _executions[testApp];
+ _output.TestCompleted(appInfo.ModulePath, appInfo.TargetFramework, appInfo.Architecture, appInfo.ExecutionId,
+ testResult.Uid,
+ testResult.DisplayName,
+ ToOutcome(testResult.State),
+ TimeSpan.FromTicks(testResult.Duration ?? 0),
+ exceptions: null,
+ expected: null,
+ actual: null,
+ standardOutput: null,
+ errorOutput: null);
+ }
+
+ foreach (var testResult in args.FailedTestResults)
+ {
+ var testApp = (TestApplication)sender;
+ // TODO: expected
+ // TODO: actual
+ var appInfo = _executions[testApp];
+ _output.TestCompleted(appInfo.ModulePath, appInfo.TargetFramework, appInfo.Architecture, appInfo.ExecutionId,
+ testResult.Uid,
+ testResult.DisplayName,
+ ToOutcome(testResult.State),
+ TimeSpan.FromTicks(testResult.Duration ?? 0),
+ exceptions: testResult.Exceptions.Select(fe => new Microsoft.Testing.Platform.OutputDevice.Terminal.FlatException(fe.ErrorMessage, fe.ErrorType, fe.StackTrace)).ToArray(),
+ expected: null,
+ actual: null,
+ standardOutput: null,
+ errorOutput: null);
+ }
+
if (!VSTestTrace.TraceEnabled)
{
return;
@@ -188,13 +285,39 @@ private void OnTestResultsReceived(object sender, TestResultEventArgs args)
foreach (FailedTestResult failedTestResult in args.FailedTestResults)
{
VSTestTrace.SafeWriteTrace(() => $"FailedTestResult: {failedTestResult.Uid}, {failedTestResult.DisplayName}, " +
- $"{failedTestResult.State}, {failedTestResult.Duration}, {failedTestResult.Reason}, {failedTestResult.ErrorMessage}," +
- $"{failedTestResult.ErrorStackTrace}, {failedTestResult.StandardOutput}, {failedTestResult.ErrorOutput}, {failedTestResult.SessionUid}");
+ $"{failedTestResult.State}, {failedTestResult.Duration}, {failedTestResult.Reason}, {string.Join(", ", failedTestResult.Exceptions?.Select(e => $"{e.ErrorMessage}, {e.ErrorType}, {e.StackTrace}"))}" +
+ $"{failedTestResult.StandardOutput}, {failedTestResult.ErrorOutput}, {failedTestResult.SessionUid}");
}
}
+ public static TestOutcome ToOutcome(byte? testState)
+ {
+ return testState switch
+ {
+ TestStates.Passed => TestOutcome.Passed,
+ TestStates.Skipped => TestOutcome.Skipped,
+ TestStates.Failed => TestOutcome.Fail,
+ TestStates.Error => TestOutcome.Error,
+ TestStates.Timeout => TestOutcome.Timeout,
+ TestStates.Cancelled => TestOutcome.Canceled,
+ _ => throw new ArgumentOutOfRangeException(nameof(testState), $"Invalid test state value {testState}")
+ };
+ }
+
private void OnFileArtifactsReceived(object sender, FileArtifactEventArgs args)
{
+ var testApp = (TestApplication)sender;
+ var appInfo = _executions[testApp];
+
+ foreach (var artifact in args.FileArtifacts)
+ {
+ // TODO: Is artifact out of process
+ _output.ArtifactAdded(
+ outOfProcess: false,
+ appInfo.ModulePath, appInfo.TargetFramework, appInfo.Architecture, appInfo.ExecutionId,
+ artifact.TestDisplayName, artifact.FullPath);
+ }
+
if (!VSTestTrace.TraceEnabled)
{
return;
@@ -233,6 +356,15 @@ private void OnErrorReceived(object sender, ErrorEventArgs args)
private void OnTestProcessExited(object sender, TestProcessExitEventArgs args)
{
+ var testApplication = (TestApplication)sender;
+
+ // If the application exits too early we might not start the execution,
+ // e.g. if the parameter is incorrect.
+ if (_executions.TryGetValue(testApplication, out var appInfo))
+ {
+ _output.AssemblyRunCompleted(appInfo.ModulePath, appInfo.TargetFramework, appInfo.Architecture, appInfo.ExecutionId, args.ExitCode, string.Join(Environment.NewLine, args.OutputData), string.Join(Environment.NewLine, args.ErrorData));
+ }
+
if (!VSTestTrace.TraceEnabled)
{
return;
diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformOptions.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformOptions.cs
index f894c4cab66f..cd0a128146ce 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 ListTestsOption = new("--list-tests")
+ {
+ Description = LocalizableStrings.CmdListTestsDescription,
+ Arity = ArgumentArity.Zero
+ };
}
}
diff --git a/src/Cli/dotnet/dotnet.csproj b/src/Cli/dotnet/dotnet.csproj
index 82db3621e7fc..f19ef88ebd53 100644
--- a/src/Cli/dotnet/dotnet.csproj
+++ b/src/Cli/dotnet/dotnet.csproj
@@ -100,6 +100,7 @@
+