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
17 changes: 15 additions & 2 deletions src/Microsoft.TestPlatform.Build/Tasks/VSTestTask2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public class VSTestTask2 : ToolTask, ITestTask
public string? VSTestArtifactsProcessingMode { get; set; }
public string? VSTestSessionCorrelationId { get; set; }

private readonly string _testResultSplitter = "++++";
private readonly string[] _testResultSplitterArray = new[] { "++++" };

private readonly string _errorSplitter = "||||";
private readonly string[] _errorSplitterArray = new[] { "||||" };
Expand Down Expand Up @@ -91,8 +93,7 @@ protected override void LogEventsFromTextOutput(string singleLine, MessageImport
return;
}
}

if (singleLine.StartsWith(_fullErrorSplitter))
else if (singleLine.StartsWith(_fullErrorSplitter))
{
var parts = singleLine.Split(_fullErrorSplitterArray, StringSplitOptions.None);
if (parts.Length > 1)
Expand All @@ -119,6 +120,18 @@ protected override void LogEventsFromTextOutput(string singleLine, MessageImport
return;
}
}
else if (singleLine.StartsWith(_testResultSplitter))
{
var parts = singleLine.Split(_testResultSplitterArray, StringSplitOptions.None);
if (parts.Length == 3)
{
var outcome = parts[1];
var testName = parts[2];

Log.LogMessage(MessageImportance.Low, $"{outcome} {testName}");
return;
}
}

base.LogEventsFromTextOutput(singleLine, messageImportance);
}
Expand Down
127 changes: 74 additions & 53 deletions src/vstest.console/Internal/MSBuildLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

namespace Microsoft.VisualStudio.TestPlatform.CommandLine.Internal;

// Not using FriendlyName because it
[ExtensionUri(ExtensionUri)]
[FriendlyName(FriendlyName)]
internal class MSBuildLogger : ITestLoggerWithParameters
Expand Down Expand Up @@ -100,72 +99,83 @@ private void TestResultHandler(object? sender, TestResultEventArgs e)
TPDebug.Assert(Output != null, "Initialize should have been called.");
switch (e.Result.Outcome)
{
case TestOutcome.Passed:
case TestOutcome.Skipped:

var test = e.Result.TestCase.DisplayName;
var outcome = e.Result.Outcome == TestOutcome.Passed
? CommandLineResources.PassedTestIndicator
: CommandLineResources.SkippedTestIndicator;
var info = $"++++{outcome}++++{ReplacePlusSeparator(test)}";

Debug.WriteLine(">>>>MESSAGE:" + info);
Output.Information(false, info);
break;
case TestOutcome.Failed:

var result = e.Result;
if (!StringUtils.IsNullOrWhiteSpace(result.ErrorStackTrace))
{
var result = e.Result;
if (!StringUtils.IsNullOrWhiteSpace(result.ErrorStackTrace))
var maxLength = 1000;
string? error = null;
if (result.ErrorMessage != null)
{
var maxLength = 1000;
string? error = null;
if (result.ErrorMessage != null)
{
var oneLineMessage = result.ErrorMessage.Replace(Environment.NewLine, " ");
error = oneLineMessage.Length > maxLength ? oneLineMessage.Substring(0, maxLength) : oneLineMessage;
}
// Do not use environment.newline here, we want to replace also \n on Windows.
var oneLineMessage = result.ErrorMessage.Replace("\n", " ").Replace("\r", " ");
error = oneLineMessage.Length > maxLength ? oneLineMessage.Substring(0, maxLength) : oneLineMessage;
}

string? stackFrame = null;
var stackFrames = Regex.Split(result.ErrorStackTrace, Environment.NewLine);
string? line = null;
string? file = null;
string? place = null;
if (stackFrames.Length > 0)
string? stackFrame = null;
var stackFrames = Regex.Split(result.ErrorStackTrace, Environment.NewLine);
string? line = null;
string? file = null;
string? place = null;
if (stackFrames.Length > 0)
{
foreach (var frame in stackFrames.Take(20))
{
foreach (var frame in stackFrames.Take(20))
if (TryGetStackFrameLocation(frame, out line, out file, out place))
{
if (TryGetStackFrameLocation(frame, out line, out file, out place))
{
break;
}
break;
}
}
}

// We did not find any stack frame with location in the first 20 frames.
// Try getting location of the test.
if (file == null)
// We did not find any stack frame with location in the first 20 frames.
// Try getting location of the test.
if (file == null)
{
if (!StringUtils.IsNullOrEmpty(result.TestCase.CodeFilePath))
{
if (!StringUtils.IsNullOrEmpty(result.TestCase.CodeFilePath))
{
// if there are no symbols but we collect source info, us the source info.
file = result.TestCase.CodeFilePath;
line = result.TestCase.LineNumber > 0 ? result.TestCase.LineNumber.ToString(CultureInfo.InvariantCulture) : null;
place = stackFrame;
}
else
{
// if there are no symbols and no source info use the dll
place = result.TestCase.DisplayName;
file = result.TestCase.Source;
}
// if there are no symbols but we collect source info, us the source info.
file = result.TestCase.CodeFilePath;
line = result.TestCase.LineNumber > 0 ? result.TestCase.LineNumber.ToString(CultureInfo.InvariantCulture) : null;
place = stackFrame;
}
else
{
// if there are no symbols and no source info use the dll
place = result.TestCase.DisplayName;
file = result.TestCase.Source;
}

place = $"({result.TestCase.DisplayName}) {place}";
var message = $"||||{ReplacePipeSeparator(file)}||||{line}||||{ReplacePipeSeparator(place)}||||{ReplacePipeSeparator(error)}";

Trace.WriteLine(">>>>MESSAGE:" + message);
Output.Error(false, message);

var fullError = $"~~~~{ReplaceTildaSeparator(result.ErrorMessage)}~~~~{ReplaceTildaSeparator(result.ErrorStackTrace)}";
Output.Information(false, fullError);
return;
}
else
{
Output.Error(false, result.DisplayName?.Replace(Environment.NewLine, " ") ?? string.Empty);
}

place = $"({result.TestCase.DisplayName}) {place}";
var message = $"||||{ReplacePipeSeparator(file)}||||{line}||||{ReplacePipeSeparator(place)}||||{ReplacePipeSeparator(error)}";

break;
Debug.WriteLine(">>>>MESSAGE:" + message);
Output.Error(false, message);

var fullError = $"~~~~{ReplaceTildaSeparator(result.ErrorMessage)}~~~~{ReplaceTildaSeparator(result.ErrorStackTrace)}";
Output.Information(false, fullError);
return;
}
else
{
Output.Error(false, result.DisplayName?.Replace(Environment.NewLine, " ") ?? string.Empty);
}

break;
}
}

