diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 034cae4..c31beb8 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-format": { - "version": "4.1.131201", + "version": "5.1.225507", "commands": [ "dotnet-format" ] diff --git a/Dockerfile b/Dockerfile index 4d50655..48f0079 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:5.0.202-alpine3.12-amd64 AS build +FROM mcr.microsoft.com/dotnet/sdk:5.0.202-alpine3.13-amd64 AS build WORKDIR /app # Copy csproj and restore as distinct layers @@ -7,21 +7,14 @@ RUN dotnet restore -r linux-musl-x64 # Copy everything else and build COPY src/Exercism.TestRunner.CSharp/ ./ -RUN dotnet publish -r linux-musl-x64 -c Release -o /opt/test-runner --no-restore -p:PublishReadyToRun=true - -# Pre-install packages for offline usage -RUN dotnet add package Microsoft.NET.Test.Sdk -v 16.8.3 && \ - dotnet add package xunit -v 2.4.1 && \ - dotnet add package xunit.runner.visualstudio -v 2.4.3 && \ - dotnet add package Exercism.Tests -v 0.1.0-alpha +RUN dotnet publish -r linux-musl-x64 -c Release -o /opt/test-runner --no-restore --self-contained true # Build runtime image -FROM mcr.microsoft.com/dotnet/sdk:5.0.202-alpine3.12-amd64 AS runtime +FROM mcr.microsoft.com/dotnet/runtime-deps:5.0.5-alpine3.13-amd64 AS runtime WORKDIR /opt/test-runner COPY --from=build /opt/test-runner/ . COPY --from=build /usr/local/bin/ /usr/local/bin/ -COPY --from=build /root/.nuget/packages/ /root/.nuget/packages/ COPY run.sh /opt/test-runner/bin/ diff --git a/src/Exercism.TestRunner.CSharp/Exercism.TestRunner.CSharp.csproj b/src/Exercism.TestRunner.CSharp/Exercism.TestRunner.CSharp.csproj index a360df8..e9343df 100644 --- a/src/Exercism.TestRunner.CSharp/Exercism.TestRunner.CSharp.csproj +++ b/src/Exercism.TestRunner.CSharp/Exercism.TestRunner.CSharp.csproj @@ -6,14 +6,17 @@ false - - - + + true + + - + + + diff --git a/src/Exercism.TestRunner.CSharp/Options.cs b/src/Exercism.TestRunner.CSharp/Options.cs index 7a302b5..e1a1e49 100644 --- a/src/Exercism.TestRunner.CSharp/Options.cs +++ b/src/Exercism.TestRunner.CSharp/Options.cs @@ -22,10 +22,6 @@ public Options(string slug, string inputDirectory, string outputDirectory) => public string TestsFilePath => Path.Combine(InputDirectory, $"{Exercise}Tests.cs"); - public string BuildLogFilePath => Path.Combine(InputDirectory, "msbuild.log"); - - public string TestResultsFilePath => Path.Combine(InputDirectory, "TestResults", "tests.trx"); - public string ResultsJsonFilePath => Path.GetFullPath(Path.Combine(OutputDirectory, "results.json")); private string Exercise => Slug.Dehumanize().Pascalize(); diff --git a/src/Exercism.TestRunner.CSharp/Program.cs b/src/Exercism.TestRunner.CSharp/Program.cs index 0b5ab7a..d08893e 100644 --- a/src/Exercism.TestRunner.CSharp/Program.cs +++ b/src/Exercism.TestRunner.CSharp/Program.cs @@ -15,8 +15,7 @@ private static void CreateTestResults(Options options) { Console.WriteLine($"[{DateTimeOffset.UtcNow:u}] Running test runner for '{options.Slug}' solution..."); - var testSuite = TestSuite.FromOptions(options); - var testRun = testSuite.Run(); + var testRun = TestSuite.RunTests(options); testRun.WriteToFile(options.ResultsJsonFilePath); Console.WriteLine($"[{DateTimeOffset.UtcNow:u}] Ran test runner for '{options.Slug}' solution"); diff --git a/src/Exercism.TestRunner.CSharp/StringExtensions.cs b/src/Exercism.TestRunner.CSharp/StringExtensions.cs index a8eb2f8..7893652 100644 --- a/src/Exercism.TestRunner.CSharp/StringExtensions.cs +++ b/src/Exercism.TestRunner.CSharp/StringExtensions.cs @@ -2,4 +2,7 @@ { public static string UseUnixNewlines(this string str) => str.Replace("\r\n", "\n"); + + public static string NullIfEmpty(this string str) => + str == string.Empty ? null : str; } \ No newline at end of file diff --git a/src/Exercism.TestRunner.CSharp/TestCompilation.cs b/src/Exercism.TestRunner.CSharp/TestCompilation.cs new file mode 100644 index 0000000..48bd5cd --- /dev/null +++ b/src/Exercism.TestRunner.CSharp/TestCompilation.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using Exercism.Tests; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Text; + +namespace Exercism.TestRunner.CSharp +{ + internal static class TestCompilation + { + public static Compilation Compile(Options options) => + CSharpCompilation.Create(Guid.NewGuid().ToString("N"), SyntaxTrees(options), References(), CompilationOptions()); + + private static IEnumerable SyntaxTrees(Options options) + { + SyntaxTree ParseSyntaxTree(string file) + { + var source = SourceText.From(File.OpenRead(file)); + var syntaxTree = CSharpSyntaxTree.ParseText(source, path: file); + + // We need to rewrite the test suite to un-skip all tests and capture any console output + if (file == options.TestsFilePath) + { + return syntaxTree.Rewrite(); + } + + return syntaxTree; + } + + return Directory.EnumerateFiles(options.InputDirectory, "*.cs", SearchOption.AllDirectories) + .Select(ParseSyntaxTree); + } + + private static CSharpCompilationOptions CompilationOptions() => + new(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release); + + private static IEnumerable References() + { + var trustedAssembliesPaths = ((string)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES"))!.Split(Path.PathSeparator); + return trustedAssembliesPaths + .Select(p => MetadataReference.CreateFromFile(p)) + .Append(MetadataReference.CreateFromFile(typeof(Xunit.FactAttribute).Assembly.Location)) + .Append(MetadataReference.CreateFromFile(typeof(Xunit.Assert).Assembly.Location)) + .Append(MetadataReference.CreateFromFile(typeof(TaskAttribute).Assembly.Location)); + } + } +} \ No newline at end of file diff --git a/src/Exercism.TestRunner.CSharp/TestResultParser.cs b/src/Exercism.TestRunner.CSharp/TestResultParser.cs index e9befb6..eaa3f5b 100644 --- a/src/Exercism.TestRunner.CSharp/TestResultParser.cs +++ b/src/Exercism.TestRunner.CSharp/TestResultParser.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Xml.Serialization; using Humanizer; @@ -10,80 +8,71 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Xunit.Runners; + namespace Exercism.TestRunner.CSharp { internal static class TestResultParser { - internal static TestResult[] FromFile(string logFilePath, SyntaxTree testsSyntaxTree) + public static TestResult[] FromTests(IEnumerable tests, SyntaxTree testsSyntaxTree) { - using var fileStream = File.OpenRead(logFilePath); - var result = (XmlTestRun)new XmlSerializer(typeof(XmlTestRun)).Deserialize(fileStream); - - if (result.Results == null) - return Array.Empty(); - - return result.ToTestResults(testsSyntaxTree); - } - - private static TestResult[] ToTestResults(this XmlTestRun result, SyntaxTree testsSyntaxTree) - { - var methodDeclarations = + var testMethods = testsSyntaxTree .GetRoot() .DescendantNodes() .OfType() .ToArray(); - - var testResults = - from unitTestResult in result.Results.UnitTestResult - let testMethodDeclaration = unitTestResult.TestMethod(methodDeclarations) - orderby testMethodDeclaration.GetLocation().GetLineSpan().StartLinePosition.Line - select ToTestResult(unitTestResult, testMethodDeclaration); - return testResults.ToArray(); + return tests + .Select(test => (test: test, testMethod: test.TestMethod(testMethods))) + .OrderBy(testAndMethod => Array.IndexOf(testMethods, testAndMethod.testMethod)) + .Select(testAndMethod => FromTest(testAndMethod.test, testAndMethod.testMethod)) + .ToArray(); } - private static TestResult ToTestResult(XmlUnitTestResult xmlUnitTestResult, MethodDeclarationSyntax testMethodDeclaration) => + private static TestResult FromTest(TestInfo test, MethodDeclarationSyntax testMethod) => + test switch + { + TestFailedInfo failedTest => FromFailedTest(failedTest, testMethod), + TestPassedInfo passedTest => FromPassedTest(passedTest, testMethod), + _ => throw new ArgumentOutOfRangeException(nameof(test)) + }; + + private static TestResult FromFailedTest(TestFailedInfo info, MethodDeclarationSyntax testMethod) => new() { - Name = xmlUnitTestResult.Name(), - Status = xmlUnitTestResult.Status(), - Message = xmlUnitTestResult.Message(), - Output = xmlUnitTestResult.Output(), - TaskId = testMethodDeclaration.TaskId(), - TestCode = testMethodDeclaration.TestCode() + Name = info.Name(), + Status = TestStatus.Fail, + Message = info.Message(), + Output = info.Output(), + TaskId = testMethod.TaskId(), + TestCode = testMethod.TestCode() }; - private static MethodDeclarationSyntax TestMethod(this XmlUnitTestResult xmlUnitTestResult, IEnumerable methodDeclarations) - { - var classAndMethodName = xmlUnitTestResult.TestName.Split("."); - var className = classAndMethodName[0]; - var methodName = classAndMethodName[1]; + private static TestResult FromPassedTest(TestPassedInfo info, MethodDeclarationSyntax testMethod) => + new() + { + Name = info.Name(), + Status = TestStatus.Pass, + Output = info.Output(), + TaskId = testMethod.TaskId(), + TestCode = testMethod.TestCode() + }; - return methodDeclarations.Single(method => - method.Identifier.Text == methodName && + private static MethodDeclarationSyntax TestMethod(this TestInfo testInfo, IEnumerable methodDeclarations) => + methodDeclarations.Single(method => + method.Identifier.Text == testInfo.MethodName && method.Parent is ClassDeclarationSyntax classDeclaration && - classDeclaration.Identifier.Text == className); - } - - private static string Name(this XmlUnitTestResult xmlUnitTestResult) => - xmlUnitTestResult.TestName - .Substring(xmlUnitTestResult.TestName.LastIndexOf(".", StringComparison.Ordinal) + 1) - .Humanize(); + classDeclaration.Identifier.Text == testInfo.TypeName); - private static TestStatus Status(this XmlUnitTestResult xmlUnitTestResult) => - xmlUnitTestResult.Outcome switch - { - "Passed" => TestStatus.Pass, - "Failed" => TestStatus.Fail, - _ => TestStatus.Error - }; + private static string Name(this TestInfo testInfo) => + testInfo.MethodName.Humanize(); - private static string Message(this XmlUnitTestResult xmlUnitTestResult) => - xmlUnitTestResult.Output?.ErrorInfo?.Message?.UseUnixNewlines()?.Trim(); + private static string Message(this TestFailedInfo testInfo) => + testInfo.ExceptionMessage.UseUnixNewlines()?.Trim(); - private static string Output(this XmlUnitTestResult xmlUnitTestResult) => - xmlUnitTestResult.Output?.StdOut?.UseUnixNewlines()?.Trim(); + private static string Output(this TestExecutedInfo testInfo) => + testInfo.Output.UseUnixNewlines().Trim().NullIfEmpty(); private static string TestCode(this MethodDeclarationSyntax testMethod) { @@ -108,50 +97,4 @@ private static string TestCode(this MethodDeclarationSyntax testMethod) .Select(taskNumberExpression => (int?)taskNumberExpression.Token.Value!) .FirstOrDefault(); } - - [XmlRoot(ElementName = "Output", Namespace = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010")] - public class XmlOutput - { - [XmlElement(ElementName = "StdOut", Namespace = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010")] - public string StdOut { get; set; } - - [XmlElement(ElementName = "ErrorInfo", - Namespace = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010")] - public XmlErrorInfo ErrorInfo { get; set; } - } - - [XmlRoot(ElementName = "UnitTestResult", Namespace = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010")] - public class XmlUnitTestResult - { - [XmlElement(ElementName = "Output", Namespace = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010")] - public XmlOutput Output { get; set; } - - [XmlAttribute(AttributeName = "testName")] - public string TestName { get; set; } - - [XmlAttribute(AttributeName = "outcome")] - public string Outcome { get; set; } - } - - [XmlRoot(ElementName = "ErrorInfo", Namespace = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010")] - public class XmlErrorInfo - { - [XmlElement(ElementName = "Message", Namespace = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010")] - public string Message { get; set; } - } - - [XmlRoot(ElementName = "Results", Namespace = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010")] - public class XmlResults - { - [XmlElement(ElementName = "UnitTestResult", - Namespace = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010")] - public List UnitTestResult { get; set; } - } - - [XmlRoot(ElementName = "TestRun", Namespace = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010")] - public class XmlTestRun - { - [XmlElement(ElementName = "Results", Namespace = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010")] - public XmlResults Results { get; set; } - } } \ No newline at end of file diff --git a/src/Exercism.TestRunner.CSharp/TestRun.cs b/src/Exercism.TestRunner.CSharp/TestRun.cs index 3a6710a..4ea1a65 100644 --- a/src/Exercism.TestRunner.CSharp/TestRun.cs +++ b/src/Exercism.TestRunner.CSharp/TestRun.cs @@ -16,7 +16,7 @@ internal class TestResult [JsonPropertyName("status")] public TestStatus Status { get; set; } - + [JsonPropertyName("task_id")] public int? TaskId { get; set; } @@ -34,7 +34,7 @@ internal class TestRun { [JsonPropertyName("version")] public int Version { get; set; } = 3; - + [JsonPropertyName("status")] public TestStatus Status { get; set; } diff --git a/src/Exercism.TestRunner.CSharp/TestRunParser.cs b/src/Exercism.TestRunner.CSharp/TestRunParser.cs index 3abdc26..1c9b606 100644 --- a/src/Exercism.TestRunner.CSharp/TestRunParser.cs +++ b/src/Exercism.TestRunner.CSharp/TestRunParser.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; @@ -9,29 +8,12 @@ namespace Exercism.TestRunner.CSharp { internal static class TestRunParser { - public static TestRun Parse(Options options, SyntaxTree testsSyntaxTree) - { - var logLines = File.ReadLines(options.BuildLogFilePath); - var buildFailed = logLines.Any(); - - if (buildFailed) - { - return TestRunWithError(logLines); - } - - return TestRunWithoutError(options, testsSyntaxTree); - } - - private static TestRun TestRunWithoutError(Options options, SyntaxTree testsSyntaxTree) - { - var testResults = TestResultParser.FromFile(options.TestResultsFilePath, testsSyntaxTree); - - return new TestRun + public static TestRun TestRunWithoutError(TestResult[] testResults) => + new() { Status = testResults.ToTestStatus(), Tests = testResults }; - } private static TestStatus ToTestStatus(this TestResult[] tests) { @@ -44,31 +26,19 @@ private static TestStatus ToTestStatus(this TestResult[] tests) return TestStatus.Error; } - private static TestRun TestRunWithError(IEnumerable logLines) => - new TestRun + public static TestRun TestRunWithError(Diagnostic[] logLines) => + new() { Message = string.Join("\n", logLines.Select(NormalizeLogLine)), Status = TestStatus.Error, Tests = Array.Empty() }; - private static string NormalizeLogLine(this string logLine) => - logLine.RemoveProjectReference().RemovePath().UseUnixNewlines().Trim(); + private static string NormalizeLogLine(this Diagnostic diagnostic) => + diagnostic.ToString().RemovePath(diagnostic).UseUnixNewlines().Trim(); - private static string RemoveProjectReference(this string logLine) => - logLine[..(logLine.LastIndexOf('[') - 1)]; - - private static string RemovePath(this string logLine) - { - var testFileIndex = logLine.IndexOf(".cs(", StringComparison.Ordinal); - if (testFileIndex == -1) - return logLine; - - var lastDirectorySeparatorIndex = logLine.LastIndexOf(Path.DirectorySeparatorChar, testFileIndex); - if (lastDirectorySeparatorIndex == -1) - return logLine; - - return logLine.Substring(lastDirectorySeparatorIndex + 1); - } + private static string RemovePath(this string logLine, Diagnostic diagnostic) => + logLine.Replace(diagnostic.Location.SourceTree.FilePath, + Path.GetFileName(diagnostic.Location.SourceTree.FilePath)); } } \ No newline at end of file diff --git a/src/Exercism.TestRunner.CSharp/TestRunner.cs b/src/Exercism.TestRunner.CSharp/TestRunner.cs new file mode 100644 index 0000000..004e58b --- /dev/null +++ b/src/Exercism.TestRunner.CSharp/TestRunner.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading; + +using Microsoft.CodeAnalysis; + +using Xunit.Runners; + +namespace Exercism.TestRunner.CSharp +{ + internal static class TestRunner + { + public static TestResult[] RunTests(Compilation compilation, Options options) + { + var outputPath = Path.Combine(Path.GetTempPath(), compilation.SourceModule.Name); + compilation.Emit(outputPath); + Assembly.LoadFrom(outputPath); + + var tests = new ConcurrentStack(); + var finished = new ManualResetEventSlim(); + var runner = AssemblyRunner.WithoutAppDomain(outputPath); + runner.OnTestFailed += info => tests.Push(info); + runner.OnTestPassed += info => tests.Push(info); + runner.OnExecutionComplete += _ => finished.Set(); + + runner.Start(); + finished.Wait(); + + var testsSyntaxTree = compilation.SyntaxTrees.First(tree => tree.FilePath == options.TestsFilePath); + return TestResultParser.FromTests(tests, testsSyntaxTree); + } + } +} \ No newline at end of file diff --git a/src/Exercism.TestRunner.CSharp/TestSuite.cs b/src/Exercism.TestRunner.CSharp/TestSuite.cs index bc9850d..b547e59 100644 --- a/src/Exercism.TestRunner.CSharp/TestSuite.cs +++ b/src/Exercism.TestRunner.CSharp/TestSuite.cs @@ -1,54 +1,24 @@ -using System.Diagnostics; -using System.IO; +using System.Linq; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; namespace Exercism.TestRunner.CSharp { - internal class TestSuite + internal static class TestSuite { - private readonly SyntaxTree _originalSyntaxTree; - private readonly Options _options; - - private TestSuite(SyntaxTree originalSyntaxTree, Options options) - { - _originalSyntaxTree = originalSyntaxTree; - _options = options; - } - - public TestRun Run() + public static TestRun RunTests(Options options) { - Rewrite(); - RunDotnetTest(); - UndoRewrite(); + var compilation = TestCompilation.Compile(options); - return TestRunParser.Parse(_options, _originalSyntaxTree); - } + var errors = compilation.GetDiagnostics() + .Where(diag => diag.Severity == DiagnosticSeverity.Error) + .ToArray(); - private void RunDotnetTest() - { - var command = "dotnet"; - var arguments = $"test --verbosity=quiet --logger \"trx;LogFileName={Path.GetFileName(_options.TestResultsFilePath)}\" /flp:v=q"; + if (errors.Any()) + return TestRunParser.TestRunWithError(errors); - var processStartInfo = new ProcessStartInfo(command, arguments) - { - WorkingDirectory = Path.GetDirectoryName(_options.TestsFilePath)!, - RedirectStandardInput = true, - RedirectStandardError = true, - RedirectStandardOutput = true - }; - Process.Start(processStartInfo)?.WaitForExit(); - } - - private void Rewrite() => File.WriteAllText(_options.TestsFilePath, _originalSyntaxTree.Rewrite().ToString()); - - private void UndoRewrite() => File.WriteAllText(_options.TestsFilePath, _originalSyntaxTree.ToString()); - - public static TestSuite FromOptions(Options options) - { - var originalSyntaxTree = CSharpSyntaxTree.ParseText(File.ReadAllText(options.TestsFilePath)); - return new TestSuite(originalSyntaxTree, options); + var testResults = TestRunner.RunTests(compilation, options); + return TestRunParser.TestRunWithoutError(testResults); } } } \ No newline at end of file diff --git a/src/Exercism.TestRunner.CSharp/TestsRewriter.cs b/src/Exercism.TestRunner.CSharp/TestsRewriter.cs index 263870a..8a9f0b7 100644 --- a/src/Exercism.TestRunner.CSharp/TestsRewriter.cs +++ b/src/Exercism.TestRunner.CSharp/TestsRewriter.cs @@ -12,8 +12,7 @@ public static SyntaxTree Rewrite(this SyntaxTree tree) => tree.WithRootAndOptions(tree.GetRoot().Rewrite(), tree.Options); private static SyntaxNode Rewrite(this SyntaxNode node) => - node - .UnskipTests() + node.UnskipTests() .CaptureConsoleOutput(); private static SyntaxNode UnskipTests(this SyntaxNode testsRoot) => @@ -130,34 +129,7 @@ public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node) => ArgumentList( SingletonSeparatedList( Argument( - IdentifierName("_stringWriter")))))), - ExpressionStatement( - InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("System"), - IdentifierName("Diagnostics")), - IdentifierName("Trace")), - IdentifierName("Listeners")), - IdentifierName("Add"))) - .WithArgumentList( - ArgumentList( - SingletonSeparatedList( - Argument( - ObjectCreationExpression( - QualifiedName( - QualifiedName( - IdentifierName("System"), - IdentifierName("Diagnostics")), - IdentifierName("ConsoleTraceListener"))) - .WithArgumentList( - ArgumentList())))))))), + IdentifierName("_stringWriter")))))))), MethodDeclaration( PredefinedType( Token(SyntaxKind.VoidKeyword)), diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Exercism.TestRunner.CSharp.IntegrationTests.csproj b/test/Exercism.TestRunner.CSharp.IntegrationTests/Exercism.TestRunner.CSharp.IntegrationTests.csproj index d416643..dc94ae7 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Exercism.TestRunner.CSharp.IntegrationTests.csproj +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Exercism.TestRunner.CSharp.IntegrationTests.csproj @@ -6,7 +6,7 @@ - + diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/NormalizationExtensions.cs b/test/Exercism.TestRunner.CSharp.IntegrationTests/NormalizationExtensions.cs index 6670240..865222a 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/NormalizationExtensions.cs +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/NormalizationExtensions.cs @@ -12,6 +12,6 @@ private static string NormalizeNewlines(this string json) => json.Replace("\r\n", "\n"); private static JsonSerializerSettings CreateJsonSerializerSettings() => - new JsonSerializerSettings { ContractResolver = new DefaultContractResolver { NamingStrategy = new SnakeCaseNamingStrategy() } }; + new() { ContractResolver = new DefaultContractResolver { NamingStrategy = new SnakeCaseNamingStrategy() } }; } } \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/AllTestsWithTask/Fake.csproj b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/AllTestsWithTask/Fake.csproj index 5e8fe89..d6a2c1b 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/AllTestsWithTask/Fake.csproj +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/AllTestsWithTask/Fake.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleCompileErrors/Fake.csproj b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleCompileErrors/Fake.csproj index 5e8fe89..d6a2c1b 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleCompileErrors/Fake.csproj +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleCompileErrors/Fake.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestClassesWithAllPasses/Fake.csproj b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestClassesWithAllPasses/Fake.csproj index 5e8fe89..d6a2c1b 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestClassesWithAllPasses/Fake.csproj +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestClassesWithAllPasses/Fake.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithAllPasses/Fake.csproj b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithAllPasses/Fake.csproj index 5e8fe89..d6a2c1b 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithAllPasses/Fake.csproj +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithAllPasses/Fake.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithMultipleFails/Fake.csproj b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithMultipleFails/Fake.csproj index 5e8fe89..d6a2c1b 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithMultipleFails/Fake.csproj +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithMultipleFails/Fake.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithSingleFail/Fake.csproj b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithSingleFail/Fake.csproj index 5e8fe89..d6a2c1b 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithSingleFail/Fake.csproj +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithSingleFail/Fake.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutput/Fake.cs b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutput/Fake.cs index 089f84e..d5d8795 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutput/Fake.cs +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutput/Fake.cs @@ -5,10 +5,10 @@ public static class Fake { public static int Add(int x, int y) { - System.Diagnostics.Trace.Write("String "); + Console.Out.Write("String "); Console.Write("without "); Console.Error.Write("params "); - System.Diagnostics.Debug.Write("output"); + Console.Write("output"); return x + y; } @@ -19,9 +19,9 @@ public static int Sub(int x, int y) public static int Mul(int x, int y) { - System.Diagnostics.Trace.WriteLine("String with params output"); + Console.WriteLine("String with params output"); System.Console.WriteLine("Values used:"); - Debug.WriteLine("{0}, {1}", 2, true); + Console.WriteLine("{0}, {1}", 2, true); System.Console.Out.WriteLine("-----"); return x * y; } diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutput/Fake.csproj b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutput/Fake.csproj index 5e8fe89..d6a2c1b 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutput/Fake.csproj +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutput/Fake.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutputExceedingLimit/Fake.cs b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutputExceedingLimit/Fake.cs index da1131a..00801a8 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutputExceedingLimit/Fake.cs +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutputExceedingLimit/Fake.cs @@ -5,7 +5,7 @@ public static class Fake { public static int Add(int x, int y) { - Trace.WriteLine(new string('a', 498) + 'b' + 'c' + 'd'); + Console.WriteLine(new string('a', 498) + 'b' + 'c' + 'd'); return x + y; } @@ -13,7 +13,7 @@ public static int Add(int x, int y) public static int Mul(int x, int y) { - Debug.WriteLine("Maximum not exceeded"); + Console.WriteLine("Maximum not exceeded"); return x * y; } } \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutputExceedingLimit/Fake.csproj b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutputExceedingLimit/Fake.csproj index 5e8fe89..d6a2c1b 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutputExceedingLimit/Fake.csproj +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutputExceedingLimit/Fake.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NoTasks/Fake.csproj b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NoTasks/Fake.csproj index 5e8fe89..d6a2c1b 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NoTasks/Fake.csproj +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NoTasks/Fake.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NoTests/Fake.csproj b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NoTests/Fake.csproj index 5e8fe89..d6a2c1b 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NoTests/Fake.csproj +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NoTests/Fake.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NotImplemented/Fake.csproj b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NotImplemented/Fake.csproj index 5e8fe89..d6a2c1b 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NotImplemented/Fake.csproj +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NotImplemented/Fake.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NotImplemented/expected_results.json b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NotImplemented/expected_results.json index 351211a..edce11c 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NotImplemented/expected_results.json +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NotImplemented/expected_results.json @@ -5,7 +5,7 @@ { "name": "Add should add numbers", "status": "fail", - "message": "System.NotImplementedException : Please implement the Fake.Add method", + "message": "Please implement the Fake.Add method", "test_code": "Assert.Equal(3, Fake.Add(1, 1))" } ] diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleCompileError/Fake.csproj b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleCompileError/Fake.csproj index 5e8fe89..d6a2c1b 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleCompileError/Fake.csproj +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleCompileError/Fake.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatFails/Fake.csproj b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatFails/Fake.csproj index 5e8fe89..d6a2c1b 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatFails/Fake.csproj +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatFails/Fake.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatPasses/Fake.csproj b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatPasses/Fake.csproj index 5e8fe89..d6a2c1b 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatPasses/Fake.csproj +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatPasses/Fake.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatPassesWithDifferentSlug/Foo.csproj b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatPassesWithDifferentSlug/Foo.csproj index 5e8fe89..d6a2c1b 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatPassesWithDifferentSlug/Foo.csproj +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatPassesWithDifferentSlug/Foo.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SomeTestsWithTask/Fake.csproj b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SomeTestsWithTask/Fake.csproj index 5e8fe89..d6a2c1b 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SomeTestsWithTask/Fake.csproj +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SomeTestsWithTask/Fake.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/TestsInDifferentFormats/Fake.csproj b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/TestsInDifferentFormats/Fake.csproj index 5e8fe89..d6a2c1b 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/TestsInDifferentFormats/Fake.csproj +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/TestsInDifferentFormats/Fake.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/TestRunResultReader.cs b/test/Exercism.TestRunner.CSharp.IntegrationTests/TestRunResultReader.cs index d9d4db7..be8d0f5 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/TestRunResultReader.cs +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/TestRunResultReader.cs @@ -55,7 +55,7 @@ private struct TestRunResult { [JsonPropertyName("version")] public int Version { get; set; } - + [JsonPropertyName("status")] public TestStatus Status { get; set; } @@ -76,7 +76,7 @@ private struct TestResult [JsonPropertyName("name")] public string Name { get; set; } - + [JsonPropertyName("task_id")] public int? TaskId { get; set; } diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/TestSolutionRunner.cs b/test/Exercism.TestRunner.CSharp.IntegrationTests/TestSolutionRunner.cs index c6f8486..1d2fdf4 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/TestSolutionRunner.cs +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/TestSolutionRunner.cs @@ -21,7 +21,20 @@ private static void RunTestRunner(TestSolution testSolution) } private static void RunTestRunnerUsingDocker(TestSolution testSolution) => - Process.Start("docker", $"run -v {testSolution.DirectoryFullPath}:/solution -v {testSolution.DirectoryFullPath}:/results exercism/csharp-test-runner {testSolution.Slug} /solution /results")!.WaitForExit(); + Process.Start("docker", + new[] + { + "run", + "--network", "none", + "--read-only", + "--mount", $"type=bind,src={testSolution.DirectoryFullPath},dst=/solution", + "--mount", $"type=bind,src={testSolution.DirectoryFullPath},dst=/output", + "--mount", "type=tmpfs,dst=/tmp", + "exercism/csharp-test-runner", + testSolution.Slug, + "/solution", + "/output" + })!.WaitForExit(); private static void RunTestRunnerWithoutDocker(TestSolution testSolution) => Program.Main(new[] { testSolution.Slug, testSolution.DirectoryFullPath, testSolution.DirectoryFullPath });