Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 7 additions & 28 deletions bin/generate_tests
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@ def get_cases(specs: pathlib.Path, exercise: pathlib.Path) -> list[dict]:
return cases


def filter_togdscript(obj) -> str:
"""Filter `togdscript` that converts an arbitrary object to the GDScript version."""
def filter_quote(obj) -> str:
"""Filter `quote` that converts an arbitrary object to the GDScript version."""
if isinstance(obj, dict):
out = ", ".join(f"{filter_togdscript(k)}: {filter_togdscript(v)}" for k, v in obj.items())
out = ", ".join(f"{filter_quote(k)}: {filter_quote(v)}" for k, v in obj.items())
return "{" + out + "}"
if isinstance(obj, list):
out = ", ".join(filter_togdscript(i) for i in obj)
out = ", ".join(filter_quote(i) for i in obj)
return f"[{out}]"
if obj is True:
return "true"
Expand All @@ -91,7 +91,7 @@ def filter_togdscript(obj) -> str:
if "'" not in obj:
return "'" + obj + "'"
return '"' + obj.replace('"', '\\"') + '"'
return TypeError(f"filter_togdscript for type {type(obj)} not supported")
return TypeError(f"filter_quote for type {type(obj)} not supported")


def filter_tojson(data, separators=(',', ':'), indent=None) -> str:
Expand All @@ -102,41 +102,20 @@ 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
# GDScript
env.filters["togdscript"] = filter_togdscript
# GDScript quoting
env.filters["quote"] = filter_quote
# 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) + "]"
)
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)
translation = str.maketrans({i: "_" for i in string.punctuation + " "})
for case in cases:
case["expected"] = bool_to_str(case["expected"])
case["func_name"] = re.sub("_+", "_", case["description"].lower().translate(translation))
if len(case.get("input", {}).values()) == 1:
case["given"] = list(case["input"].values())[0]
Expand Down
4 changes: 2 additions & 2 deletions exercises/practice/acronym/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% for case in cases %}
func test_{{ case.func_name }}(solution_script):
var got = solution_script.{{ case.property | camel_to_snake }}({{ case.given | togdscript }})
var want = {{ case.expected | togdscript }}
var got = solution_script.{{ case.property | camel_to_snake }}({{ case.given | quote }})
var want = {{ case.expected | quote }}
return [got, want]

{% endfor %}
Expand Down
2 changes: 1 addition & 1 deletion exercises/practice/allergies/.meta/template.j2
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ func _sorted(array):

func test_{{ case.input.item }}_{{ case.func_name }}(allergies):
allergies.score = {{ case.input.score }}
return [allergies.allergic_to("{{ case.input.item }}"), {{ case.expected }}]
return [allergies.allergic_to("{{ case.input.item }}"), {{ case.expected | quote }}]
{%- else %}

func test_list_when_{{ case.func_name }}(allergies):
Expand Down
4 changes: 2 additions & 2 deletions exercises/practice/alphametics/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% for case in cases %}
func test_{{ case.func_name }}(solution_script):
var got = solution_script.{{ case.property | camel_to_snake }}({{ case.given | togdscript }})
var want = {{ case.expected | togdscript }}
var got = solution_script.{{ case.property | camel_to_snake }}({{ case.given | quote }})
var want = {{ case.expected | quote }}
return [got, want]

{% endfor %}
Expand Down
4 changes: 2 additions & 2 deletions exercises/practice/binary-search-tree/.meta/template.j2
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ func equal(a, b):

{% for case in cases %}
func test_{{ case.func_name }}(solution_script):
var bst = solution_script.BinarySearchTree.new({{ case.given | togdscript | replace('"', '') }})
var bst = solution_script.BinarySearchTree.new({{ case.given | quote | replace('"', '') }})
var got = bst.{{ case.property | camel_to_snake }}()
{%- if case.property == "data" %}
var want = {{ node(case.expected) }}
return [equal(got, want), true]
{%- else %}
var want = {{ case.expected | togdscript | replace('"', '') }}
var want = {{ case.expected | quote | replace('"', '') }}
return [got, want]
{%- endif %}

