diff --git a/src/Exercism.TestRunner.CSharp/FilesParser.cs b/src/Exercism.TestRunner.CSharp/FilesParser.cs new file mode 100644 index 0000000..4d18b1d --- /dev/null +++ b/src/Exercism.TestRunner.CSharp/FilesParser.cs @@ -0,0 +1,36 @@ +using System.IO; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Exercism.TestRunner.CSharp +{ + internal static class FilesParser + { + public static Files Parse(Options options) + { + var configuration = JsonSerializer.Deserialize(ConfigJson(options)); + return configuration!.Files; + } + + private static string ConfigJson(Options options) => + File.ReadAllText(options.ConfigJsonPath()); + + private static string ConfigJsonPath(this Options options) => + Path.Combine(options.InputDirectory, ".meta", "config.json"); + } + + internal class Files + { + [JsonPropertyName("solution")] + public string[] Solution { get; set; } + + [JsonPropertyName("test")] + public string[] Test { get; set; } + } + + internal class Configuration + { + [JsonPropertyName("files")] + public Files Files { get; set; } + } +} \ No newline at end of file diff --git a/src/Exercism.TestRunner.CSharp/Options.cs b/src/Exercism.TestRunner.CSharp/Options.cs index e1a1e49..d67931c 100644 --- a/src/Exercism.TestRunner.CSharp/Options.cs +++ b/src/Exercism.TestRunner.CSharp/Options.cs @@ -1,9 +1,5 @@ -using System.IO; - using CommandLine; -using Humanizer; - namespace Exercism.TestRunner.CSharp { internal class Options @@ -19,11 +15,5 @@ internal class Options public Options(string slug, string inputDirectory, string outputDirectory) => (Slug, InputDirectory, OutputDirectory) = (slug, inputDirectory, outputDirectory); - - public string TestsFilePath => Path.Combine(InputDirectory, $"{Exercise}Tests.cs"); - - public string ResultsJsonFilePath => Path.GetFullPath(Path.Combine(OutputDirectory, "results.json")); - - private string Exercise => Slug.Dehumanize().Pascalize(); } } \ No newline at end of file diff --git a/src/Exercism.TestRunner.CSharp/Program.cs b/src/Exercism.TestRunner.CSharp/Program.cs index d08893e..a9094ed 100644 --- a/src/Exercism.TestRunner.CSharp/Program.cs +++ b/src/Exercism.TestRunner.CSharp/Program.cs @@ -16,7 +16,7 @@ private static void CreateTestResults(Options options) Console.WriteLine($"[{DateTimeOffset.UtcNow:u}] Running test runner for '{options.Slug}' solution..."); var testRun = TestSuite.RunTests(options); - testRun.WriteToFile(options.ResultsJsonFilePath); + testRun.WriteToFile(options); Console.WriteLine($"[{DateTimeOffset.UtcNow:u}] Ran test runner for '{options.Slug}' solution"); } diff --git a/src/Exercism.TestRunner.CSharp/TestCompilation.cs b/src/Exercism.TestRunner.CSharp/TestCompilation.cs index 48bd5cd..703ae2c 100644 --- a/src/Exercism.TestRunner.CSharp/TestCompilation.cs +++ b/src/Exercism.TestRunner.CSharp/TestCompilation.cs @@ -13,27 +13,24 @@ namespace Exercism.TestRunner.CSharp { internal static class TestCompilation { - public static Compilation Compile(Options options) => - CSharpCompilation.Create(Guid.NewGuid().ToString("N"), SyntaxTrees(options), References(), CompilationOptions()); + public static Compilation Compile(Options options, Files files) => + CSharpCompilation.Create(AssemblyName(), SyntaxTrees(options, files), References(), CompilationOptions()); - private static IEnumerable SyntaxTrees(Options options) + private static string AssemblyName() => Guid.NewGuid().ToString("N"); + + private static IEnumerable SyntaxTrees(Options options, Files files) + { + var solutionFiles = files.Solution.Select(file => ParseSyntaxTree(file, options)); + var testFiles = files.Test.Select(file => ParseSyntaxTree(file, options).Rewrite()); + + return solutionFiles.Concat(testFiles); + } + + private static SyntaxTree ParseSyntaxTree(string file, 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); + var filePath = Path.Combine(options.InputDirectory, file); + var source = SourceText.From(File.OpenRead(filePath)); + return CSharpSyntaxTree.ParseText(source, path: file); } private static CSharpCompilationOptions CompilationOptions() => diff --git a/src/Exercism.TestRunner.CSharp/TestResultParser.cs b/src/Exercism.TestRunner.CSharp/TestResultParser.cs index eaa3f5b..56a4f1f 100644 --- a/src/Exercism.TestRunner.CSharp/TestResultParser.cs +++ b/src/Exercism.TestRunner.CSharp/TestResultParser.cs @@ -14,12 +14,12 @@ namespace Exercism.TestRunner.CSharp { internal static class TestResultParser { - public static TestResult[] FromTests(IEnumerable tests, SyntaxTree testsSyntaxTree) + public static TestResult[] FromTests(IEnumerable tests, Compilation compilation, Files files) { var testMethods = - testsSyntaxTree - .GetRoot() - .DescendantNodes() + compilation.SyntaxTrees + .Where(tree => files.Test.Contains(tree.FilePath)) + .SelectMany(tree => tree.GetRoot().DescendantNodes()) .OfType() .ToArray(); diff --git a/src/Exercism.TestRunner.CSharp/TestRunWriter.cs b/src/Exercism.TestRunner.CSharp/TestRunWriter.cs index 4b56b0e..668c906 100644 --- a/src/Exercism.TestRunner.CSharp/TestRunWriter.cs +++ b/src/Exercism.TestRunner.CSharp/TestRunWriter.cs @@ -6,8 +6,11 @@ namespace Exercism.TestRunner.CSharp { internal static class TestRunWriter { - public static void WriteToFile(this TestRun testRun, string resultsJsonFilePath) => - File.WriteAllText(resultsJsonFilePath, testRun.ToJson()); + public static void WriteToFile(this TestRun testRun, Options options) => + File.WriteAllText(options.ResultsJsonPath(), testRun.ToJson()); + + private static string ResultsJsonPath(this Options options) => + Path.GetFullPath(Path.Combine(options.OutputDirectory, "results.json")); private static string ToJson(this TestRun testRun) => JsonSerializer.Serialize(testRun, CreateJsonSerializerOptions()); diff --git a/src/Exercism.TestRunner.CSharp/TestRunner.cs b/src/Exercism.TestRunner.CSharp/TestRunner.cs index 004e58b..5bc24d2 100644 --- a/src/Exercism.TestRunner.CSharp/TestRunner.cs +++ b/src/Exercism.TestRunner.CSharp/TestRunner.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; +using System.Collections.Concurrent; using System.IO; -using System.Linq; using System.Reflection; using System.Threading; @@ -14,7 +11,7 @@ namespace Exercism.TestRunner.CSharp { internal static class TestRunner { - public static TestResult[] RunTests(Compilation compilation, Options options) + public static TestResult[] RunTests(Compilation compilation, Files files) { var outputPath = Path.Combine(Path.GetTempPath(), compilation.SourceModule.Name); compilation.Emit(outputPath); @@ -30,8 +27,7 @@ public static TestResult[] RunTests(Compilation compilation, Options options) runner.Start(); finished.Wait(); - var testsSyntaxTree = compilation.SyntaxTrees.First(tree => tree.FilePath == options.TestsFilePath); - return TestResultParser.FromTests(tests, testsSyntaxTree); + return TestResultParser.FromTests(tests, compilation, files); } } } \ No newline at end of file diff --git a/src/Exercism.TestRunner.CSharp/TestSuite.cs b/src/Exercism.TestRunner.CSharp/TestSuite.cs index b547e59..6f06dec 100644 --- a/src/Exercism.TestRunner.CSharp/TestSuite.cs +++ b/src/Exercism.TestRunner.CSharp/TestSuite.cs @@ -8,7 +8,8 @@ internal static class TestSuite { public static TestRun RunTests(Options options) { - var compilation = TestCompilation.Compile(options); + var files = FilesParser.Parse(options); + var compilation = TestCompilation.Compile(options, files); var errors = compilation.GetDiagnostics() .Where(diag => diag.Severity == DiagnosticSeverity.Error) @@ -17,7 +18,7 @@ public static TestRun RunTests(Options options) if (errors.Any()) return TestRunParser.TestRunWithError(errors); - var testResults = TestRunner.RunTests(compilation, options); + var testResults = TestRunner.RunTests(compilation, files); return TestRunParser.TestRunWithoutError(testResults); } } diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/AllTestsWithTask/.meta/config.json b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/AllTestsWithTask/.meta/config.json new file mode 100644 index 0000000..b6395cc --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/AllTestsWithTask/.meta/config.json @@ -0,0 +1,7 @@ +{ + "files": { + "solution": ["Fake.cs"], + "test": ["FakeTests.cs"], + "example": [".meta/Example.cs"] + } +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleCompileErrors/.meta/config.json b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleCompileErrors/.meta/config.json new file mode 100644 index 0000000..b6395cc --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleCompileErrors/.meta/config.json @@ -0,0 +1,7 @@ +{ + "files": { + "solution": ["Fake.cs"], + "test": ["FakeTests.cs"], + "example": [".meta/Example.cs"] + } +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestClassesWithAllPasses/.meta/config.json b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestClassesWithAllPasses/.meta/config.json new file mode 100644 index 0000000..b6395cc --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestClassesWithAllPasses/.meta/config.json @@ -0,0 +1,7 @@ +{ + "files": { + "solution": ["Fake.cs"], + "test": ["FakeTests.cs"], + "example": [".meta/Example.cs"] + } +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithAllPasses/.meta/config.json b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithAllPasses/.meta/config.json new file mode 100644 index 0000000..b6395cc --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithAllPasses/.meta/config.json @@ -0,0 +1,7 @@ +{ + "files": { + "solution": ["Fake.cs"], + "test": ["FakeTests.cs"], + "example": [".meta/Example.cs"] + } +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithMultipleFails/.meta/config.json b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithMultipleFails/.meta/config.json new file mode 100644 index 0000000..b6395cc --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithMultipleFails/.meta/config.json @@ -0,0 +1,7 @@ +{ + "files": { + "solution": ["Fake.cs"], + "test": ["FakeTests.cs"], + "example": [".meta/Example.cs"] + } +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithSingleFail/.meta/config.json b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithSingleFail/.meta/config.json new file mode 100644 index 0000000..b6395cc --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithSingleFail/.meta/config.json @@ -0,0 +1,7 @@ +{ + "files": { + "solution": ["Fake.cs"], + "test": ["FakeTests.cs"], + "example": [".meta/Example.cs"] + } +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutput/.meta/config.json b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutput/.meta/config.json new file mode 100644 index 0000000..b6395cc --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutput/.meta/config.json @@ -0,0 +1,7 @@ +{ + "files": { + "solution": ["Fake.cs"], + "test": ["FakeTests.cs"], + "example": [".meta/Example.cs"] + } +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutputExceedingLimit/.meta/config.json b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutputExceedingLimit/.meta/config.json new file mode 100644 index 0000000..b6395cc --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/MultipleTestsWithTestOutputExceedingLimit/.meta/config.json @@ -0,0 +1,7 @@ +{ + "files": { + "solution": ["Fake.cs"], + "test": ["FakeTests.cs"], + "example": [".meta/Example.cs"] + } +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NoTasks/.meta/config.json b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NoTasks/.meta/config.json new file mode 100644 index 0000000..b6395cc --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NoTasks/.meta/config.json @@ -0,0 +1,7 @@ +{ + "files": { + "solution": ["Fake.cs"], + "test": ["FakeTests.cs"], + "example": [".meta/Example.cs"] + } +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NoTests/.meta/config.json b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NoTests/.meta/config.json new file mode 100644 index 0000000..b6395cc --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NoTests/.meta/config.json @@ -0,0 +1,7 @@ +{ + "files": { + "solution": ["Fake.cs"], + "test": ["FakeTests.cs"], + "example": [".meta/Example.cs"] + } +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NotImplemented/.meta/config.json b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NotImplemented/.meta/config.json new file mode 100644 index 0000000..b6395cc --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/NotImplemented/.meta/config.json @@ -0,0 +1,7 @@ +{ + "files": { + "solution": ["Fake.cs"], + "test": ["FakeTests.cs"], + "example": [".meta/Example.cs"] + } +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleCompileError/.meta/config.json b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleCompileError/.meta/config.json new file mode 100644 index 0000000..b6395cc --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleCompileError/.meta/config.json @@ -0,0 +1,7 @@ +{ + "files": { + "solution": ["Fake.cs"], + "test": ["FakeTests.cs"], + "example": [".meta/Example.cs"] + } +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatFails/.meta/config.json b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatFails/.meta/config.json new file mode 100644 index 0000000..b6395cc --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatFails/.meta/config.json @@ -0,0 +1,7 @@ +{ + "files": { + "solution": ["Fake.cs"], + "test": ["FakeTests.cs"], + "example": [".meta/Example.cs"] + } +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatPasses/.meta/config.json b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatPasses/.meta/config.json new file mode 100644 index 0000000..b6395cc --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatPasses/.meta/config.json @@ -0,0 +1,7 @@ +{ + "files": { + "solution": ["Fake.cs"], + "test": ["FakeTests.cs"], + "example": [".meta/Example.cs"] + } +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatPassesWithDifferentSlug/.meta/config.json b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatPassesWithDifferentSlug/.meta/config.json new file mode 100644 index 0000000..8b49575 --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SingleTestThatPassesWithDifferentSlug/.meta/config.json @@ -0,0 +1,7 @@ +{ + "files": { + "solution": ["Foo.cs"], + "test": ["FooTests.cs"], + "example": [".meta/Example.cs"] + } +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SomeTestsWithTask/.meta/config.json b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SomeTestsWithTask/.meta/config.json new file mode 100644 index 0000000..b6395cc --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/SomeTestsWithTask/.meta/config.json @@ -0,0 +1,7 @@ +{ + "files": { + "solution": ["Fake.cs"], + "test": ["FakeTests.cs"], + "example": [".meta/Example.cs"] + } +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/TestsInDifferentFormats/.meta/config.json b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/TestsInDifferentFormats/.meta/config.json new file mode 100644 index 0000000..b6395cc --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/TestsInDifferentFormats/.meta/config.json @@ -0,0 +1,7 @@ +{ + "files": { + "solution": ["Fake.cs"], + "test": ["FakeTests.cs"], + "example": [".meta/Example.cs"] + } +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/WithExample/.meta/Example.cs b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/WithExample/.meta/Example.cs new file mode 100644 index 0000000..bfbf44e --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/WithExample/.meta/Example.cs @@ -0,0 +1,4 @@ +public static class Fake +{ + public static int Add(int x, int y) => x + y; +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/WithExample/.meta/config.json b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/WithExample/.meta/config.json new file mode 100644 index 0000000..b6395cc --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/WithExample/.meta/config.json @@ -0,0 +1,7 @@ +{ + "files": { + "solution": ["Fake.cs"], + "test": ["FakeTests.cs"], + "example": [".meta/Example.cs"] + } +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/WithExample/Fake.cs b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/WithExample/Fake.cs new file mode 100644 index 0000000..bfbf44e --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/WithExample/Fake.cs @@ -0,0 +1,4 @@ +public static class Fake +{ + public static int Add(int x, int y) => x + y; +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/WithExample/Fake.csproj b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/WithExample/Fake.csproj new file mode 100644 index 0000000..d6a2c1b --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/WithExample/Fake.csproj @@ -0,0 +1,18 @@ + + + + net5.0 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/WithExample/FakeTests.cs b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/WithExample/FakeTests.cs new file mode 100644 index 0000000..fecec13 --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/WithExample/FakeTests.cs @@ -0,0 +1,6 @@ +using Xunit; +public class FakeTests +{ + [Fact] + public void Add_should_add_numbers() => Assert.Equal(2, Fake.Add(1, 1)); +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/WithExample/expected_results.json b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/WithExample/expected_results.json new file mode 100644 index 0000000..56365e0 --- /dev/null +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/Solutions/WithExample/expected_results.json @@ -0,0 +1,11 @@ +{ + "version": 3, + "status": "pass", + "tests": [ + { + "name": "Add should add numbers", + "status": "pass", + "test_code": "Assert.Equal(2, Fake.Add(1, 1))" + } + ] +} \ No newline at end of file diff --git a/test/Exercism.TestRunner.CSharp.IntegrationTests/TestRunnerTests.cs b/test/Exercism.TestRunner.CSharp.IntegrationTests/TestRunnerTests.cs index df59a7b..d65ea2a 100644 --- a/test/Exercism.TestRunner.CSharp.IntegrationTests/TestRunnerTests.cs +++ b/test/Exercism.TestRunner.CSharp.IntegrationTests/TestRunnerTests.cs @@ -122,5 +122,12 @@ public void SomeTestsWithTask() var testRun = TestSolutionRunner.Run("SomeTestsWithTask"); Assert.Equal(testRun.Expected, testRun.Actual); } + + [Fact] + public void WithExample() + { + var testRun = TestSolutionRunner.Run("WithExample"); + Assert.Equal(testRun.Expected, testRun.Actual); + } } } \ No newline at end of file