diff --git a/exercises/food-chain/Example.cs b/exercises/food-chain/Example.cs index 3537693930..2a4717cd4f 100644 --- a/exercises/food-chain/Example.cs +++ b/exercises/food-chain/Example.cs @@ -36,10 +36,10 @@ public static class FoodChain "I don't know why she swallowed the fly. Perhaps she'll die." }; - public static string Song() => string.Join("\n\n", Enumerable.Range(1, Verses).Select(Verse)); - public static string Verse(int number) => $"{VerseBegin(number)}\n{VerseEnd(number)}"; + public static string Verse(int begin, int end) => string.Join("\n\n", Enumerable.Range(begin, end - begin + 1).Select(i => Verse(i))); + private static string VerseBegin(int number) { if (number == 1) diff --git a/exercises/food-chain/FoodChain.cs b/exercises/food-chain/FoodChain.cs index 5023fb38af..cf94b008f5 100644 --- a/exercises/food-chain/FoodChain.cs +++ b/exercises/food-chain/FoodChain.cs @@ -2,12 +2,12 @@ public static class FoodChain { - public static string Song() + public static string Verse(int number) { throw new NotImplementedException("You need to implement this function."); } - public static string Verse(int number) + public static string Verse(int begin, int end) { throw new NotImplementedException("You need to implement this function."); } diff --git a/exercises/food-chain/FoodChainTest.cs b/exercises/food-chain/FoodChainTest.cs index f625e58cf8..56541512fe 100644 --- a/exercises/food-chain/FoodChainTest.cs +++ b/exercises/food-chain/FoodChainTest.cs @@ -1,103 +1,180 @@ -using Xunit; +using Xunit; public class FoodChainTest { [Fact] - public void Verse_one() + public void Fly() { - const string expected = "I know an old lady who swallowed a fly.\n" + - "I don't know why she swallowed the fly. Perhaps she'll die."; - + var expected = + "I know an old lady who swallowed a fly.\n"+ + "I don't know why she swallowed the fly. Perhaps she'll die."; Assert.Equal(expected, FoodChain.Verse(1)); } [Fact(Skip = "Remove to run test")] - public void Verse_two() + public void Spider() { - const string expected = "I know an old lady who swallowed a spider.\n" + - "It wriggled and jiggled and tickled inside her.\n" + - "She swallowed the spider to catch the fly.\n" + - "I don't know why she swallowed the fly. Perhaps she'll die."; - + var expected = + "I know an old lady who swallowed a spider.\n"+ + "It wriggled and jiggled and tickled inside her.\n"+ + "She swallowed the spider to catch the fly.\n"+ + "I don't know why she swallowed the fly. Perhaps she'll die."; Assert.Equal(expected, FoodChain.Verse(2)); } [Fact(Skip = "Remove to run test")] - public void Verse_four() + public void Bird() { - const string expected = "I know an old lady who swallowed a cat.\n" + - "Imagine that, to swallow a cat!\n" + - "She swallowed the cat to catch the bird.\n" + - "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.\n" + - "She swallowed the spider to catch the fly.\n" + - "I don't know why she swallowed the fly. Perhaps she'll die."; + var expected = + "I know an old lady who swallowed a bird.\n"+ + "How absurd to swallow a bird!\n"+ + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.\n"+ + "She swallowed the spider to catch the fly.\n"+ + "I don't know why she swallowed the fly. Perhaps she'll die."; + Assert.Equal(expected, FoodChain.Verse(3)); + } + [Fact(Skip = "Remove to run test")] + public void Cat() + { + var expected = + "I know an old lady who swallowed a cat.\n"+ + "Imagine that, to swallow a cat!\n"+ + "She swallowed the cat to catch the bird.\n"+ + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.\n"+ + "She swallowed the spider to catch the fly.\n"+ + "I don't know why she swallowed the fly. Perhaps she'll die."; Assert.Equal(expected, FoodChain.Verse(4)); } [Fact(Skip = "Remove to run test")] - public void Verse_eight() + public void Dog() { - const string expected = "I know an old lady who swallowed a horse.\n" + - "She's dead, of course!"; + var expected = + "I know an old lady who swallowed a dog.\n"+ + "What a hog, to swallow a dog!\n"+ + "She swallowed the dog to catch the cat.\n"+ + "She swallowed the cat to catch the bird.\n"+ + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.\n"+ + "She swallowed the spider to catch the fly.\n"+ + "I don't know why she swallowed the fly. Perhaps she'll die."; + Assert.Equal(expected, FoodChain.Verse(5)); + } + + [Fact(Skip = "Remove to run test")] + public void Goat() + { + var expected = + "I know an old lady who swallowed a goat.\n"+ + "Just opened her throat and swallowed a goat!\n"+ + "She swallowed the goat to catch the dog.\n"+ + "She swallowed the dog to catch the cat.\n"+ + "She swallowed the cat to catch the bird.\n"+ + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.\n"+ + "She swallowed the spider to catch the fly.\n"+ + "I don't know why she swallowed the fly. Perhaps she'll die."; + Assert.Equal(expected, FoodChain.Verse(6)); + } + [Fact(Skip = "Remove to run test")] + public void Cow() + { + var expected = + "I know an old lady who swallowed a cow.\n"+ + "I don't know how she swallowed a cow!\n"+ + "She swallowed the cow to catch the goat.\n"+ + "She swallowed the goat to catch the dog.\n"+ + "She swallowed the dog to catch the cat.\n"+ + "She swallowed the cat to catch the bird.\n"+ + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.\n"+ + "She swallowed the spider to catch the fly.\n"+ + "I don't know why she swallowed the fly. Perhaps she'll die."; + Assert.Equal(expected, FoodChain.Verse(7)); + } + + [Fact(Skip = "Remove to run test")] + public void Horse() + { + var expected = + "I know an old lady who swallowed a horse.\n"+ + "She's dead, of course!"; Assert.Equal(expected, FoodChain.Verse(8)); } [Fact(Skip = "Remove to run test")] - public void Complete_song() + public void Multiple_verses() { - const string expected = "I know an old lady who swallowed a fly.\n" + - "I don't know why she swallowed the fly. Perhaps she'll die.\n" + - "\n" + - "I know an old lady who swallowed a spider.\n" + - "It wriggled and jiggled and tickled inside her.\n" + - "She swallowed the spider to catch the fly.\n" + - "I don't know why she swallowed the fly. Perhaps she'll die.\n" + - "\n" + - "I know an old lady who swallowed a bird.\n" + - "How absurd to swallow a bird!\n" + - "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.\n" + - "She swallowed the spider to catch the fly.\n" + - "I don't know why she swallowed the fly. Perhaps she'll die.\n" + - "\n" + - "I know an old lady who swallowed a cat.\n" + - "Imagine that, to swallow a cat!\n" + - "She swallowed the cat to catch the bird.\n" + - "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.\n" + - "She swallowed the spider to catch the fly.\n" + - "I don't know why she swallowed the fly. Perhaps she'll die.\n" + - "\n" + - "I know an old lady who swallowed a dog.\n" + - "What a hog, to swallow a dog!\n" + - "She swallowed the dog to catch the cat.\n" + - "She swallowed the cat to catch the bird.\n" + - "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.\n" + - "She swallowed the spider to catch the fly.\n" + - "I don't know why she swallowed the fly. Perhaps she'll die.\n" + - "\n" + - "I know an old lady who swallowed a goat.\n" + - "Just opened her throat and swallowed a goat!\n" + - "She swallowed the goat to catch the dog.\n" + - "She swallowed the dog to catch the cat.\n" + - "She swallowed the cat to catch the bird.\n" + - "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.\n" + - "She swallowed the spider to catch the fly.\n" + - "I don't know why she swallowed the fly. Perhaps she'll die.\n" + - "\n" + - "I know an old lady who swallowed a cow.\n" + - "I don't know how she swallowed a cow!\n" + - "She swallowed the cow to catch the goat.\n" + - "She swallowed the goat to catch the dog.\n" + - "She swallowed the dog to catch the cat.\n" + - "She swallowed the cat to catch the bird.\n" + - "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.\n" + - "She swallowed the spider to catch the fly.\n" + - "I don't know why she swallowed the fly. Perhaps she'll die.\n" + - "\n" + - "I know an old lady who swallowed a horse.\n" + - "She's dead, of course!"; + var expected = + "I know an old lady who swallowed a fly.\n"+ + "I don't know why she swallowed the fly. Perhaps she'll die.\n"+ + "\n"+ + "I know an old lady who swallowed a spider.\n"+ + "It wriggled and jiggled and tickled inside her.\n"+ + "She swallowed the spider to catch the fly.\n"+ + "I don't know why she swallowed the fly. Perhaps she'll die.\n"+ + "\n"+ + "I know an old lady who swallowed a bird.\n"+ + "How absurd to swallow a bird!\n"+ + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.\n"+ + "She swallowed the spider to catch the fly.\n"+ + "I don't know why she swallowed the fly. Perhaps she'll die."; + Assert.Equal(expected, FoodChain.Verse(1, 3)); + } - Assert.Equal(expected, FoodChain.Song()); + [Fact(Skip = "Remove to run test")] + public void Full_song() + { + var expected = + "I know an old lady who swallowed a fly.\n"+ + "I don't know why she swallowed the fly. Perhaps she'll die.\n"+ + "\n"+ + "I know an old lady who swallowed a spider.\n"+ + "It wriggled and jiggled and tickled inside her.\n"+ + "She swallowed the spider to catch the fly.\n"+ + "I don't know why she swallowed the fly. Perhaps she'll die.\n"+ + "\n"+ + "I know an old lady who swallowed a bird.\n"+ + "How absurd to swallow a bird!\n"+ + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.\n"+ + "She swallowed the spider to catch the fly.\n"+ + "I don't know why she swallowed the fly. Perhaps she'll die.\n"+ + "\n"+ + "I know an old lady who swallowed a cat.\n"+ + "Imagine that, to swallow a cat!\n"+ + "She swallowed the cat to catch the bird.\n"+ + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.\n"+ + "She swallowed the spider to catch the fly.\n"+ + "I don't know why she swallowed the fly. Perhaps she'll die.\n"+ + "\n"+ + "I know an old lady who swallowed a dog.\n"+ + "What a hog, to swallow a dog!\n"+ + "She swallowed the dog to catch the cat.\n"+ + "She swallowed the cat to catch the bird.\n"+ + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.\n"+ + "She swallowed the spider to catch the fly.\n"+ + "I don't know why she swallowed the fly. Perhaps she'll die.\n"+ + "\n"+ + "I know an old lady who swallowed a goat.\n"+ + "Just opened her throat and swallowed a goat!\n"+ + "She swallowed the goat to catch the dog.\n"+ + "She swallowed the dog to catch the cat.\n"+ + "She swallowed the cat to catch the bird.\n"+ + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.\n"+ + "She swallowed the spider to catch the fly.\n"+ + "I don't know why she swallowed the fly. Perhaps she'll die.\n"+ + "\n"+ + "I know an old lady who swallowed a cow.\n"+ + "I don't know how she swallowed a cow!\n"+ + "She swallowed the cow to catch the goat.\n"+ + "She swallowed the goat to catch the dog.\n"+ + "She swallowed the dog to catch the cat.\n"+ + "She swallowed the cat to catch the bird.\n"+ + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.\n"+ + "She swallowed the spider to catch the fly.\n"+ + "I don't know why she swallowed the fly. Perhaps she'll die.\n"+ + "\n"+ + "I know an old lady who swallowed a horse.\n"+ + "She's dead, of course!"; + Assert.Equal(expected, FoodChain.Verse(1, 8)); } } \ No newline at end of file diff --git a/exercises/luhn/LuhnTest.cs b/exercises/luhn/LuhnTest.cs index 759b577c42..e3aa4ceed8 100644 --- a/exercises/luhn/LuhnTest.cs +++ b/exercises/luhn/LuhnTest.cs @@ -14,32 +14,30 @@ public void A_single_zero_is_invalid() Assert.False(Luhn.IsValid("0")); } - [Fact(Skip = "Remove to run test")] - public void A_simple_valid_SIN_that_remains_valid_if_reversed() + public void A_simple_valid_sin_that_remains_valid_if_reversed() { Assert.True(Luhn.IsValid("059")); } [Fact(Skip = "Remove to run test")] - public void A_simple_valid_SIN_that_becomes_invalid_if_reversed() + public void A_simple_valid_sin_that_becomes_invalid_if_reversed() { Assert.True(Luhn.IsValid("59")); } [Fact(Skip = "Remove to run test")] - public void A_valid_Canadian_SIN() + public void A_valid_canadian_sin() { Assert.True(Luhn.IsValid("055 444 285")); } [Fact(Skip = "Remove to run test")] - public void Invalid_Canadian_SIN() + public void Invalid_canadian_sin() { Assert.False(Luhn.IsValid("055 444 286")); } - [Fact(Skip = "Remove to run test")] public void Invalid_credit_card() { @@ -81,5 +79,4 @@ public void Input_digit_9_is_correctly_converted_to_output_digit_9() { Assert.True(Luhn.IsValid("091")); } - -} +} \ No newline at end of file diff --git a/generators/Data/CanonicalDataValue.cs b/generators/Data/CanonicalDataValue.cs new file mode 100644 index 0000000000..83fcffa6dd --- /dev/null +++ b/generators/Data/CanonicalDataValue.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json.Linq; + +namespace Generators.Data +{ + public static class CanonicalDataValue + { + public static string StringArrayToString(object expected) + => string.Join("\\n\"+\n\"", ((JArray) expected).Values()); + } +} diff --git a/generators/Exercises/FoodChainExercise.cs b/generators/Exercises/FoodChainExercise.cs new file mode 100644 index 0000000000..be8c10b823 --- /dev/null +++ b/generators/Exercises/FoodChainExercise.cs @@ -0,0 +1,27 @@ +using Generators.Data; +using Generators.Methods; + +namespace Generators.Exercises +{ + public class FoodChainExercise : EqualityExercise + { + public FoodChainExercise() : base("food-chain") + { + } + + protected override TestMethodData CreateTestMethodData(CanonicalData canonicalData, CanonicalDataCase canonicalDataCase, int index) + { + var testMethodData = base.CreateTestMethodData(canonicalData, canonicalDataCase, index); + + testMethodData.Options.UseVariableForExpected = true; + testMethodData.CanonicalDataCase.Expected = CanonicalDataValue.StringArrayToString(canonicalDataCase.Expected); + + if (testMethodData.CanonicalDataCase.Data.ContainsKey("end verse")) + testMethodData.CanonicalDataCase.Input = new[] { testMethodData.CanonicalDataCase.Data["start verse"], testMethodData.CanonicalDataCase.Data["end verse"] }; + else + testMethodData.Options.InputProperty = "start verse"; + + return testMethodData; + } + } +} \ No newline at end of file diff --git a/generators/Exercises/NthPrimeExercise.cs b/generators/Exercises/NthPrimeExercise.cs index dabe7cbbb8..291dca5f73 100644 --- a/generators/Exercises/NthPrimeExercise.cs +++ b/generators/Exercises/NthPrimeExercise.cs @@ -1,7 +1,5 @@ using System; -using Generators.Data; using Generators.Methods; -using Humanizer; namespace Generators.Exercises { @@ -13,7 +11,7 @@ public NthPrimeExercise() : base("nth-prime") protected override TestMethod CreateTestMethod(TestMethodData testMethodData) { - if (testMethodData.CanonicalDataCase.Expected is bool b) + if (testMethodData.CanonicalDataCase.Expected is bool b && !b) { testMethodData.Options.ExceptionType = typeof(ArgumentOutOfRangeException); return CreateExceptionTestMethod(testMethodData); diff --git a/generators/Methods/EqualityTestMethodGenerator.cs b/generators/Methods/EqualityTestMethodGenerator.cs index 2d0cdd440e..d13e6b6210 100644 --- a/generators/Methods/EqualityTestMethodGenerator.cs +++ b/generators/Methods/EqualityTestMethodGenerator.cs @@ -1,9 +1,12 @@ using System; +using System.Linq; namespace Generators.Methods { public class EqualityTestMethodGenerator : TestMethodGenerator { + private const string Tab = " "; + protected override string Body { get @@ -11,15 +14,36 @@ protected override string Body switch (TestMethodData.Options.TestedMethodType) { case TestedMethodType.Static: + if (TestMethodData.Options.UseVariableForExpected) + return $"var expected = {FormattedExpectedVariable};\nAssert.Equal(expected, {TestedClassName}.{TestedMethod}({Input}));"; + return $"Assert.Equal({Expected}, {TestedClassName}.{TestedMethod}({Input}));"; case TestedMethodType.Instance: + if (TestMethodData.Options.UseVariableForExpected) + return $"var expected = {FormattedExpectedVariable};\nvar sut = new {TestedClassName}();\n Assert.Equal({Expected}, sut.{TestedMethod}({Input}));"; + return $"var sut = new {TestedClassName}();\n Assert.Equal({Expected}, sut.{TestedMethod}({Input}));"; case TestedMethodType.Extension: + if (TestMethodData.Options.UseVariableForExpected) + return $"var expected = {FormattedExpectedVariable};\nAssert.Equal(expected, {Input}.{TestedMethod}());"; + return $"Assert.Equal({Expected}, {Input}.{TestedMethod}());"; default: throw new ArgumentOutOfRangeException(); } } - } + } + + protected virtual string FormattedExpectedVariable + { + get + { + var lines = Expected.ToString().Split('\n'); + if (lines.Length == 1) + return lines[0]; + + return "\n" + string.Join("\n", lines.Select(line => $"{Tab}{line}")); + } + } } } \ No newline at end of file diff --git a/generators/Methods/TestMethodGenerator.cs b/generators/Methods/TestMethodGenerator.cs index da669c7e5f..551eb0570d 100644 --- a/generators/Methods/TestMethodGenerator.cs +++ b/generators/Methods/TestMethodGenerator.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using Humanizer; using To = Generators.Helpers.To; @@ -37,7 +38,7 @@ protected virtual string TestedMethod protected virtual object Input => TestMethodData.Options.FormatInput - ? FormatValue(InputValue) + ? FormatInputValue(InputValue) : InputValue; protected virtual object Expected => @@ -45,9 +46,9 @@ protected virtual string TestedMethod ? FormatValue(TestMethodData.CanonicalDataCase.Expected) : TestMethodData.CanonicalDataCase.Expected; - protected virtual object InputValue - => TestMethodData.Options.InputProperty == null - ? TestMethodData.CanonicalDataCase.Input + protected virtual object InputValue => + TestMethodData.Options.InputProperty == null + ? TestMethodData.CanonicalDataCase.Input : TestMethodData.CanonicalDataCase.Data[TestMethodData.Options.InputProperty]; protected virtual object FormatValue(object val) @@ -60,5 +61,16 @@ protected virtual object FormatValue(object val) return val; } } + + protected virtual object FormatInputValue(object val) + { + switch (val) + { + case IEnumerable inputs when !(val is string): + return string.Join(", ", inputs.Select(FormatValue)); + default: + return FormatValue(val); + } + } } } \ No newline at end of file diff --git a/generators/Methods/TestMethodOptions.cs b/generators/Methods/TestMethodOptions.cs index 2df1b31a4c..5689e264a0 100644 --- a/generators/Methods/TestMethodOptions.cs +++ b/generators/Methods/TestMethodOptions.cs @@ -7,6 +7,7 @@ public class TestMethodOptions public string InputProperty { get; set; } public bool FormatInput { get; set; } = true; public bool FormatExpected { get; set; } = true; + public bool UseVariableForExpected { get; set; } = false; public Type ExceptionType { get; set; } = typeof(ArgumentException); public TestedMethodType TestedMethodType { get; set; } = TestedMethodType.Static; } diff --git a/generators/Methods/TestMethodRenderer.cs b/generators/Methods/TestMethodRenderer.cs index d0343e82cf..1be927eb57 100644 --- a/generators/Methods/TestMethodRenderer.cs +++ b/generators/Methods/TestMethodRenderer.cs @@ -1,18 +1,26 @@ -namespace Generators.Methods +using System.Linq; + +namespace Generators.Methods { public static class TestMethodRenderer { + private const string Tab = " "; + private const string TestMethodTemplate = -@" [Fact{Skip}] - public void {Name}() - { - {Body} - }"; +@"{Tab}[Fact{Skip}] +{Tab}public void {Name}() +{Tab}{ +{Body} +{Tab}}"; public static string Render(TestMethod testMethod) => TestMethodTemplate + .Replace("{Tab}", Tab) .Replace("{Name}", testMethod.MethodName) - .Replace("{Body}", testMethod.Body) + .Replace("{Body}", RenderBody(testMethod)) .Replace("{Skip}", testMethod.Index == 0 ? "" : "(Skip = \"Remove to run test\")"); + + private static string RenderBody(TestMethod testMethod) + => string.Join("\n", testMethod.Body.Split('\n').Select(line => $"{Tab}{Tab}{line}")); } } diff --git a/generators/Program.cs b/generators/Program.cs index f4355c56fd..a06543c490 100644 --- a/generators/Program.cs +++ b/generators/Program.cs @@ -25,6 +25,8 @@ private static void GenerateAll() { Log.Information("Start generating tests..."); + TestFileGenerator.Generate(new FoodChainExercise()); + foreach (var exercise in new ExerciseCollection()) TestFileGenerator.Generate(exercise);