Expand Down
6 changes: 3 additions & 3 deletions exercises/practice/camicia/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{% for case in cases %}
func test_{{ case.func_name }}(solution_script):
var want = {{ case.expected | togdscript }}
var want = {{ case.expected | quote }}
var got = solution_script.{{ case.property | camel_to_snake }}(
{{ case.input.playerA | togdscript }},
{{ case.input.playerB | togdscript }}
{{ case.input.playerA | quote }},
{{ case.input.playerB | quote }}
)
return [got, want]

Expand Down
4 changes: 2 additions & 2 deletions exercises/practice/difference-of-squares/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% for case in cases %}
func test_{{ case.func_name }}(solution_script):
var inputs = {{ case.given | togdscript }}
var expected = {{ case.expected | togdscript }}
var inputs = {{ case.given | quote }}
var expected = {{ case.expected | quote }}
return [solution_script.{{ case.property | camel_to_snake }}(inputs), expected]

{% endfor %}
4 changes: 2 additions & 2 deletions exercises/practice/etl/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{% for case in cases %}
func test_{{ case.func_name }}(solution_script):
var inputs = { {%- for key, val in case.given.items() %}
{{ key }}: {{ val | togdscript }},{% endfor %}
{{ key }}: {{ val | quote }},{% endfor %}
}
var expected = { {%- for key, val in case.expected.items() %}
{{ key | togdscript }}: {{ val | togdscript }},{% endfor %}
{{ key | quote }}: {{ val | quote }},{% endfor %}
}
return [solution_script.{{ case.property | camel_to_snake }}(inputs), expected]

Expand Down
4 changes: 2 additions & 2 deletions exercises/practice/flatten-array/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% for case in cases %}
func test_{{ case.func_name }}(solution_script):
var inputs = {{ case.given | togdscript }}
var expected = {{ case.expected | togdscript }}
var inputs = {{ case.given | quote }}
var expected = {{ case.expected | quote }}
return [solution_script.{{ case.property }}(inputs), expected]

{% endfor %}
4 changes: 2 additions & 2 deletions exercises/practice/game-of-life/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{% for case in cases %}
func test_{{ case.func_name }}(solution_script):
var initial = [{% for row in case.given %}
{{ row | togdscript }},{% endfor %}
{{ row | quote }},{% endfor %}
]
var want = [{% for row in case.expected %}
{{ row | togdscript }},{% endfor %}
{{ row | quote }},{% endfor %}
]
return [solution_script.{{ case.property | camel_to_snake }}(initial), want]

Expand Down
4 changes: 2 additions & 2 deletions exercises/practice/gigasecond/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% for case in cases %}{% if case.property == "add" %}
func test_{{ case.func_name }}(solution_script):
var got = solution_script.{{ case.property | camel_to_snake }}({{ case.given | togdscript }})
var want = {{ case.expected | togdscript }}
var got = solution_script.{{ case.property | camel_to_snake }}({{ case.given | quote }})
var want = {{ case.expected | quote }}
return [got, want]

{% endif %}{% endfor %}
Expand Down
4 changes: 2 additions & 2 deletions exercises/practice/hamming/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% for case in cases %}
func test_{{ case.func_name }}(solution_script):
var strand1 = {{ case.input.strand1 | togdscript }}
var strand2 = {{ case.input.strand2 | togdscript }}
var strand1 = {{ case.input.strand1 | quote }}
var strand2 = {{ case.input.strand2 | quote }}
var got = solution_script.{{ case.property }}(strand1, strand2)
{%- if not case.expect_error %}
var want = {{ case.expected }}
Expand Down
2 changes: 1 addition & 1 deletion exercises/practice/hello-world/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{% for case in cases %}
func test_{{ case.func_name }}(solution_script):
var expected = {{ case.expected | togdscript }}
var expected = {{ case.expected | quote }}
var got = solution_script.{{ case.property | camel_to_snake }}()
return [got, expected]

Expand Down
28 changes: 14 additions & 14 deletions exercises/practice/isogram/isogram_test.gd
Original file line number Diff line number Diff line change
@@ -1,82 +1,82 @@
func test_empty_string(solution_script):
var got = solution_script.is_isogram("")
var want = true
var want = True
return [got, want]


func test_isogram_with_only_lower_case_characters(solution_script):
var got = solution_script.is_isogram("isogram")
var want = true
var want = True
return [got, want]


