Skip to content

Commit ef7ff07

Browse files
authored
Capture and record output from tests from Console.Out into the test results (#63606)
* Capture and record output from tests from Console.Out into the test results. * Simplify construction. * Fix nullability * Fix constructor visibility
1 parent ad1b5b8 commit ef7ff07

File tree

4 files changed

+58
-15
lines changed

4 files changed

+58
-15
lines changed

src/tests/Common/XUnitWrapperGenerator/ITestInfo.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,13 +314,15 @@ sealed class NoTestReporting : ITestReporterWrapper
314314

315315
sealed class WrapperLibraryTestSummaryReporting : ITestReporterWrapper
316316
{
317-
private string _summaryLocalIdentifier;
317+
private readonly string _summaryLocalIdentifier;
318318
private readonly string _filterLocalIdentifier;
319+
private readonly string _outputRecorderIdentifier;
319320

320-
public WrapperLibraryTestSummaryReporting(string summaryLocalIdentifier, string filterLocalIdentifier)
321+
public WrapperLibraryTestSummaryReporting(string summaryLocalIdentifier, string filterLocalIdentifier, string outputRecorderIdentifier)
321322
{
322323
_summaryLocalIdentifier = summaryLocalIdentifier;
323324
_filterLocalIdentifier = filterLocalIdentifier;
325+
_outputRecorderIdentifier = outputRecorderIdentifier;
324326
}
325327

326328
public string WrapTestExecutionWithReporting(string testExecutionExpression, ITestInfo test)
@@ -332,12 +334,13 @@ public string WrapTestExecutionWithReporting(string testExecutionExpression, ITe
332334
builder.AppendLine($"System.TimeSpan testStart = stopwatch.Elapsed;");
333335
builder.AppendLine("try {");
334336
builder.AppendLine($"System.Console.WriteLine(\"Running test: {{0}}\", {test.TestNameExpression});");
337+
builder.AppendLine($"{_outputRecorderIdentifier}.ResetTestOutput();");
335338
builder.AppendLine(testExecutionExpression);
336-
builder.AppendLine($"{_summaryLocalIdentifier}.ReportPassedTest({test.TestNameExpression}, \"{test.ContainingType}\", @\"{test.Method}\", stopwatch.Elapsed - testStart);");
339+
builder.AppendLine($"{_summaryLocalIdentifier}.ReportPassedTest({test.TestNameExpression}, \"{test.ContainingType}\", @\"{test.Method}\", stopwatch.Elapsed - testStart, {_outputRecorderIdentifier}.GetTestOutput());");
337340
builder.AppendLine($"System.Console.WriteLine(\"Passed test: {{0}}\", {test.TestNameExpression});");
338341
builder.AppendLine("}");
339342
builder.AppendLine("catch (System.Exception ex) {");
340-
builder.AppendLine($"{_summaryLocalIdentifier}.ReportFailedTest({test.TestNameExpression}, \"{test.ContainingType}\", @\"{test.Method}\", stopwatch.Elapsed - testStart, ex);");
343+
builder.AppendLine($"{_summaryLocalIdentifier}.ReportFailedTest({test.TestNameExpression}, \"{test.ContainingType}\", @\"{test.Method}\", stopwatch.Elapsed - testStart, ex, {_outputRecorderIdentifier}.GetTestOutput());");
341344
builder.AppendLine($"System.Console.WriteLine(\"Failed test: {{0}}\", {test.TestNameExpression});");
342345
builder.AppendLine("}");
343346

src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,15 @@ private static string GenerateFullTestRunner(ImmutableArray<ITestInfo> testInfos
151151
{
152152
// For simplicity, we'll use top-level statements for the generated Main method.
153153
StringBuilder builder = new();
154-
ITestReporterWrapper reporter = new WrapperLibraryTestSummaryReporting("summary", "filter");
155154
builder.AppendLine(string.Join("\n", aliasMap.Values.Where(alias => alias != "global").Select(alias => $"extern alias {alias};")));
156155

157156
builder.AppendLine("XUnitWrapperLibrary.TestFilter filter = args.Length != 0 ? new XUnitWrapperLibrary.TestFilter(args[0]) : null;");
158157
builder.AppendLine("XUnitWrapperLibrary.TestSummary summary = new();");
159158
builder.AppendLine("System.Diagnostics.Stopwatch stopwatch = System.Diagnostics.Stopwatch.StartNew();");
159+
builder.AppendLine("XUnitWrapperLibrary.TestOutputRecorder outputRecorder = new(System.Console.Out);");
160+
builder.AppendLine("System.Console.SetOut(outputRecorder);");
161+
162+
ITestReporterWrapper reporter = new WrapperLibraryTestSummaryReporting("summary", "filter", "outputRecorder");
160163

161164
foreach (ITestInfo test in testInfos)
162165
{
@@ -175,16 +178,19 @@ private static string GenerateXHarnessTestRunner(ImmutableArray<ITestInfo> testI
175178
StringBuilder builder = new();
176179
builder.AppendLine(string.Join("\n", aliasMap.Values.Where(alias => alias != "global").Select(alias => $"extern alias {alias};")));
177180

178-
181+
179182
builder.AppendLine("try {");
180183
builder.AppendLine($@"return await XHarnessRunnerLibrary.RunnerEntryPoint.RunTests(RunTests, ""{assemblyName}"", args.Length != 0 ? args[0] : null);");
181184
builder.AppendLine("} catch(System.Exception ex) { System.Console.WriteLine(ex.ToString()); return 101; }");
182185

183186
builder.AppendLine("static XUnitWrapperLibrary.TestSummary RunTests(XUnitWrapperLibrary.TestFilter filter)");
184187
builder.AppendLine("{");
185188
builder.AppendLine("XUnitWrapperLibrary.TestSummary summary = new();");
186-
ITestReporterWrapper reporter = new WrapperLibraryTestSummaryReporting("summary", "filter");
187189
builder.AppendLine("System.Diagnostics.Stopwatch stopwatch = new();");
190+
builder.AppendLine("XUnitWrapperLibrary.TestOutputRecorder outputRecorder = new(System.Console.Out);");
191+
builder.AppendLine("System.Console.SetOut(outputRecorder);");
192+
193+
ITestReporterWrapper reporter = new WrapperLibraryTestSummaryReporting("summary", "filter", "outputRecorder");
188194

189195
foreach (ITestInfo test in testInfos)
190196
{
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
//
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.IO;
8+
using System.Text;
9+
using System.Threading;
10+
11+
namespace XUnitWrapperLibrary;
12+
13+
public sealed class TestOutputRecorder : TextWriter
14+
{
15+
private TextWriter _inner;
16+
private ThreadLocal<StringBuilder> _testOutput = new(() => new StringBuilder());
17+
18+
public TestOutputRecorder(TextWriter inner)
19+
{
20+
_inner = inner;
21+
}
22+
23+
public override void Write(char value)
24+
{
25+
_inner.Write(value);
26+
_testOutput.Value!.Append(value);
27+
}
28+
29+
public override Encoding Encoding => _inner.Encoding;
30+
31+
public void ResetTestOutput() => _testOutput.Value!.Clear();
32+
33+
public string GetTestOutput() => _testOutput.Value!.ToString();
34+
}

src/tests/Common/XUnitWrapperLibrary/TestSummary.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace XUnitWrapperLibrary;
99

1010
public class TestSummary
1111
{
12-
readonly record struct TestResult(string Name, string ContainingTypeName, string MethodName, TimeSpan Duration, Exception? Exception, string? SkipReason);
12+
readonly record struct TestResult(string Name, string ContainingTypeName, string MethodName, TimeSpan Duration, Exception? Exception, string? SkipReason, string? Output);
1313

1414
public int PassedTests { get; private set; } = 0;
1515
public int FailedTests { get; private set; } = 0;
@@ -19,22 +19,22 @@ public class TestSummary
1919

2020
private DateTime _testRunStart = DateTime.Now;
2121

22-
public void ReportPassedTest(string name, string containingTypeName, string methodName, TimeSpan duration)
22+
public void ReportPassedTest(string name, string containingTypeName, string methodName, TimeSpan duration, string output)
2323
{
2424
PassedTests++;
25-
_testResults.Add(new TestResult(name, containingTypeName, methodName, duration, null, null));
25+
_testResults.Add(new TestResult(name, containingTypeName, methodName, duration, null, null, output));
2626
}
2727

28-
public void ReportFailedTest(string name, string containingTypeName, string methodName, TimeSpan duration, Exception ex)
28+
public void ReportFailedTest(string name, string containingTypeName, string methodName, TimeSpan duration, Exception ex, string output)
2929
{
3030
FailedTests++;
31-
_testResults.Add(new TestResult(name, containingTypeName, methodName, duration, ex, null));
31+
_testResults.Add(new TestResult(name, containingTypeName, methodName, duration, ex, null, output));
3232
}
3333

3434
public void ReportSkippedTest(string name, string containingTypeName, string methodName, TimeSpan duration, string reason)
3535
{
3636
SkippedTests++;
37-
_testResults.Add(new TestResult(name, containingTypeName, methodName, duration, null, reason));
37+
_testResults.Add(new TestResult(name, containingTypeName, methodName, duration, null, reason, null));
3838
}
3939

4040
public string GetTestResultOutput(string assemblyName)
@@ -72,15 +72,15 @@ public string GetTestResultOutput(string assemblyName)
7272
resultsFile.Append($@"<test name=""{test.Name}"" type=""{test.ContainingTypeName}"" method=""{test.MethodName}"" time=""{test.Duration.TotalSeconds:F6}"" ");
7373
if (test.Exception is not null)
7474
{
75-
resultsFile.AppendLine($@"result=""Fail""><failure exception-type=""{test.Exception.GetType()}""><message><![CDATA[{test.Exception.Message}]]></message><stack-trace><![CDATA[{test.Exception.StackTrace}]]></stack-trace></failure></test>");
75+
resultsFile.AppendLine($@"result=""Fail""><failure exception-type=""{test.Exception.GetType()}""><message><![CDATA[{test.Exception.Message}]]></message><stack-trace><![CDATA[{test.Exception.StackTrace}]]></stack-trace></failure><output><![CDATA[{test.Output}]]></output></test>");
7676
}
7777
else if (test.SkipReason is not null)
7878
{
7979
resultsFile.AppendLine($@"result=""Skip""><reason><![CDATA[{test.SkipReason}]]></reason></test>");
8080
}
8181
else
8282
{
83-
resultsFile.AppendLine(@" result=""Pass"" />");
83+
resultsFile.AppendLine($@" result=""Pass""><output><![CDATA[{test.Output}]]></output></test>");
8484
}
8585
}
8686

0 commit comments

Comments
 (0)