Skip to content

Commit 8948920

Browse files
rabestroJegors Cemisovsclaude
authored
Generate the leap, saddle-points and kindergarten-garden suites (#903)
* Add a test generator that renders suites from the canonical data Ports the generator @IsaacG wrote for the awk, jq and tcl tracks (exercism/jq bin/test-generator), which he pointed at in #896, rather than writing another one. His case handling is kept as-is: `reimplements` chains, `include = false`, the problem-specifications lookup and the `git pull`. Two things are added on top. A `--check` mode, and CI gating on it, which his tracks do not have. That is the point of the exercise: `configlet sync --tests` compares tests.toml against problem-specifications and never compares the suite against either, so an exercise whose tests.toml is complete reads as in sync however thin its suite is. That is how the five exercises in #894, #896, #898, #900 and #901 drifted, and it covers 17 exercises and 67 cases. A generated suite cannot drift again. And a refusal to generate when tests.toml is missing canonical cases. The `reimplements` links live in tests.toml, so a stale one silently emits superseded cases: anagram would have emitted 3, phone-number 4, grade-school 1. Thirteen exercises are in that state today. Erroring out and pointing at `configlet sync --tests` is better than generating from a partial picture. darts is converted as the first exercise; nothing changes for the rest. Its 13 test bodies are byte for byte what was already committed, and only the header differs, now saying the file is generated. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Generate the leap, saddle-points and kindergarten-garden suites Converts the three exercises whose suites were synced by hand in #894, #896 and #900, now that the generator exists. darts was converted with it, so these complete the set. Writing a template for a suite that already exists is the honest check on whether the abstraction can express what the track writes. It could, with two shared macros: `assert_be` for `subject should be(expected)`, on one line while it fits and wrapped otherwise, and `test_open` for a `test(...)` line with a long description. The templates reproduce the committed suites apart from the header, which now says the file is generated, and three details: - saddle-points had two lines wrapped although they fit within the width; the generator leaves them on one line - kindergarten-garden's grouped test names now read outermost group first, "partial garden - multiple students ... - second student's garden", rather than the reversed order the hand-written file used Every generated line is well inside the 160 columns .scalafmt.conf allows; the longest is 108. Each template carries the mapping no generator can infer: saddle-points translates the canonical one-based {row, column} objects into this track's zero-based tuples, and kindergarten-garden maps the plant names onto the Plant enumeration. leap needed nothing beyond the shared macros. Verified with `bin/test leap darts saddle-points kindergarten-garden` (48 tests) and `bin/generate-tests --check`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Jegors Cemisovs <jcemisovs@evolution.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 69f2856 commit 8948920

6 files changed

Lines changed: 78 additions & 21 deletions

File tree

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{%- import "macros.j2" as macros -%}
2+
{%- set PLANT = {"violets": "Plant.Violets", "clover": "Plant.Clover",
3+
"radishes": "Plant.Radishes", "grass": "Plant.Grass"} -%}
4+
{%- macro plants(expected) -%}
5+
List({% for p in expected %}{{ PLANT[p] }}{% if not loop.last %}, {% endif %}{% endfor %})
6+
{%- endmacro -%}
7+
{{ macros.header("KindergartenGarden") }}
8+
{% for case in cases %}
9+
{% set diagram = case.input.diagram | scala %}
10+
{% set student = case.input.student | scala %}
11+
{% set inline = "Garden.defaultGarden(" ~ diagram ~ ").plants(" ~ student ~ ")" %}
12+
13+
{{ macros.test_open(case) }}
14+
{% if not loop.first %}
15+
pending
16+
{% endif %}
17+
{% if (" " ~ inline ~ " should be(") | length <= 100 %}
18+
{{ inline }} should be(
19+
{% else %}
20+
Garden
21+
.defaultGarden({{ diagram }})
22+
.plants({{ student }}) should be(
23+
{% endif %}
24+
{{ plants(case.expected) }})
25+
}
26+
{% endfor %}
27+
}

‎exercises/practice/kindergarten-garden/src/test/scala/GardenTest.scala‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import org.scalatest.funsuite.AnyFunSuite
22
import org.scalatest.matchers.should.Matchers
33