func test_word_with_one_duplicated_character(solution_script):
var got = solution_script.is_isogram("eleven")
var want = false
var want = False
return [got, want]


func test_word_with_one_duplicated_character_from_the_end_of_the_alphabet(solution_script):
var got = solution_script.is_isogram("zzyzx")
var want = false
var want = False
return [got, want]


func test_longest_reported_english_isogram(solution_script):
var got = solution_script.is_isogram("subdermatoglyphic")
var want = true
var want = True
return [got, want]


func test_word_with_duplicated_character_in_mixed_case(solution_script):
var got = solution_script.is_isogram("Alphabet")
var want = false
var want = False
return [got, want]


func test_word_with_duplicated_character_in_mixed_case_lowercase_first(solution_script):
var got = solution_script.is_isogram("alphAbet")
var want = false
var want = False
return [got, want]


func test_hypothetical_isogrammic_word_with_hyphen(solution_script):
var got = solution_script.is_isogram("thumbscrew-japingly")
var want = true
var want = True
return [got, want]


func test_hypothetical_word_with_duplicated_character_following_hyphen(solution_script):
var got = solution_script.is_isogram("thumbscrew-jappingly")
var want = false
var want = False
return [got, want]


func test_isogram_with_duplicated_hyphen(solution_script):
var got = solution_script.is_isogram("six-year-old")
var want = true
var want = True
return [got, want]


func test_made_up_name_that_is_an_isogram(solution_script):
var got = solution_script.is_isogram("Emily Jung Schwartzkopf")
var want = true
var want = True
return [got, want]


func test_duplicated_character_in_the_middle(solution_script):
var got = solution_script.is_isogram("accentor")
var want = false
var want = False
return [got, want]


func test_same_first_and_last_characters(solution_script):
var got = solution_script.is_isogram("angola")
var want = false
var want = False
return [got, want]


func test_word_with_duplicated_character_and_with_two_hyphens(solution_script):
var got = solution_script.is_isogram("up-to-date")
var want = false
var want = False
return [got, want]
4 changes: 2 additions & 2 deletions exercises/practice/killer-sudoku-helper/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% for case in cases %}
func test_{{ case.func_name }}(solution_script):
var got = solution_script.{{ case.property | camel_to_snake }}({{ case.given.sum }}, {{ case.given.size }}, {{ case.given.exclude | togdscript }})
var want = {{ case.expected | togdscript }}
var got = solution_script.{{ case.property | camel_to_snake }}({{ case.given.sum }}, {{ case.given.size }}, {{ case.given.exclude | quote }})
var want = {{ case.expected | quote }}
got.sort()
want.sort()
return [got, want]
Expand Down
18 changes: 9 additions & 9 deletions exercises/practice/leap/leap_test.gd
Original file line number Diff line number Diff line change
@@ -1,52 +1,52 @@
func test_year_not_divisible_by_4_in_common_year(solution_script):
var got = solution_script.leap(2015)
var want = false
var want = False
return [got, want]


func test_year_divisible_by_2_not_divisible_by_4_in_common_year(solution_script):
var got = solution_script.leap(1970)
var want = false
var want = False
return [got, want]


func test_year_divisible_by_4_not_divisible_by_100_in_leap_year(solution_script):
var got = solution_script.leap(1996)
var want = true
var want = True
return [got, want]


func test_year_divisible_by_4_and_5_is_still_a_leap_year(solution_script):
var got = solution_script.leap(1960)
var want = true
var want = True
return [got, want]


func test_year_divisible_by_100_not_divisible_by_400_in_common_year(solution_script):
var got = solution_script.leap(2100)
var want = false
var want = False
return [got, want]


func test_year_divisible_by_100_but_not_by_3_is_still_not_a_leap_year(solution_script):
var got = solution_script.leap(1900)
var want = false
var want = False
return [got, want]


func test_year_divisible_by_400_is_leap_year(solution_script):
var got = solution_script.leap(2000)
var want = true
var want = True
return [got, want]


func test_year_divisible_by_400_but_not_by_125_is_still_a_leap_year(solution_script):
var got = solution_script.leap(2400)
var want = true
var want = True
return [got, want]


func test_year_divisible_by_200_not_divisible_by_400_in_common_year(solution_script):
var got = solution_script.leap(1800)
var want = false
var want = False
return [got, want]
Loading
Loading