Skip to content

Commit

Permalink
Sync triangle with problem-specifications (#1816)
Browse files Browse the repository at this point in the history
There was one challenge implementing the test template.
The problem specification of this exercise states that it is up to the
individual language tracks how to handle invalid triangles.
However, this property is not easy to detect from the specification of
the test cases.
The test cases with invalid triangles were excluded in tests.toml
and reimplemented in additional-tests.json with a custom property
of "invalid".

It was also necessary to separate the different properties into
individual modules. Some descriptions - and therefore the generated
test case names - are duplicated across different properties, so
they have to be in different namespaces.
  • Loading branch information
senekor authored Dec 12, 2023
1 parent 43074d5 commit 5916fdf
Show file tree
Hide file tree
Showing 7 changed files with 435 additions and 208 deletions.
4 changes: 4 additions & 0 deletions docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ Feel free to add your own in the crate `rust-tooling`.
Custom filters added there will be available to all templates.
How to create such custom filters is documented int he [tera docs][tera-docs-filters].

For a rather complicated example, check out the test template of `triangle`.
It organizes the test cases in modules and dynamically detects which tests to put behind feature gates.
That exercise also reimplements some test cases from upstream in `additional-tests.json`, in order to add more information to them necessary for generating good tests.

[tera-docs]: https://keats.github.io/tera/docs/#templates
[word-count-tmpl]: /exercises/practice/word-count/.meta/test_template.tera
[var-len-q-tmpl]: /exercises/practice/variable-length-quantity/.meta/test_template.tera
Expand Down
52 changes: 52 additions & 0 deletions exercises/practice/triangle/.meta/additional-tests.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
[
{
"uuid": "3ddc03ad-4066-41b0-8ba2-1b0ce0146064",
"description": "all zero sides is not a triangle",
"comments": ["reimplements 16e8ceb0-eadb-46d1-b892-c50327479251"],
"property": "invalid",
"input": {
"sides": [0, 0, 0]
},
"expected": false
},
{
"uuid": "3ddc03ad-4066-41b0-8ba2-1b0ce0146064",
"description": "first triangle inequality violation",
"comments": ["reimplements 2eba0cfb-6c65-4c40-8146-30b608905eae"],
"property": "invalid",
"input": {
"sides": [1, 1, 3]
},
"expected": false
},
{
"uuid": "3ddc03ad-4066-41b0-8ba2-1b0ce0146064",
"description": "second triangle inequality violation",
"comments": ["reimplements 278469cb-ac6b-41f0-81d4-66d9b828f8ac"],
"property": "invalid",
"input": {
"sides": [1, 3, 1]
},
"expected": false
},
{
"uuid": "3ddc03ad-4066-41b0-8ba2-1b0ce0146064",
"description": "third triangle inequality violation",
"comments": ["reimplements 90efb0c7-72bb-4514-b320-3a3892e278ff"],
"property": "invalid",
"input": {
"sides": [3, 1, 1]
},
"expected": false
},
{
"uuid": "3ddc03ad-4066-41b0-8ba2-1b0ce0146064",
"description": "may not violate triangle inequality",
"comments": ["reimplements 70ad5154-0033-48b7-af2c-b8d739cd9fdc"],
"property": "invalid",
"input": {
"sides": [7, 3, 2]
},
"expected": false
}
]
2 changes: 1 addition & 1 deletion exercises/practice/triangle/.meta/example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ where
}

pub fn is_isosceles(&self) -> bool {
self.count_distinct_pairs() == 2
self.count_distinct_pairs() <= 2
}

