Skip to content

Commit a379dd0

Browse files
Bringing your track in line with the latest changes to Problem Specifications (#978)
* Add tests.toml file for all exercises with canonical data * update tests according to what's actually been implemented Used a pretty simple heuristic for discovering what's been implemented; this could likely use some refinement. Still, at least we're closer now to accuracy. * re-quote uuid keys to minimize diff * update perfect-numbers readme * use better formatting heuristics to detect which tests are implemented ```python3 import json import re import requests import shlex import string import subprocess import tomlkit from base64 import b64decode from collections import defaultdict from pathlib import Path from pprint import pprint REPO_ROOT = Path( subprocess.run(["git", "rev-parse", "--show-toplevel"], capture_output=True) .stdout.strip() .decode() ) FN_RE = re.compile(r"fn ([\w\d]+)") CANONICAL_DATA_URL = "https://raw.githubusercontent.com/exercism/problem-specifications/master/exercises/{}/canonical-data.json" ALPHANUMERIC = set(string.ascii_letters + string.digits + " ") class CanonicalData(dict): def __getitem__(self, exercise: str): try: return super().__getitem__(exercise) except KeyError: response = requests.get(CANONICAL_DATA_URL.format(exercise), timeout=1) response.raise_for_status() data = json.loads(response.content) names = {} for case in walk_cases(data): names[case["uuid"]] = ( "".join(c for c in case["description"] if c in ALPHANUMERIC) .lower() .replace(" ", "_") ) super().__setitem__(exercise, names) return super().__getitem__(exercise) def walk_cases(data): if "cases" in data: for case in data["cases"]: yield from walk_cases(case) if "uuid" in case and "description" in case: yield case CANONICAL_DATA = CanonicalData() class Functions(dict): def __getitem__(self, exercise: Path): try: return super().__getitem__(exercise) except KeyError: super().__setitem__(exercise, test_names(exercise)) return super().__getitem__(exercise) def test_names(exercise: Path): names = set() for test_file in (exercise / "tests").glob("*.rs"): with test_file.open("rt") as fp: test_data = fp.read() for match in FN_RE.finditer(test_data): names.add(match.group(1)) return names FUNCTIONS = Functions() def exercise_directories(): return (ex for ex in (REPO_ROOT / "exercises").iterdir() if ex.is_dir()) def canonical_name_for(exercise: Path, uuid: str) -> str: return CANONICAL_DATA[exercise.name][uuid] def categorize_implemented(exercise, canonical_tests): """ Accepts an iterable of uuid Produces a tuple `(implemented, unimplemented)`. `implemented` is the set of all uuids of canonical tests which have definitely been implemented. `unimplemented` is the set of all uuids of canonical tests which have not definitely been implemented. """ implemented = set() unimplemented = set() for test_uuid in canonical_tests: if is_implemented(exercise, test_uuid): implemented.add(test_uuid) else: unimplemented.add(test_uuid) return (implemented, unimplemented) def is_implemented(exercise: Path, uuid: str) -> bool: canonical_name = canonical_name_for(exercise, uuid) return ( canonical_name in FUNCTIONS[exercise] or generated_name(canonical_name) in FUNCTIONS[exercise] ) def generated_name(canonical_name: str) -> str: """ see https://github.com/exercism/rust/blob/abdc94b6082a5cf63ff7f22b7420965e8755951b/util/exercise/src/lib.rs#L197-L204 """ name = "".join(c for c in canonical_name if c in ALPHANUMERIC) return "test_" + name.replace(" ", "_").lower() def main(): for exercise in exercise_directories(): try: with (exercise / ".meta" / "tests.toml").open("rt") as tests_toml: tests_data = tomlkit.parse(tests_toml.read()) print(f"{exercise.name}:") try: implemented, unimplemented = categorize_implemented( exercise, tests_data["canonical-tests"].keys() ) for test_uuid in tests_data["canonical-tests"].keys(): tests_data["canonical-tests"][test_uuid] = test_uuid in implemented except Exception as e: # even if one test doesn't work, keep going through all of them print(f" {e}") else: # only modify the data if it passes, though with (exercise / ".meta" / "tests.toml").open("wt") as tests_toml: tests_toml.write(tomlkit.dumps(tests_data)) print(" unimplemented canonical functions:") for u in unimplemented: print(f" {u} => {canonical_name_for(exercise, u)}") except FileNotFoundError: pass # re-add quotes around uuid keys for minimal diffs subprocess.run( shlex.split( r"fd -uu tests.toml -x sed -i -E -e 's/^([-a-zA-Z0-9]+) =/\"\1\" =/'" ), cwd=REPO_ROOT / "exercises", ).check_returncode() if __name__ == "__main__": main() ``` Co-authored-by: Peter Goodspeed-Niklaus <[email protected]>
1 parent abdc94b commit a379dd0

File tree

81 files changed

+3466
-2
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+3466
-2
lines changed

exercises/acronym/.meta/tests.toml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[canonical-tests]
2+
3+
# basic
4+
"1e22cceb-c5e4-4562-9afe-aef07ad1eaf4" = true
5+
6+
# lowercase words
7+
"79ae3889-a5c0-4b01-baf0-232d31180c08" = true
8+
9+
# punctuation
10+
"ec7000a7-3931-4a17-890e-33ca2073a548" = true
11+
12+
# all caps word
13+
"32dd261c-0c92-469a-9c5c-b192e94a63b0" = true
14+
15+
# punctuation without whitespace
16+
"ae2ac9fa-a606-4d05-8244-3bcc4659c1d4" = true
17+
18+
# very long abbreviation
19+
"0e4b1e7c-1a6d-48fb-81a7-bf65eb9e69f9" = true
20+
21+
# consecutive delimiters
22+
"6a078f49-c68d-4b7b-89af-33a1a98c28cc" = true
23+
24+
# apostrophes
25+
"5118b4b1-4572-434c-8d57-5b762e57973e" = true
26+
27+
# underscore emphasis
28+
"adc12eab-ec2d-414f-b48c-66a4fc06cdef" = true
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
[canonical-tests]
2+
3+
# encode yes
4+
"2ee1d9af-1c43-416c-b41b-cefd7d4d2b2a" = true
5+
6+
# encode no
7+
"785bade9-e98b-4d4f-a5b0-087ba3d7de4b" = true
8+
9+
# encode OMG
10+
"2854851c-48fb-40d8-9bf6-8f192ed25054" = true
11+
12+
# encode O M G
13+
"bc0c1244-b544-49dd-9777-13a770be1bad" = true
14+
15+
# encode mindblowingly
16+
"381a1a20-b74a-46ce-9277-3778625c9e27" = true
17+
18+
# encode numbers
19+
"6686f4e2-753b-47d4-9715-876fdc59029d" = true
20+
21+
# encode deep thought
22+
"ae23d5bd-30a8-44b6-afbe-23c8c0c7faa3" = true
23+
24+
# encode all the letters
25+
"c93a8a4d-426c-42ef-9610-76ded6f7ef57" = true
26+
27+
# encode with a not coprime to m
28+
"0673638a-4375-40bd-871c-fb6a2c28effb" = true
29+
30+
# decode exercism
31+
"3f0ac7e2-ec0e-4a79-949e-95e414953438" = true
32+
33+
# decode a sentence
34+
"241ee64d-5a47-4092-a5d7-7939d259e077" = true
35+
36+
# decode numbers
37+
"33fb16a1-765a-496f-907f-12e644837f5e" = true
38+
39+
# decode all the letters
40+
"20bc9dce-c5ec-4db6-a3f1-845c776bcbf7" = true
41+
42+
# decode with no spaces in input
43+
"623e78c0-922d-49c5-8702-227a3e8eaf81" = true
44+
45+
# decode with too many spaces
46+
"58fd5c2a-1fd9-4563-a80a-71cff200f26f" = true
47+
48+
# decode with a not coprime to m
49+
"b004626f-c186-4af9-a3f4-58f74cdb86d5" = true
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
[canonical-tests]
2+
3+
# single bit one to decimal
4+
"5ce422f9-7a4b-4f44-ad29-49c67cb32d2c" = true
5+
6+
# binary to single decimal
7+
"0cc3fea8-bb79-46ac-a2ab-5a2c93051033" = true
8+
9+
# single decimal to binary
10+
"f12db0f9-0d3d-42c2-b3ba-e38cb375a2b8" = true
11+
12+
# binary to multiple decimal
13+
"2c45cf54-6da3-4748-9733-5a3c765d925b" = true
14+
15+
# decimal to binary
16+
"65ddb8b4-8899-4fcc-8618-181b2cf0002d" = true
17+
18+
# trinary to hexadecimal
19+
"8d418419-02a7-4824-8b7a-352d33c6987e" = true
20+
21+
# hexadecimal to trinary
22+
"d3901c80-8190-41b9-bd86-38d988efa956" = true
23+
24+
# 15-bit integer
25+
"5d42f85e-21ad-41bd-b9be-a3e8e4258bbf" = false
26+
27+
# empty list
28+
"d68788f7-66dd-43f8-a543-f15b6d233f83" = true
29+
30+
# single zero
31+
"5e27e8da-5862-4c5f-b2a9-26c0382b6be7" = true
32+
33+
# multiple zeros
34+
"2e1c2573-77e4-4b9c-8517-6c56c5bcfdf2" = true
35+
36+
# leading zeros
37+
"3530cd9f-8d6d-43f5-bc6e-b30b1db9629b" = true
38+
39+
# input base is one
40+
"a6b476a1-1901-4f2a-92c4-4d91917ae023" = true
41+
42+
# input base is zero
43+
"e21a693a-7a69-450b-b393-27415c26a016" = true
44+
45+
# input base is negative
46+
"54a23be5-d99e-41cc-88e0-a650ffe5fcc2" = false
47+
48+
# negative digit
49+
"9eccf60c-dcc9-407b-95d8-c37b8be56bb6" = false
50+
51+
# invalid positive digit
52+
"232fa4a5-e761-4939-ba0c-ed046cd0676a" = true
53+
54+
# output base is one
55+
"14238f95-45da-41dc-95ce-18f860b30ad3" = true
56+
57+
# output base is zero
58+
"73dac367-da5c-4a37-95fe-c87fad0a4047" = true
59+
60+
# output base is negative
61+
"13f81f42-ff53-4e24-89d9-37603a48ebd9" = false
62+
63+
# both bases are negative
64+
"0e6c895d-8a5d-4868-a345-309d094cfe8d" = false

exercises/allergies/.meta/tests.toml

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
[canonical-tests]
2+
3+
# not allergic to anything
4+
"17fc7296-2440-4ac4-ad7b-d07c321bc5a0" = false
5+
6+
# allergic only to eggs
7+
"07ced27b-1da5-4c2e-8ae2-cb2791437546" = false
8+
9+
# allergic to eggs and something else
10+
"5035b954-b6fa-4b9b-a487-dae69d8c5f96" = false
11+
12+
# allergic to something, but not eggs
13+
"64a6a83a-5723-4b5b-a896-663307403310" = false
14+
15+
# allergic to everything
16+
"90c8f484-456b-41c4-82ba-2d08d93231c6" = true
17+
18+
# not allergic to anything
19+
"d266a59a-fccc-413b-ac53-d57cb1f0db9d" = false
20+
21+
# allergic only to peanuts
22+
"ea210a98-860d-46b2-a5bf-50d8995b3f2a" = false
23+
24+
# allergic to peanuts and something else
25+
"eac69ae9-8d14-4291-ac4b-7fd2c73d3a5b" = false
26+
27+
# allergic to something, but not peanuts
28+
"9152058c-ce39-4b16-9b1d-283ec6d25085" = false
29+
30+
# allergic to everything
31+
"d2d71fd8-63d5-40f9-a627-fbdaf88caeab" = true
32+
33+
# not allergic to anything
34+
"b948b0a1-cbf7-4b28-a244-73ff56687c80" = false
35+
36+
# allergic only to shellfish
37+
"9ce9a6f3-53e9-4923-85e0-73019047c567" = false
38+
39+
# allergic to shellfish and something else
40+
"b272fca5-57ba-4b00-bd0c-43a737ab2131" = false
41+
42+
# allergic to something, but not shellfish
43+
"21ef8e17-c227-494e-8e78-470a1c59c3d8" = false
44+
45+
# allergic to everything
46+
"cc789c19-2b5e-4c67-b146-625dc8cfa34e" = true
47+
48+
# not allergic to anything
49+
"651bde0a-2a74-46c4-ab55-02a0906ca2f5" = false
50+
51+
# allergic only to strawberries
52+
"b649a750-9703-4f5f-b7f7-91da2c160ece" = false
53+
54+
# allergic to strawberries and something else
55+
"50f5f8f3-3bac-47e6-8dba-2d94470a4bc6" = false
56+
57+
# allergic to something, but not strawberries
58+
"23dd6952-88c9-48d7-a7d5-5d0343deb18d" = false
59+
60+
# allergic to everything
61+
"74afaae2-13b6-43a2-837a-286cd42e7d7e" = true
62+
63+
# not allergic to anything
64+
"c49a91ef-6252-415e-907e-a9d26ef61723" = false
65+
66+
# allergic only to tomatoes
67+
"b69c5131-b7d0-41ad-a32c-e1b2cc632df8" = false
68+
69+
# allergic to tomatoes and something else
70+
"1ca50eb1-f042-4ccf-9050-341521b929ec" = false
71+
72+
# allergic to something, but not tomatoes
73+
"e9846baa-456b-4eff-8025-034b9f77bd8e" = false
74+
75+
# allergic to everything
76+
"b2414f01-f3ad-4965-8391-e65f54dad35f" = true
77+
78+
# not allergic to anything
79+
"978467ab-bda4-49f7-b004-1d011ead947c" = false
80+
81+
# allergic only to chocolate
82+
"59cf4e49-06ea-4139-a2c1-d7aad28f8cbc" = false
83+
84+
# allergic to chocolate and something else
85+
"b0a7c07b-2db7-4f73-a180-565e07040ef1" = false
86+
87+
# allergic to something, but not chocolate
88+
"f5506893-f1ae-482a-b516-7532ba5ca9d2" = false
89+
90+
# allergic to everything
91+
"02debb3d-d7e2-4376-a26b-3c974b6595c6" = true
92+
93+
# not allergic to anything
94+
"17f4a42b-c91e-41b8-8a76-4797886c2d96" = false
95+
96+
# allergic only to pollen
97+
"7696eba7-1837-4488-882a-14b7b4e3e399" = false
98+
99+
# allergic to pollen and something else
100+
"9a49aec5-fa1f-405d-889e-4dfc420db2b6" = false
101+
102+
# allergic to something, but not pollen
103+
"3cb8e79f-d108-4712-b620-aa146b1954a9" = false
104+
105+
# allergic to everything
106+
"1dc3fe57-7c68-4043-9d51-5457128744b2" = true
107+
108+
# not allergic to anything
109+
"d3f523d6-3d50-419b-a222-d4dfd62ce314" = false
110+
111+
# allergic only to cats
112+
"eba541c3-c886-42d3-baef-c048cb7fcd8f" = false
113+
114+
# allergic to cats and something else
115+
"ba718376-26e0-40b7-bbbe-060287637ea5" = false
116+
117+
# allergic to something, but not cats
118+
"3c6dbf4a-5277-436f-8b88-15a206f2d6c4" = false
119+
120+
# allergic to everything
121+
"1faabb05-2b98-4995-9046-d83e4a48a7c1" = true
122+
123+
# no allergies
124+
"f9c1b8e7-7dc5-4887-aa93-cebdcc29dd8f" = false
125+
126+
# just eggs
127+
"9e1a4364-09a6-4d94-990f-541a94a4c1e8" = false
128+
129+
# just peanuts
130+
"8851c973-805e-4283-9e01-d0c0da0e4695" = false
131+
132+
# just strawberries
133+
"2c8943cb-005e-435f-ae11-3e8fb558ea98" = false
134+
135+
# eggs and peanuts
136+
"6fa95d26-044c-48a9-8a7b-9ee46ec32c5c" = false
137+
138+
# more than eggs but not peanuts
139+
"19890e22-f63f-4c5c-a9fb-fb6eacddfe8e" = false
140+
141+
# lots of stuff
142+
"4b68f470-067c-44e4-889f-c9fe28917d2f" = false
143+
144+
# everything
145+
"0881b7c5-9efa-4530-91bd-68370d054bc7" = false
146+
147+
# no allergen score parts
148+
"12ce86de-b347-42a0-ab7c-2e0570f0c65b" = false
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
[canonical-tests]
2+
3+
# puzzle with three letters
4+
"e0c08b07-9028-4d5f-91e1-d178fead8e1a" = false
5+
6+
# solution must have unique value for each letter
7+
"a504ee41-cb92-4ec2-9f11-c37e95ab3f25" = false
8+
9+
# leading zero solution is invalid
10+
"4e3b81d2-be7b-4c5c-9a80-cd72bc6d465a" = false
11+
12+
# puzzle with two digits final carry
13+
"8a3e3168-d1ee-4df7-94c7-b9c54845ac3a" = true
14+
15+
# puzzle with four letters
16+
"a9630645-15bd-48b6-a61e-d85c4021cc09" = false
17+
18+
# puzzle with six letters
19+
"3d905a86-5a52-4e4e-bf80-8951535791bd" = false
20+
21+
# puzzle with seven letters
22+
"4febca56-e7b7-4789-97b9-530d09ba95f0" = false
23+
24+
# puzzle with eight letters
25+
"12125a75-7284-4f9a-a5fa-191471e0d44f" = false
26+
27+
# puzzle with ten letters
28+
"fb05955f-38dc-477a-a0b6-5ef78969fffa" = false
29+
30+
# puzzle with ten letters and 199 addends
31+
"9a101e81-9216-472b-b458-b513a7adacf7" = false

exercises/anagram/.meta/tests.toml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
[canonical-tests]
2+
3+
# no matches
4+
"dd40c4d2-3c8b-44e5-992a-f42b393ec373" = false
5+
6+
# detects two anagrams
7+
"b3cca662-f50a-489e-ae10-ab8290a09bdc" = false
8+
9+
# does not detect anagram subsets
10+
"a27558ee-9ba0-4552-96b1-ecf665b06556" = false
11+
12+
# detects anagram
13+
"64cd4584-fc15-4781-b633-3d814c4941a4" = false
14+
15+
# detects three anagrams
16+
"99c91beb-838f-4ccd-b123-935139917283" = false
17+
18+
# detects multiple anagrams with different case
19+
"78487770-e258-4e1f-a646-8ece10950d90" = false
20+
21+
# does not detect non-anagrams with identical checksum
22+
"1d0ab8aa-362f-49b7-9902-3d0c668d557b" = false
23+
24+
# detects anagrams case-insensitively
25+
"9e632c0b-c0b1-4804-8cc1-e295dea6d8a8" = false
26+
27+
# detects anagrams using case-insensitive subject
28+
"b248e49f-0905-48d2-9c8d-bd02d8c3e392" = false
29+
30+
# detects anagrams using case-insensitive possible matches
31+
"f367325c-78ec-411c-be76-e79047f4bd54" = false
32+
33+
# does not detect an anagram if the original word is repeated
34+
"7cc195ad-e3c7-44ee-9fd2-d3c344806a2c" = false
35+
36+
# anagrams must use all letters exactly once
37+
"9878a1c9-d6ea-4235-ae51-3ea2befd6842" = false
38+
39+
# words are not anagrams of themselves (case-insensitive)
40+
"85757361-4535-45fd-ac0e-3810d40debc1" = false
41+
42+
# words other than themselves can be anagrams
43+
"a0705568-628c-4b55-9798-82e4acde51ca" = false
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[canonical-tests]
2+
3+
# Zero is an Armstrong number
4+
"c1ed103c-258d-45b2-be73-d8c6d9580c7b" = false
5+
6+
# Single digit numbers are Armstrong numbers
7+
"579e8f03-9659-4b85-a1a2-d64350f6b17a" = false
8+
9+
# There are no 2 digit Armstrong numbers
10+
"2d6db9dc-5bf8-4976-a90b-b2c2b9feba60" = false
11+
12+
# Three digit number that is an Armstrong number
13+
"509c087f-e327-4113-a7d2-26a4e9d18283" = false
14+
15+
# Three digit number that is not an Armstrong number
16+
"7154547d-c2ce-468d-b214-4cb953b870cf" = false
17+
18+
# Four digit number that is an Armstrong number
19+
"6bac5b7b-42e9-4ecb-a8b0-4832229aa103" = false
20+
21+
# Four digit number that is not an Armstrong number
22+
"eed4b331-af80-45b5-a80b-19c9ea444b2e" = false
23+
24+
# Seven digit number that is an Armstrong number
25+
"f971ced7-8d68-4758-aea1-d4194900b864" = false
26+
27+
# Seven digit number that is not an Armstrong number
28+
"7ee45d52-5d35-4fbd-b6f1-5c8cd8a67f18" = false

0 commit comments

Comments
 (0)