Expand All @@ -186,7 +196,7 @@ private static bool TryGetStackFrameLocation(string stackFrame, out string? line
line = match.Groups["line"].Value;
}

Trace.WriteLine($">>>> {(match.Success ? "MATCH" : "NOMATCH")} {stackFrame}");
Debug.WriteLine($">>>> {(match.Success ? "MATCH" : "NOMATCH")} {stackFrame}");

return match.Success;
}
Expand All @@ -203,6 +213,17 @@ private static bool TryGetStackFrameLocation(string stackFrame, out string? line
return text.Replace("||||", "____");
}

private static string? ReplacePlusSeparator(string? text)
{
if (text == null)
{
return null;
}

// Remove any occurrence of message splitter.
return text.Replace("++++", "____");
}

private static string? ReplaceTildaSeparator(string? text)
{
if (text == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ public void RunDotnetTestWithCsproj(RunnerInfo runnerInfo)
// C:\Users\nohwnd\AppData\Local\Temp\vstest\xvoVt\UnitTest1.cs(41): error VSTEST1: (FailingTest) SampleUnitTestProject.UnitTest1.FailingTest() Assert.AreEqual failed. Expected:<2>. Actual:<3>. [C:\Users\nohwnd\AppData\Local\Temp\vstest\xvoVt\SimpleTestProject.csproj::TargetFramework=netcoreapp3.1]

StdOutputContains("error VSTEST1: (FailingTest) SampleUnitTestProject.UnitTest1.FailingTest() Assert.AreEqual failed. Expected:<2>. Actual:<3>.");
// We are sending those as low prio messages, they won't show up on screen but will be in binlog.
//StdOutputContains("passed PassingTest");
//StdOutputContains("skipped SkippingTest");
ExitCodeEquals(1);
}
}