pub fn is_scalene(&self) -> bool {
Expand Down
85 changes: 85 additions & 0 deletions exercises/practice/triangle/.meta/test_template.tera
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
mod equilateral {
use triangle::Triangle;
{% for test in cases %}
{% if test.property != "equilateral" %}{% continue %}{% endif %}

#[test]
{% if loop.index != 1 -%}
#[ignore]
{% endif -%}
{% if test.description is containing("float") %}
#[cfg(feature = "generic")]
{% endif -%}
fn {{ test.description | slugify | replace(from="-", to="_") }}() {
let input = {{ test.input.sides | json_encode() }};
let output = Triangle::build(input).unwrap();
{%- if test.expected %}
assert!(output.is_equilateral());
{% else %}
assert!(!output.is_equilateral());
{% endif -%}
}
{% endfor -%}
}

mod isosceles {
use triangle::Triangle;
{% for test in cases %}
{% if test.property != "isosceles" %}{% continue %}{% endif %}

#[test]
#[ignore]
{% if test.scenarios and test.scenarios is containing("floating-point") %}
#[cfg(feature = "generic")]
{% endif -%}
fn {{ test.description | slugify | replace(from="-", to="_") }}() {
let input = {{ test.input.sides | json_encode() }};
let output = Triangle::build(input).unwrap();
{%- if test.expected %}
assert!(output.is_isosceles());
{% else %}
assert!(!output.is_isosceles());
{% endif -%}
}
{% endfor -%}
}

mod scalene {
use triangle::Triangle;
{% for test in cases %}
{% if test.property != "scalene" %}{% continue %}{% endif %}

#[test]
#[ignore]
{% if test.scenarios and test.scenarios is containing("floating-point") %}
#[cfg(feature = "generic")]
{% endif -%}
fn {{ test.description | slugify | replace(from="-", to="_") }}() {
let input = {{ test.input.sides | json_encode() }};
let output = Triangle::build(input).unwrap();
{%- if test.expected %}
assert!(output.is_scalene());
{% else %}
assert!(!output.is_scalene());
{% endif -%}
}
{% endfor -%}
}

mod invalid {
use triangle::Triangle;
{% for test in cases %}
{% if test.property != "invalid" %}{% continue %}{% endif %}

#[test]
#[ignore]
{% if test.scenarios and test.scenarios is containing("floating-point") %}
#[cfg(feature = "generic")]
{% endif -%}
fn {{ test.description | slugify | replace(from="-", to="_") }}() {
let input = {{ test.input.sides | json_encode() }};
let output = Triangle::build(input);
assert!(output.is_none());
}
{% endfor -%}
}
111 changes: 108 additions & 3 deletions exercises/practice/triangle/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,108 @@
# This is an auto-generated file. Regular comments will be removed when this
# file is regenerated. Regenerating will not touch any manually added keys,
# so comments can be added in a "comment" key.
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.

[8b2c43ac-7257-43f9-b552-7631a91988af]
description = "equilateral triangle -> all sides are equal"

[33eb6f87-0498-4ccf-9573-7f8c3ce92b7b]
description = "equilateral triangle -> any side is unequal"

[c6585b7d-a8c0-4ad8-8a34-e21d36f7ad87]
description = "equilateral triangle -> no sides are equal"

[16e8ceb0-eadb-46d1-b892-c50327479251]
description = "equilateral triangle -> all zero sides is not a triangle"
include = false
comment = """
This is testing an invalid triangle, which we chose to implement
using an option. The problem specifications make it hard to detect
invalid triangles. We therefore skip this test and reimplement it
in .meta/additional-tests.json with the custom property "invalid".
"""

[3022f537-b8e5-4cc1-8f12-fd775827a00c]
description = "equilateral triangle -> sides may be floats"

[cbc612dc-d75a-4c1c-87fc-e2d5edd70b71]
description = "isosceles triangle -> last two sides are equal"

[e388ce93-f25e-4daf-b977-4b7ede992217]
description = "isosceles triangle -> first two sides are equal"

[d2080b79-4523-4c3f-9d42-2da6e81ab30f]
description = "isosceles triangle -> first and last sides are equal"

[8d71e185-2bd7-4841-b7e1-71689a5491d8]
description = "isosceles triangle -> equilateral triangles are also isosceles"

[840ed5f8-366f-43c5-ac69-8f05e6f10bbb]
description = "isosceles triangle -> no sides are equal"

[2eba0cfb-6c65-4c40-8146-30b608905eae]
description = "isosceles triangle -> first triangle inequality violation"
include = false
comment = """
This is testing an invalid triangle, which we chose to implement
using an option. The problem specifications make it hard to detect
invalid triangles. We therefore skip this test and reimplement it
in .meta/additional-tests.json with the custom property "invalid".
"""

[278469cb-ac6b-41f0-81d4-66d9b828f8ac]
description = "isosceles triangle -> second triangle inequality violation"
include = false
comment = """
This is testing an invalid triangle, which we chose to implement
using an option. The problem specifications make it hard to detect
invalid triangles. We therefore skip this test and reimplement it
in .meta/additional-tests.json with the custom property "invalid".
"""

[90efb0c7-72bb-4514-b320-3a3892e278ff]
description = "isosceles triangle -> third triangle inequality violation"
include = false
comment = """
This is testing an invalid triangle, which we chose to implement
using an option. The problem specifications make it hard to detect
invalid triangles. We therefore skip this test and reimplement it
in .meta/additional-tests.json with the custom property "invalid".
"""

[adb4ee20-532f-43dc-8d31-e9271b7ef2bc]
description = "isosceles triangle -> sides may be floats"

[e8b5f09c-ec2e-47c1-abec-f35095733afb]
description = "scalene triangle -> no sides are equal"

[2510001f-b44d-4d18-9872-2303e7977dc1]
description = "scalene triangle -> all sides are equal"

[c6e15a92-90d9-4fb3-90a2-eef64f8d3e1e]
description = "scalene triangle -> first and second sides are equal"

[3da23a91-a166-419a-9abf-baf4868fd985]
description = "scalene triangle -> first and third sides are equal"

[b6a75d98-1fef-4c42-8e9a-9db854ba0a4d]
description = "scalene triangle -> second and third sides are equal"

[70ad5154-0033-48b7-af2c-b8d739cd9fdc]
description = "scalene triangle -> may not violate triangle inequality"
include = false
comment = """
This is testing an invalid triangle, which we chose to implement
using an option. The problem specifications make it hard to detect
invalid triangles. We therefore skip this test and reimplement it
in .meta/additional-tests.json with the custom property "invalid".
"""

[26d9d59d-f8f1-40d3-ad58-ae4d54123d7d]
description = "scalene triangle -> sides may be floats"
Loading

0 comments on commit 5916fdf

Please sign in to comment.