4-
5-
/** @version 1.1.1 */
4+
/** Generated from the canonical data in problem-specifications.
5+
*
6+
* Do not edit by hand: change .meta/template.j2 and run bin/generate-tests.
7+
*/
68
class KindergartenGardenTest extends AnyFunSuite with Matchers {
79

810
test("partial garden - garden with single student") {
@@ -23,14 +25,14 @@ class KindergartenGardenTest extends AnyFunSuite with Matchers {
2325
}
2426

2527
test(
26-
"multiple students for the same garden with three students, partial garden - second student's garden") {
28+
"partial garden - multiple students for the same garden with three students - second student's garden") {
2729
pending
2830
Garden.defaultGarden("VVCCGG\nVVCCGG").plants("Bob") should be(
2931
List(Plant.Clover, Plant.Clover, Plant.Clover, Plant.Clover))
3032
}
3133

3234
test(
33-
"multiple students for the same garden with three students, partial garden - third student's garden") {
35+
"partial garden - multiple students for the same garden with three students - third student's garden") {
3436
pending
3537
Garden.defaultGarden("VVCCGG\nVVCCGG").plants("Charlie") should be(
3638
List(Plant.Grass, Plant.Grass, Plant.Grass, Plant.Grass))
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{%- import "macros.j2" as macros -%}
2+
{{ macros.header("Leap") }}
3+
{% for case in cases %}
4+
5+
{{ macros.test_open(case) }}
6+
{% if not loop.first %}
7+
pending
8+
{% endif %}
9+
Leap.leapYear({{ case.input.year }}) should be ({{ case.expected | scala }})
10+
}
11+
{% endfor %}
12+
}

‎exercises/practice/leap/src/test/scala/LeapTest.scala‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import org.scalatest.funsuite.AnyFunSuite
22
import org.scalatest.matchers.should.Matchers
33

4-
5-
/** @version 1.3.0 */
4+
/** Generated from the canonical data in problem-specifications.
5+
*
6+
* Do not edit by hand: change .meta/template.j2 and run bin/generate-tests.
7+
*/
68
class LeapTest extends AnyFunSuite with Matchers {
79

810
test("year not divisible by 4 in common year") {
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{%- import "macros.j2" as macros -%}
2+
{#- The canonical data indexes rows and columns from 1; this track uses
3+
zero-based (row, column) tuples, so the expectations are translated. -#}
4+
{%- macro points(expected) -%}
5+
{% if expected %}Set({% for p in expected %}({{ p.row - 1 }}, {{ p.column - 1 }}){% if not loop.last %}, {% endif %}{% endfor %}){% else %}Set(){% endif %}
6+
{%- endmacro -%}
7+
{{ macros.header("SaddlePoints") }}
8+
{% for case in cases %}
9+
10+
{{ macros.test_open(case) }}
11+
{% if not loop.first %}
12+
pending
13+
{% endif %}
14+
{{ macros.assert_be("Matrix(" ~ (case.input.matrix | scala) ~ ").saddlePoints", points(case.expected)) }}
15+
}
16+
{% endfor %}
17+
}
Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import org.scalatest.funsuite.AnyFunSuite
22
import org.scalatest.matchers.should.Matchers
33

4-
5-
/** @version 1.1.0 */
4+
/** Generated from the canonical data in problem-specifications.
5+
*
6+
* Do not edit by hand: change .meta/template.j2 and run bin/generate-tests.
7+
*/
68
class SaddlePointsTest extends AnyFunSuite with Matchers {
79

810
test("Can identify single saddle point") {
9-
Matrix(List(List(9, 8, 7), List(5, 3, 2), List(6, 6, 7))).saddlePoints should be(
10-
Set((1, 0)))
11+
Matrix(List(List(9, 8, 7), List(5, 3, 2), List(6, 6, 7))).saddlePoints should be(Set((1, 0)))
1112
}
1213

1314
test("Can identify that empty matrix has no saddle points") {
@@ -17,8 +18,7 @@ class SaddlePointsTest extends AnyFunSuite with Matchers {
1718

1819
test("Can identify lack of saddle points when there are none") {
1920
pending
20-
Matrix(List(List(1, 2, 3), List(3, 1, 2), List(2, 3, 1))).saddlePoints should be(
21-
Set())
21+
Matrix(List(List(1, 2, 3), List(3, 1, 2), List(2, 3, 1))).saddlePoints should be(Set())
2222
}
2323

2424
test("Can identify multiple saddle points in a column") {
@@ -35,25 +35,22 @@ class SaddlePointsTest extends AnyFunSuite with Matchers {
3535

3636
test("Can identify saddle point in bottom right corner") {
3737
pending
38-
Matrix(List(List(8, 7, 9), List(6, 7, 6), List(3, 2, 5))).saddlePoints should be(
39-
Set((2, 2)))
38+
Matrix(List(List(8, 7, 9), List(6, 7, 6), List(3, 2, 5))).saddlePoints should be(Set((2, 2)))
4039
}
4140

4241
test("Can identify saddle points in a non square matrix") {
4342
pending
44-
Matrix(List(List(3, 1, 3), List(3, 2, 4))).saddlePoints should be(
45-
Set((0, 2), (0, 0)))
43+
Matrix(List(List(3, 1, 3), List(3, 2, 4))).saddlePoints should be(Set((0, 2), (0, 0)))
4644
}
4745

48-
test("Can identify that saddle points in a single column matrix are those with the minimum value") {
46+
test(
47+
"Can identify that saddle points in a single column matrix are those with the minimum value") {
4948
pending
50-
Matrix(List(List(2), List(1), List(4), List(1))).saddlePoints should be(
51-
Set((1, 0), (3, 0)))
49+
Matrix(List(List(2), List(1), List(4), List(1))).saddlePoints should be(Set((1, 0), (3, 0)))
5250
}
5351

5452
test("Can identify that saddle points in a single row matrix are those with the maximum value") {
5553
pending
56-
Matrix(List(List(2, 5, 3, 5))).saddlePoints should be(
57-
Set((0, 1), (0, 3)))
54+
Matrix(List(List(2, 5, 3, 5))).saddlePoints should be(Set((0, 1), (0, 3)))
5855
}
5956
}

0 commit comments

Comments
 (0)