Skip to content

Commit

Permalink
Converting templates to Scriban
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikSchierboom committed Feb 2, 2025
1 parent 7dc6a88 commit dbbb872
Show file tree
Hide file tree
Showing 33 changed files with 290 additions and 287 deletions.
6 changes: 2 additions & 4 deletions docs/GENERATORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,10 @@ To generate a practice exercise's tests, the test generator:

### Step 1: read `canonical-data.json` file

The test generator parses the test cases from the exercise's `canonical-data.json` using the [JSON.net library](https://www.newtonsoft.com/json).
The test generator parses the test cases from the exercise's `canonical-data.json` using the [System.Text.Json namespace](https://learn.microsoft.com/en-us/dotnet/api/system.text.json).

Since some canonical data uses nesting, the parsed test case includes an additional `path` field that contains the `description` properties of any parent elements, as well as the test case's own `description` property.

Note: the JSON is parsed to an `ExpandoObject` instance, which makes dealing with dynamic data easier.

### Step 2: omit excluded tests from `tests.toml` file

Each exercise has a `tests.toml` file, in which individual tests can be excluded/disabled.
Expand All @@ -44,7 +42,7 @@ Finally, the output of the rendered template is written to the exercise's test f

## Templates

The templates are rendered using the [Handlebars.Net library](https://github.com/Handlebars-Net/Handlebars.Net), which supports [handlebars syntax](https://handlebarsjs.com/).
The templates are rendered using the [Scriban library](https://github.com/scriban/scriban/).

## Command-line interface

Expand Down
10 changes: 5 additions & 5 deletions exercises/practice/acronym/.meta/Generator.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ using Xunit;

public class AcronymTests
{
{{#test_cases}}
[Fact{{#unless @first}}(Skip = "Remove this Skip property to run this test"){{/unless}}]
public void {{test_method_name}}()
{{for testCase in testCases}}
[Fact{{if !for.first}}(Skip = "Remove this Skip property to run this test"){{end}}]
public void {{testCase.testMethodName}}()
{
Assert.Equal({{lit expected}}, Acronym.Abbreviate({{lit input.phrase}}));
Assert.Equal({{testCase.expected | string.literal}}, Acronym.Abbreviate({{testCase.input.phrase | string.literal}}));
}
{{/test_cases}}
{{end}}
}
30 changes: 15 additions & 15 deletions exercises/practice/affine-cipher/.meta/Generator.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,27 @@ using Xunit;

public class AffineCipherTests
{
{{#test_cases_by_property.encode}}
[Fact{{#unless @first}}(Skip = "Remove this Skip property to run this test"){{/unless}}]
public void {{short_test_method_name}}()
{{for testCase in testCasesByProperty.encode}}
[Fact{{if !for.first}}(Skip = "Remove this Skip property to run this test"){{end}}]
public void {{testCase.shortTestMethodName}}()
{
{{#if expected.error}}
Assert.Throws<ArgumentException>(() => AffineCipher.Encode({{lit input.phrase}}, {{input.key.a}}, {{input.key.b}}));
{{if testCase.expected.error}}
Assert.Throws<ArgumentException>(() => AffineCipher.Encode({{testCase.input.phrase | string.literal}}, {{testCase.input.key.a}}, {{testCase.input.key.b}}));
{{else}}
Assert.Equal({{lit expected}}, AffineCipher.Encode({{lit input.phrase}}, {{input.key.a}}, {{input.key.b}}));
{{/if}}
Assert.Equal({{testCase.expected | string.literal}}, AffineCipher.Encode({{testCase.input.phrase | string.literal}}, {{testCase.input.key.a}}, {{testCase.input.key.b}}));
{{end}}
}
{{/test_cases_by_property.encode}}
{{end}}

{{#test_cases_by_property.decode}}
{{for testCase in testCasesByProperty.decode}}
[Fact(Skip = "Remove this Skip property to run this test")]
public void {{short_test_method_name}}()
public void {{testCase.shortTestMethodName}}()
{
{{#if expected.error}}
Assert.Throws<ArgumentException>(() => AffineCipher.Decode({{lit input.phrase}}, {{input.key.a}}, {{input.key.b}}));
{{if testCase.expected.error}}
Assert.Throws<ArgumentException>(() => AffineCipher.Decode({{testCase.input.phrase | string.literal}}, {{testCase.input.key.a}}, {{testCase.input.key.b}}));
{{else}}
Assert.Equal({{lit expected}}, AffineCipher.Decode({{lit input.phrase}}, {{input.key.a}}, {{input.key.b}}));
{{/if}}
Assert.Equal({{testCase.expected | string.literal}}, AffineCipher.Decode({{testCase.input.phrase}}, {{testCase.input.key.a}}, {{testCase.input.key.b}}));
{{end}}
}
{{/test_cases_by_property.decode}}
{{end}}
}
12 changes: 6 additions & 6 deletions exercises/practice/affine-cipher/AffineCipherTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,37 +60,37 @@ public void Encode_with_a_not_coprime_to_m()
[Fact(Skip = "Remove this Skip property to run this test")]
public void Decode_exercism()
{
Assert.Equal("exercism", AffineCipher.Decode("tytgn fjr", 3, 7));
Assert.Equal("exercism", AffineCipher.Decode(tytgn fjr, 3, 7));

Check failure on line 63 in exercises/practice/affine-cipher/AffineCipherTests.cs

View workflow job for this annotation

GitHub Actions / test

Syntax error, ',' expected
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void Decode_a_sentence()
{
Assert.Equal("anobstacleisoftenasteppingstone", AffineCipher.Decode("qdwju nqcro muwhn odqun oppmd aunwd o", 19, 16));
Assert.Equal("anobstacleisoftenasteppingstone", AffineCipher.Decode(qdwju nqcro muwhn odqun oppmd aunwd o, 19, 16));

Check failure on line 69 in exercises/practice/affine-cipher/AffineCipherTests.cs

View workflow job for this annotation

GitHub Actions / test

Syntax error, ',' expected

Check failure on line 69 in exercises/practice/affine-cipher/AffineCipherTests.cs

View workflow job for this annotation

GitHub Actions / test

Syntax error, ',' expected

Check failure on line 69 in exercises/practice/affine-cipher/AffineCipherTests.cs

View workflow job for this annotation

GitHub Actions / test

Syntax error, ',' expected

Check failure on line 69 in exercises/practice/affine-cipher/AffineCipherTests.cs

View workflow job for this annotation

GitHub Actions / test

Syntax error, ',' expected

Check failure on line 69 in exercises/practice/affine-cipher/AffineCipherTests.cs

View workflow job for this annotation

GitHub Actions / test

Syntax error, ',' expected

Check failure on line 69 in exercises/practice/affine-cipher/AffineCipherTests.cs

View workflow job for this annotation

GitHub Actions / test

Syntax error, ',' expected
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void Decode_numbers()
{
Assert.Equal("testing123testing", AffineCipher.Decode("odpoz ub123 odpoz ub", 25, 7));
Assert.Equal("testing123testing", AffineCipher.Decode(odpoz ub123 odpoz ub, 25, 7));

Check failure on line 75 in exercises/practice/affine-cipher/AffineCipherTests.cs

View workflow job for this annotation

GitHub Actions / test

Syntax error, ',' expected

Check failure on line 75 in exercises/practice/affine-cipher/AffineCipherTests.cs

View workflow job for this annotation

GitHub Actions / test

Syntax error, ',' expected

Check failure on line 75 in exercises/practice/affine-cipher/AffineCipherTests.cs

View workflow job for this annotation

GitHub Actions / test

Syntax error, ',' expected
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void Decode_all_the_letters()
{
Assert.Equal("thequickbrownfoxjumpsoverthelazydog", AffineCipher.Decode("swxtj npvyk lruol iejdc blaxk swxmh qzglf", 17, 33));
Assert.Equal("thequickbrownfoxjumpsoverthelazydog", AffineCipher.Decode(swxtj npvyk lruol iejdc blaxk swxmh qzglf, 17, 33));
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void Decode_with_no_spaces_in_input()
{
Assert.Equal("thequickbrownfoxjumpsoverthelazydog", AffineCipher.Decode("swxtjnpvyklruoliejdcblaxkswxmhqzglf", 17, 33));
Assert.Equal("thequickbrownfoxjumpsoverthelazydog", AffineCipher.Decode(swxtjnpvyklruoliejdcblaxkswxmhqzglf, 17, 33));
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void Decode_with_too_many_spaces()
{
Assert.Equal("jollygreengiant", AffineCipher.Decode("vszzm cly yd cg qdp", 15, 16));
Assert.Equal("jollygreengiant", AffineCipher.Decode(vszzm cly yd cg qdp, 15, 16));
}

[Fact(Skip = "Remove this Skip property to run this test")]
Expand Down
30 changes: 17 additions & 13 deletions exercises/practice/allergies/.meta/Generator.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,30 @@ using Xunit;

public class AllergiesTests
{
{{#test_cases_by_property.allergicTo}}
[Fact{{#unless @first}}(Skip = "Remove this Skip property to run this test"){{/unless}}]
public void {{test_method_name}}()
{{for testCase in testCasesByProperty.allergicTo}}
[Fact{{if !for.first}}(Skip = "Remove this Skip property to run this test"){{end}}]
public void {{testCase.testMethodName}}()
{
var sut = new Allergies({{input.score}});
Assert.{{expected}}(sut.IsAllergicTo(Allergen.{{Capitalize input.item}}));
var sut = new Allergies({{testCase.input.score}});
Assert.{{testCase.expected ? "True" : "False"}}(sut.IsAllergicTo({{testCase.input.item | enum type: "Allergen"}}));
}
{{/test_cases_by_property.allergicTo}}
{{end}}

{{#test_cases_by_property.list}}
{{for testCase in testCasesByProperty.list}}
[Fact(Skip = "Remove this Skip property to run this test")]
public void {{test_method_name}}()
public void {{testCase.testMethodName}}()
{
var sut = new Allergies({{input.score}});
{{#isempty expected}}
var sut = new Allergies({{testCase.input.score}});
{{if testCase.expected.empty?}}
Assert.Empty(sut.List());
{{else}}
Allergen[] expected = [{{#each ../expected}}Allergen.{{Capitalize .}}{{#unless @last}},{{/unless}}{{/each}}];
Allergen[] expected = [
{{for expected in testCase.expected}}
{{testCase.expected | enum type: "Allergen"}}{{if !for.last}},{{end}}
{{end}}
];
Assert.Equal(expected, sut.List());
{{/isempty}}
{{end}}
}
{{/test_cases_by_property.list}}
{{end}}
}
18 changes: 9 additions & 9 deletions exercises/practice/allergies/AllergiesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -293,71 +293,71 @@ public void List_when_no_allergies()
public void List_when_just_eggs()
{
var sut = new Allergies(1);
Allergen[] expected = [Allergen.Eggs];
Allergen[] expected = [Allergen.["eggs"]];
Assert.Equal(expected, sut.List());
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void List_when_just_peanuts()
{
var sut = new Allergies(2);
Allergen[] expected = [Allergen.Peanuts];
Allergen[] expected = [Allergen.["peanuts"]];
Assert.Equal(expected, sut.List());
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void List_when_just_strawberries()
{
var sut = new Allergies(8);
Allergen[] expected = [Allergen.Strawberries];
Allergen[] expected = [Allergen.["strawberries"]];
Assert.Equal(expected, sut.List());
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void List_when_eggs_and_peanuts()
{
var sut = new Allergies(3);
Allergen[] expected = [Allergen.Eggs, Allergen.Peanuts];
Allergen[] expected = [Allergen.["eggs", "peanuts"], Allergen.["eggs", "peanuts"]];
Assert.Equal(expected, sut.List());
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void List_when_more_than_eggs_but_not_peanuts()
{
var sut = new Allergies(5);
Allergen[] expected = [Allergen.Eggs, Allergen.Shellfish];
Allergen[] expected = [Allergen.["eggs", "shellfish"], Allergen.["eggs", "shellfish"]];
Assert.Equal(expected, sut.List());
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void List_when_lots_of_stuff()
{
var sut = new Allergies(248);
Allergen[] expected = [Allergen.Strawberries, Allergen.Tomatoes, Allergen.Chocolate, Allergen.Pollen, Allergen.Cats];
Allergen[] expected = [Allergen.["strawberries", "tomatoes", "chocolate", "pollen", "cats"], Allergen.["strawberries", "tomatoes", "chocolate", "pollen", "cats"], Allergen.["strawberries", "tomatoes", "chocolate", "pollen", "cats"], Allergen.["strawberries", "tomatoes", "chocolate", "pollen", "cats"], Allergen.["strawberries", "tomatoes", "chocolate", "pollen", "cats"]];
Assert.Equal(expected, sut.List());
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void List_when_everything()
{
var sut = new Allergies(255);
Allergen[] expected = [Allergen.Eggs, Allergen.Peanuts, Allergen.Shellfish, Allergen.Strawberries, Allergen.Tomatoes, Allergen.Chocolate, Allergen.Pollen, Allergen.Cats];
Allergen[] expected = [Allergen.["eggs", "peanuts", "shellfish", "strawberries", "tomatoes", "chocolate", "pollen", "cats"], Allergen.["eggs", "peanuts", "shellfish", "strawberries", "tomatoes", "chocolate", "pollen", "cats"], Allergen.["eggs", "peanuts", "shellfish", "strawberries", "tomatoes", "chocolate", "pollen", "cats"], Allergen.["eggs", "peanuts", "shellfish", "strawberries", "tomatoes", "chocolate", "pollen", "cats"], Allergen.["eggs", "peanuts", "shellfish", "strawberries", "tomatoes", "chocolate", "pollen", "cats"], Allergen.["eggs", "peanuts", "shellfish", "strawberries", "tomatoes", "chocolate", "pollen", "cats"], Allergen.["eggs", "peanuts", "shellfish", "strawberries", "tomatoes", "chocolate", "pollen", "cats"], Allergen.["eggs", "peanuts", "shellfish", "strawberries", "tomatoes", "chocolate", "pollen", "cats"]];
Assert.Equal(expected, sut.List());
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void List_when_no_allergen_score_parts()
{
var sut = new Allergies(509);
Allergen[] expected = [Allergen.Eggs, Allergen.Shellfish, Allergen.Strawberries, Allergen.Tomatoes, Allergen.Chocolate, Allergen.Pollen, Allergen.Cats];
Allergen[] expected = [Allergen.["eggs", "shellfish", "strawberries", "tomatoes", "chocolate", "pollen", "cats"], Allergen.["eggs", "shellfish", "strawberries", "tomatoes", "chocolate", "pollen", "cats"], Allergen.["eggs", "shellfish", "strawberries", "tomatoes", "chocolate", "pollen", "cats"], Allergen.["eggs", "shellfish", "strawberries", "tomatoes", "chocolate", "pollen", "cats"], Allergen.["eggs", "shellfish", "strawberries", "tomatoes", "chocolate", "pollen", "cats"], Allergen.["eggs", "shellfish", "strawberries", "tomatoes", "chocolate", "pollen", "cats"], Allergen.["eggs", "shellfish", "strawberries", "tomatoes", "chocolate", "pollen", "cats"]];
Assert.Equal(expected, sut.List());
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void List_when_no_allergen_score_parts_without_highest_valid_score()
{
var sut = new Allergies(257);
Allergen[] expected = [Allergen.Eggs];
Allergen[] expected = [Allergen.["eggs"]];
Assert.Equal(expected, sut.List());
}
}
10 changes: 5 additions & 5 deletions exercises/practice/darts/.meta/Generator.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ using Xunit;

public class DartsTests
{
{{#test_cases}}
[Fact{{#unless @first}}(Skip = "Remove this Skip property to run this test"){{/unless}}]
public void {{test_method_name}}()
{{for testCase in testCases}}
[Fact{{if !for.first}}(Skip = "Remove this Skip property to run this test"){{end}}]
public void {{testCase.testMethodName}}()
{
Assert.Equal({{expected}}, Darts.Score({{input.x}}, {{input.y}}));
Assert.Equal({{testCase.expected}}, Darts.Score({{testCase.input.x}}, {{testCase.input.y}}));
}
{{/test_cases}}
{{end}}
}
2 changes: 1 addition & 1 deletion exercises/practice/darts/DartsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public void Just_outside_the_middle_circle()
[Fact(Skip = "Remove this Skip property to run this test")]
public void Just_within_the_outer_circle()
{
Assert.Equal(1, Darts.Score(-7, 7));
Assert.Equal(1, Darts.Score(-7.0, 7.0));
}

[Fact(Skip = "Remove this Skip property to run this test")]
Expand Down
26 changes: 5 additions & 21 deletions exercises/practice/difference-of-squares/.meta/Generator.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,11 @@ using Xunit;

public class DifferenceOfSquaresTests
{
{{#test_cases_by_property.squareOfSum}}
[Fact{{#unless @first}}(Skip = "Remove this Skip property to run this test"){{/unless}}]
public void {{short_test_method_name}}()
{{for testCase in testCases}}
[Fact{{if !for.first}}(Skip = "Remove this Skip property to run this test"){{end}}]
public void {{testCase.shortTestMethodName}}()
{
Assert.Equal({{expected}}, DifferenceOfSquares.CalculateSquareOfSum({{input.number}}));
Assert.Equal({{testCase.expected}}, DifferenceOfSquares.Calculate{{testCase.property | string.capitalize}}({{testCase.input.number}}));
}
{{/test_cases_by_property.squareOfSum}}

{{#test_cases_by_property.sumOfSquares}}
[Fact(Skip = "Remove this Skip property to run this test")]
public void {{short_test_method_name}}()
{
Assert.Equal({{expected}}, DifferenceOfSquares.CalculateSumOfSquares({{input.number}}));
}
{{/test_cases_by_property.sumOfSquares}}

{{#test_cases_by_property.differenceOfSquares}}
[Fact(Skip = "Remove this Skip property to run this test")]
public void {{short_test_method_name}}()
{
Assert.Equal({{expected}}, DifferenceOfSquares.CalculateDifferenceOfSquares({{input.number}}));
}
{{/test_cases_by_property.differenceOfSquares}}
{{end}}
}
10 changes: 5 additions & 5 deletions exercises/practice/eliuds-eggs/.meta/Generator.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ using Xunit;

public class EliudsEggsTests
{
{{#test_cases}}
[Fact{{#unless @first}}(Skip = "Remove this Skip property to run this test"){{/unless}}]
public void {{test_method_name}}()
{{for testCase in testCases}}
[Fact{{if !for.first}}(Skip = "Remove this Skip property to run this test"){{end}}]
public void {{testCase.testMethodName}}()
{
Assert.Equal({{expected}}, EliudsEggs.EggCount({{input.number}}));
Assert.Equal({{testCase.expected}}, EliudsEggs.EggCount({{testCase.input.number}}));
}
{{/test_cases}}
{{end}}
}
16 changes: 8 additions & 8 deletions exercises/practice/hamming/.meta/Generator.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ using Xunit;

public class HammingTests
{
{{#test_cases}}
[Fact{{#unless @first}}(Skip = "Remove this Skip property to run this test"){{/unless}}]
public void {{test_method_name}}()
{{for testCase in testCases}}
[Fact{{if !for.first}}(Skip = "Remove this Skip property to run this test"){{end}}]
public void {{testCase.testMethodName}}()
{
{{#if expected.error}}
Assert.Throws<ArgumentException>(() => Hamming.Distance({{lit input.strand1}}, {{lit input.strand2}}));
{{if testCase.expected.error}}
Assert.Throws<ArgumentException>(() => Hamming.Distance({{testCase.input.strand1 | string.literal}}, {{testCase.input.strand2 | string.literal}}));
{{else}}
Assert.Equal({{expected}}, Hamming.Distance({{lit input.strand1}}, {{lit input.strand2}}));
{{/if}}
Assert.Equal({{testCase.expected}}, Hamming.Distance({{testCase.input.strand1 | string.literal}}, {{testCase.input.strand2 | string.literal}}));
{{end}}
}
{{/test_cases}}
{{end}}
}
10 changes: 5 additions & 5 deletions exercises/practice/isogram/.meta/Generator.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ using Xunit;

public class IsogramTests
{
{{#test_cases}}
[Fact{{#unless @first}}(Skip = "Remove this Skip property to run this test"){{/unless}}]
public void {{test_method_name}}()
{{for testCase in testCases}}
[Fact{{if !for.first}}(Skip = "Remove this Skip property to run this test"){{end}}]
public void {{testCase.testMethodName}}()
{
Assert.{{expected}}(Isogram.IsIsogram({{lit input.phrase}}));
Assert.{{testCase.expected ? "True" : "False"}}(Isogram.IsIsogram({{phrase | string.literal}}));
}
{{/test_cases}}
{{end}}
}
Loading

0 comments on commit dbbb872

Please sign in to comment.