diff --git a/bin/generate_tests b/bin/generate_tests index a37115f3..319a06b3 100755 --- a/bin/generate_tests +++ b/bin/generate_tests @@ -77,9 +77,13 @@ def filter_tojson(data, separators=(',', ':'), indent=None) -> str: def jinja_env(exercise: pathlib.Path) -> jinja2.Environment: """Return a configured Jinja env with filters added.""" env = jinja2.Environment(loader=jinja2.FileSystemLoader(exercise / ".meta")) + # Shell quoting env.filters["quote"] = shlex.quote + # JSON formatting, default to compact form (`jq -c`). env.filters["tojson"] = filter_tojson + # String escaping, ANSI-C style. env.filters["repr"] = repr + # Return a dict with only specified keys kepts. env.filters["camel_to_snake"] = lambda x: re.sub(r"([a-z])([A-Z])", (lambda m: f"{m.group(1)}_{m.group(2).lower()}"), x) env.filters["format_list"] = lambda x: shlex.quote( "[" + ",".join(f'"{i}"' if isinstance(i, str) else str(i) for i in x) + "]" @@ -87,9 +91,24 @@ def jinja_env(exercise: pathlib.Path) -> jinja2.Environment: return env +def bool_to_str(obj): + """Convert boolean values to strings.""" + if isinstance(obj, dict): + return {key: bool_to_str(val) for key, val in obj.items()} + if isinstance(obj, list): + return [bool_to_str(val) for val in obj] + if obj is True: + return "true" + if obj is False: + return "false" + return obj + + def generate(specs: pathlib.Path, exercise: pathlib.Path) -> None: """Generate and write test file for a given spec and exercise.""" cases = get_cases(specs, exercise) + for case in cases: + case["expected"] = bool_to_str(case["expected"]) timestamp = datetime.datetime.now(tz=datetime.UTC).replace(microsecond=0).isoformat() header = textwrap.dedent(f"""\ @@ -106,7 +125,12 @@ def generate(specs: pathlib.Path, exercise: pathlib.Path) -> None: } # Render the template. - out = jinja_env(exercise).get_template("template.j2").render(data).strip() + try: + template = jinja_env(exercise).get_template("template.j2") + out = template.render(data) + except jinja2.exceptions.TemplateAssertionError as e: + e.add_note(f"Error rendering template for {exercise.name}") + raise # Check for changes or the lack thereof. test_file = exercise / json.loads((exercise / ".meta/config.json").read_text())["files"]["test"][0] diff --git a/exercises/practice/allergies/.meta/template.j2 b/exercises/practice/allergies/.meta/template.j2 index c297fe71..7cb97b52 100644 --- a/exercises/practice/allergies/.meta/template.j2 +++ b/exercises/practice/allergies/.meta/template.j2 @@ -6,7 +6,7 @@ run bash {{ solution }} {{ case["input"]["score"] }} allergic_to {{ case["input"]["item"] }} assert_success - assert_output "{{ case["expected"] | lower }}" + assert_output "{{ case["expected"] }}" {%- else %} run bash {{ solution }} {{ case["input"]["score"] }} list diff --git a/exercises/practice/armstrong-numbers/.meta/template.j2 b/exercises/practice/armstrong-numbers/.meta/template.j2 index 3ccac0a3..e72d8732 100644 --- a/exercises/practice/armstrong-numbers/.meta/template.j2 +++ b/exercises/practice/armstrong-numbers/.meta/template.j2 @@ -4,6 +4,6 @@ {% if idx == 0 %}# {% endif %}[[ $BATS_RUN_SKIPPED == "true" ]] || skip run bash {{ solution }} {{ case["input"]["number"] }} assert_success - assert_output "{{ case["expected"] | lower }}" + assert_output "{{ case["expected"] }}" } {% endfor %} diff --git a/exercises/practice/isbn-verifier/.meta/template.j2 b/exercises/practice/isbn-verifier/.meta/template.j2 index a38a58a0..a8b790ef 100644 --- a/exercises/practice/isbn-verifier/.meta/template.j2 +++ b/exercises/practice/isbn-verifier/.meta/template.j2 @@ -4,6 +4,6 @@ {% if idx == 0 %}# {% endif %}[[ $BATS_RUN_SKIPPED == "true" ]] || skip run bash {{ solution }} '{{ case["input"]["isbn"] }}' assert_success - assert_output "{{ case["expected"] | lower }}" + assert_output "{{ case["expected"] }}" } {% endfor %} diff --git a/exercises/practice/isogram/.meta/template.j2 b/exercises/practice/isogram/.meta/template.j2 index 7162d470..2da022e2 100644 --- a/exercises/practice/isogram/.meta/template.j2 +++ b/exercises/practice/isogram/.meta/template.j2 @@ -4,6 +4,6 @@ {% if idx == 0 %}# {% endif %}[[ $BATS_RUN_SKIPPED == "true" ]] || skip run bash {{ solution }} '{{ case["input"]["phrase"] }}' assert_success - assert_output "{{ case["expected"] | lower }}" + assert_output "{{ case["expected"] }}" } {% endfor %} diff --git a/exercises/practice/leap/.meta/template.j2 b/exercises/practice/leap/.meta/template.j2 index f25d071c..1b2a1d48 100644 --- a/exercises/practice/leap/.meta/template.j2 +++ b/exercises/practice/leap/.meta/template.j2 @@ -5,7 +5,7 @@ run bash {{ solution }} {{ case["input"]["year"] }} assert_success - assert_output "{{ case["expected"] | lower }}" + assert_output "{{ case["expected"] }}" } {% endfor %} @test 'No input should return an error' { diff --git a/exercises/practice/luhn/.meta/template.j2 b/exercises/practice/luhn/.meta/template.j2 index 3ec587b1..4aa35e5f 100644 --- a/exercises/practice/luhn/.meta/template.j2 +++ b/exercises/practice/luhn/.meta/template.j2 @@ -4,6 +4,6 @@ {% if idx == 0 %}# {% endif %}[[ $BATS_RUN_SKIPPED == "true" ]] || skip run bash {{ solution }} "{{ case["input"]["value"] }}" assert_success - assert_output "{{ case["expected"] | lower }}" + assert_output "{{ case["expected"] }}" } {% endfor %} diff --git a/exercises/practice/matching-brackets/.meta/template.j2 b/exercises/practice/matching-brackets/.meta/template.j2 index a50508af..a9cd1c52 100644 --- a/exercises/practice/matching-brackets/.meta/template.j2 +++ b/exercises/practice/matching-brackets/.meta/template.j2 @@ -4,6 +4,6 @@ {% if idx == 0 %}# {% endif %}[[ $BATS_RUN_SKIPPED == "true" ]] || skip run bash {{ solution }} "{{ case["input"]["value"] }}" assert_success - assert_output "{{ case["expected"] | lower }}" + assert_output "{{ case["expected"] }}" } {% endfor %} diff --git a/exercises/practice/pangram/.meta/template.j2 b/exercises/practice/pangram/.meta/template.j2 index c3c18f86..6a39f981 100644 --- a/exercises/practice/pangram/.meta/template.j2 +++ b/exercises/practice/pangram/.meta/template.j2 @@ -4,6 +4,6 @@ {% if idx == 0 %}# {% endif %}[[ $BATS_RUN_SKIPPED == "true" ]] || skip run bash {{ solution }} '{{ case["input"]["sentence"] }}' assert_success - assert_output "{{ case["expected"] | lower }}" + assert_output "{{ case["expected"] }}" } {% endfor %} diff --git a/exercises/practice/pythagorean-triplet/pythagorean_triplet.bats b/exercises/practice/pythagorean-triplet/pythagorean_triplet.bats index ce3704ce..55ef0a93 100644 --- a/exercises/practice/pythagorean-triplet/pythagorean_triplet.bats +++ b/exercises/practice/pythagorean-triplet/pythagorean_triplet.bats @@ -1,8 +1,7 @@ #!/usr/bin/env bats load bats-extra -# generated on 2026-06-29T06:52:43+00:00 -# local version: 2.0.0.0 +# generated on 2026-06-30T03:19:10+00:00 @test "triplets whose sum is 12" { # [[ $BATS_RUN_SKIPPED == "true" ]] || skip diff --git a/exercises/practice/satellite/satellite.bats b/exercises/practice/satellite/satellite.bats index 30187ed1..376b4b45 100644 --- a/exercises/practice/satellite/satellite.bats +++ b/exercises/practice/satellite/satellite.bats @@ -1,8 +1,7 @@ #!/usr/bin/env bats load bats-extra -# generated on 2026-06-28T23:19:01+00:00 -# local version: 2.0.0.0 +# generated on 2026-06-30T03:19:10+00:00 @test "Empty tree" { # [[ $BATS_RUN_SKIPPED == "true" ]] || skip diff --git a/exercises/practice/triangle/.meta/template.j2 b/exercises/practice/triangle/.meta/template.j2 index 451421f2..65494e3e 100644 --- a/exercises/practice/triangle/.meta/template.j2 +++ b/exercises/practice/triangle/.meta/template.j2 @@ -4,6 +4,6 @@ {% if idx == 0 %}# {% endif %}[[ $BATS_RUN_SKIPPED == "true" ]] || skip run bash {{ solution }} {{ case["property"] }} {{ case["input"]["sides"] | join(" ") }} assert_success - assert_output "{{ case["expected"] | lower }}" + assert_output "{{ case["expected"] }}" } {% endfor %}