Skip to content

Commit 2d113aa

Browse files
Allow capturing console and trace output (#299)
* Directly replace with .NET 9 * Fix null issue in rewriter * Allow capturing console and trace output
1 parent b018054 commit 2d113aa

File tree

9 files changed

+251
-15
lines changed

9 files changed

+251
-15
lines changed

src/Exercism.TestRunner.CSharp/Options.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ public Options(string slug, string inputDirectory, string outputDirectory) =>
2121
public string TestsFilePath => Path.Combine(InputDirectory, $"{Exercise}Tests.cs");
2222

2323
public string ProjectFilePath => Path.Combine(InputDirectory, $"{Exercise}.csproj");
24+
25+
public string AssemblyInfoFilePath => Path.Combine(InputDirectory, "AssemblyInfo.cs");
2426

2527
public string BuildLogFilePath => Path.Combine(InputDirectory, "msbuild.log");
2628

src/Exercism.TestRunner.CSharp/TestSuite.cs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ namespace Exercism.TestRunner.CSharp
77
{
88
internal class TestSuite
99
{
10+
private const string AssemblyInfo = "[assembly: CaptureConsole]\n[assembly: CaptureTrace]\n";
11+
1012
private readonly SyntaxTree _originalSyntaxTree;
1113
private readonly string _originalProjectFile;
1214
private readonly Options _options;
@@ -20,14 +22,14 @@ private TestSuite(SyntaxTree originalSyntaxTree, string originalProjectFile, Opt
2022

2123
public TestRun Run()
2224
{
23-
Rewrite();
24-
RunDotnetTest();
25-
UndoRewrite();
25+
BeforeTests();
26+
RunTests();
27+
AfterTests();
2628

2729
return TestRunParser.Parse(_options, _originalSyntaxTree);
2830
}
2931

30-
private void RunDotnetTest()
32+
private void RunTests()
3133
{
3234
var workingDirectory = Path.GetDirectoryName(_options.TestsFilePath)!;
3335
RunProcess("dotnet", "restore --source /root/.nuget/packages/", workingDirectory);
@@ -46,32 +48,46 @@ private static void RunProcess(string command, string arguments, string workingD
4648
Process.Start(processStartInfo)?.WaitForExit();
4749
}
4850

49-
private void Rewrite()
51+
private void BeforeTests()
5052
{
5153
RewriteProjectFile();
5254
RewriteTestsFile();
55+
56+
if (CaptureOutput)
57+
AddCaptureOuputAssemblyAttributes();
5358
}
5459

5560
private void RewriteProjectFile() =>
5661
File.WriteAllText(_options.ProjectFilePath,
5762
_originalProjectFile
58-
.Replace("net5.0", "net8.0")
59-
.Replace("net6.0", "net8.0")
60-
.Replace("net7.0", "net8.0")
63+
.Replace("net5.0", "net9.0")
64+
.Replace("net6.0", "net9.0")
65+
.Replace("net7.0", "net9.0")
6166
.Replace("net8.0", "net9.0"));
6267

63-
private void RewriteTestsFile() => File.WriteAllText(_options.TestsFilePath, _originalSyntaxTree.Rewrite().ToString());
68+
private void RewriteTestsFile() =>
69+
File.WriteAllText(_options.TestsFilePath, _originalSyntaxTree.Rewrite().ToString());
70+
71+
private void AddCaptureOuputAssemblyAttributes() =>
72+
File.WriteAllText(_options.AssemblyInfoFilePath, AssemblyInfo);
6473

65-
private void UndoRewrite()
74+
private void AfterTests()
6675
{
6776
UndoRewriteProjectFile();
6877
UndoRewriteTestsFile();
78+
79+
if (CaptureOutput)
80+
UndoAddCaptureOuputAssemblyAttributes();
6981
}
7082

7183
private void UndoRewriteProjectFile() => File.WriteAllText(_options.ProjectFilePath, _originalProjectFile);
7284

7385
private void UndoRewriteTestsFile() => File.WriteAllText(_options.TestsFilePath, _originalSyntaxTree.ToString());
7486

87+
private void UndoAddCaptureOuputAssemblyAttributes() => File.Delete(_options.AssemblyInfoFilePath);
88+
89+
private bool CaptureOutput => _originalProjectFile.Contains("xunit.v3");
90+
7591
public static TestSuite FromOptions(Options options)
7692
{
7793
var originalSyntaxTree = CSharpSyntaxTree.ParseText(File.ReadAllText(options.TestsFilePath));

src/Exercism.TestRunner.CSharp/TestsRewriter.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
using Microsoft.CodeAnalysis.CSharp;
33
using Microsoft.CodeAnalysis.CSharp.Syntax;
44

5-
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
6-
75
namespace Exercism.TestRunner.CSharp
86
{
97
internal static class TestsRewriter
@@ -12,15 +10,14 @@ public static SyntaxTree Rewrite(this SyntaxTree tree) =>
1210
tree.WithRootAndOptions(tree.GetRoot().Rewrite(), tree.Options);
1311

1412
private static SyntaxNode Rewrite(this SyntaxNode node) =>
15-
node.UnskipTests()
16-
.NormalizeWhitespace();
13+
node.UnskipTests().NormalizeWhitespace();
1714

1815
private static SyntaxNode UnskipTests(this SyntaxNode testsRoot) =>
1916
new UnskipTestsRewriter().Visit(testsRoot);
2017

2118
private class UnskipTestsRewriter : CSharpSyntaxRewriter
2219
{
23-
public override SyntaxNode VisitAttributeArgument(AttributeArgumentSyntax node) =>
20+
public override SyntaxNode? VisitAttributeArgument(AttributeArgumentSyntax node) =>
2421
node.NameEquals?.Name.Identifier.Text == "Skip" ? null : base.VisitAttributeArgument(node);
2522
}
2623
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System.Diagnostics;
2+
3+
public static class Fake
4+
{
5+
public static int Add(int x, int y)
6+
{
7+
Console.WriteLine($"Console: adding {x} and {y}");
8+
Trace.WriteLine($"Trace: adding {x} and {y}");
9+
return x + y;
10+
}
11+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<OutputType>Exe</OutputType>
6+
<Nullable>enable</Nullable>
7+
<ImplicitUsings>enable</ImplicitUsings>
8+
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<Using Include="Xunit" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
17+
<PackageReference Include="xunit.v3" Version="1.1.0" />
18+
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.1" />
19+
<PackageReference Include="Exercism.Tests.xunit.v3" Version="0.1.0-beta1" />
20+
</ItemGroup>
21+
22+
</Project>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
public class FakeTests
2+
{
3+
[Fact]
4+
public void Add_should_add_numbers() => Assert.Equal(2, Fake.Add(1, 1));
5+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"version": 3,
3+
"status": "pass",
4+
"tests": [
5+
{
6+
"name": "Add should add numbers",
7+
"status": "pass",
8+
"output": "Console: adding 1 and 1\nTrace: adding 1 and 1",
9+
"test_code": "Assert.Equal(2, Fake.Add(1, 1))"
10+
}
11+
]
12+
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
{
2+
"version": 1,
3+
"dependencies": {
4+
"net9.0": {
5+
"Exercism.Tests.xunit.v3": {
6+
"type": "Direct",
7+
"requested": "[0.1.0-beta1, )",
8+
"resolved": "0.1.0-beta1",
9+
"contentHash": "XjVtQWWxmHDDj7UMdkPKpBFFKnsW0tkBhlyJSfFFh+fWwGemyyJwJYhdsvWhiKKCY7zItB+mI/o0OQtOKQxUhA==",
10+
"dependencies": {
11+
"xunit.v3.extensibility.core": "1.1.0"
12+
}
13+
},
14+
"Microsoft.NET.Test.Sdk": {
15+
"type": "Direct",
16+
"requested": "[17.12.0, )",
17+
"resolved": "17.12.0",
18+
"contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
19+
"dependencies": {
20+
"Microsoft.CodeCoverage": "17.12.0",
21+
"Microsoft.TestPlatform.TestHost": "17.12.0"
22+
}
23+
},
24+
"xunit.runner.visualstudio": {
25+
"type": "Direct",
26+
"requested": "[3.0.1, )",
27+
"resolved": "3.0.1",
28+
"contentHash": "lbyYtsBxA8Pz8kaf5Xn/Mj1mL9z2nlBWdZhqFaj66nxXBa4JwiTDm4eGcpSMet6du9TOWI6bfha+gQR6+IHawg=="
29+
},
30+
"xunit.v3": {
31+
"type": "Direct",
32+
"requested": "[1.1.0, )",
33+
"resolved": "1.1.0",
34+
"contentHash": "1ckSz5GVswlM9TCk5bGdHOjnYwqAWjkeqxckoHawQIA8sTeuN+RCBUypCi5A/Um0XlczRx5TjAK5W6BbN0HLcQ==",
35+
"dependencies": {
36+
"xunit.analyzers": "1.20.0",
37+
"xunit.v3.assert": "[1.1.0]",
38+
"xunit.v3.core": "[1.1.0]"
39+
}
40+
},
41+
"Microsoft.Bcl.AsyncInterfaces": {
42+
"type": "Transitive",
43+
"resolved": "6.0.0",
44+
"contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg=="
45+
},
46+
"Microsoft.CodeCoverage": {
47+
"type": "Transitive",
48+
"resolved": "17.12.0",
49+
"contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
50+
},
51+
"Microsoft.Testing.Extensions.TrxReport.Abstractions": {
52+
"type": "Transitive",
53+
"resolved": "1.5.3",
54+
"contentHash": "h34zKNpGyni66VH738mRHeXSnf3klSShUdavUWNhSfWICUUi5aXeI0LBvoX/ad93N0+9xBDU3Fyi6WfxrwKQGw==",
55+
"dependencies": {
56+
"Microsoft.Testing.Platform": "1.5.3"
57+
}
58+
},
59+
"Microsoft.Testing.Platform": {
60+
"type": "Transitive",
61+
"resolved": "1.5.3",
62+
"contentHash": "WqJydnJ99dEKtquR9HwINz104ehWJKTXbQQrydGatlLRw14bmsx0pa8+E6KUXMYXZAimN0swWlDmcJGjjW4TIg=="
63+
},
64+
"Microsoft.Testing.Platform.MSBuild": {
65+
"type": "Transitive",
66+
"resolved": "1.5.3",
67+
"contentHash": "bOtpRMSPeT5YLQo+NNY8EtdNTphAUcmALjW4ABU7P0rb6yR2XAZau3TzNieLmR3lRuwudguWzzBhgcLRXwZh0A==",
68+
"dependencies": {
69+
"Microsoft.Testing.Platform": "1.5.3"
70+
}
71+
},
72+
"Microsoft.TestPlatform.ObjectModel": {
73+
"type": "Transitive",
74+
"resolved": "17.12.0",
75+
"contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==",
76+
"dependencies": {
77+
"System.Reflection.Metadata": "1.6.0"
78+
}
79+
},
80+
"Microsoft.TestPlatform.TestHost": {
81+
"type": "Transitive",
82+
"resolved": "17.12.0",
83+
"contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==",
84+
"dependencies": {
85+
"Microsoft.TestPlatform.ObjectModel": "17.12.0",
86+
"Newtonsoft.Json": "13.0.1"
87+
}
88+
},
89+
"Newtonsoft.Json": {
90+
"type": "Transitive",
91+
"resolved": "13.0.1",
92+
"contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
93+
},
94+
"System.Collections.Immutable": {
95+
"type": "Transitive",
96+
"resolved": "8.0.0",
97+
"contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg=="
98+
},
99+
"System.Memory": {
100+
"type": "Transitive",
101+
"resolved": "4.6.0",
102+
"contentHash": "OEkbBQoklHngJ8UD8ez2AERSk2g+/qpAaSWWCBFbpH727HxDq5ydVkuncBaKcKfwRqXGWx64dS6G1SUScMsitg=="
103+
},
104+
"System.Reflection.Metadata": {
105+
"type": "Transitive",
106+
"resolved": "1.6.0",
107+
"contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ=="
108+
},
109+
"xunit.analyzers": {
110+
"type": "Transitive",
111+
"resolved": "1.20.0",
112+
"contentHash": "HElev2E9vFbPxwKRQtpCSSzLOu8M/N9EWBCB37v7SRx6z4Lbj19FxfLEig3v9jiI6s4b0l2uena91nEsTWl9jA=="
113+
},
114+
"xunit.v3.assert": {
115+
"type": "Transitive",
116+
"resolved": "1.1.0",
117+
"contentHash": "4D+eM08ImfhA+zLbRzi8HA4qsT98zDxgaCD7vCg8yFesokKsgSsqWsAmImHFjVymGVhVS7WFGb19d6v1k9i0xQ==",
118+
"dependencies": {
119+
"System.Collections.Immutable": "8.0.0",
120+
"System.Memory": "4.6.0"
121+
}
122+
},
123+
"xunit.v3.common": {
124+
"type": "Transitive",
125+
"resolved": "1.1.0",
126+
"contentHash": "Cq55z8pC7fOkfj+3TB/YQ6OW96qWqxKiMd15CtkIl37VtV9EsiUL4B4HsR6VLJCzkk7cBiXQ1ABVIcp3TCm6HQ==",
127+
"dependencies": {
128+
"Microsoft.Bcl.AsyncInterfaces": "6.0.0"
129+
}
130+
},
131+
"xunit.v3.core": {
132+
"type": "Transitive",
133+
"resolved": "1.1.0",
134+
"contentHash": "kXP/1d3jnQ2m4skcdM3gSMmubI6P747D6KVswzeedysgFkLj2xJlfo7p7slsmtEnp8BZb8X6D92Hssd/UtVPMw==",
135+
"dependencies": {
136+
"Microsoft.Testing.Platform.MSBuild": "1.5.3",
137+
"xunit.v3.extensibility.core": "[1.1.0]",
138+
"xunit.v3.runner.inproc.console": "[1.1.0]"
139+
}
140+
},
141+
"xunit.v3.extensibility.core": {
142+
"type": "Transitive",
143+
"resolved": "1.1.0",
144+
"contentHash": "AeQbbYN001x0c+B9pqwml6jZPovHz8O/sOp7jmrjz90rUzz/QPal12SlHLKYszR44CMnW4MsDam3RYT5pkYUxw==",
145+
"dependencies": {
146+
"xunit.v3.common": "[1.1.0]"
147+
}
148+
},
149+
"xunit.v3.runner.common": {
150+
"type": "Transitive",
151+
"resolved": "1.1.0",
152+
"contentHash": "Q81J0VPuu8fpF+/1CIjThqKKUjnqh0TQrLlD0iORkF75KdsOV+iGWT8c3AVuY96kDoxXxkTf0ZvJsK6o9osc1A==",
153+
"dependencies": {
154+
"xunit.v3.common": "[1.1.0]"
155+
}
156+
},
157+
"xunit.v3.runner.inproc.console": {
158+
"type": "Transitive",
159+
"resolved": "1.1.0",
160+
"contentHash": "lX/4TwIJe9ysCd5dqLk/Doq8ieYaZGivgf95xR59wRuSV+nHzHnyhpjXfaPUp8nkncUH1rOmJ85o1KebipisXQ==",
161+
"dependencies": {
162+
"Microsoft.Testing.Extensions.TrxReport.Abstractions": "1.5.3",
163+
"Microsoft.Testing.Platform": "1.5.3",
164+
"xunit.v3.extensibility.core": "[1.1.0]",
165+
"xunit.v3.runner.common": "[1.1.0]"
166+
}
167+
}
168+
}
169+
}
170+
}

tests/different-types-of-tests/expected_results.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
{
1616
"name": "Div should divide numbers",
1717
"status": "pass",
18+
"output": "Ok, passed 100 tests.",
1819
"test_code": "Prop.ForAll\u003CPositiveInt\u003E(i =\u003E Fake.Div(i.Get, i.Get) == 1)\n .QuickCheckThrowOnFailure()"
1920
}
2021
]

0 commit comments

Comments
 (0)