From 84d64525218b314bf473d0b2910457500ae8a315 Mon Sep 17 00:00:00 2001 From: Remo Senekowitsch Date: Mon, 11 Sep 2023 23:04:33 +0200 Subject: [PATCH] Sync exercise word-count with problem spec new tests found a bug in the example solution with apostrophe handling --- .../practice/word-count/.meta/example.rs | 2 +- .../word-count/.meta/test_template.tera | 27 +++ .../practice/word-count/.meta/tests.toml | 51 +++++- .../practice/word-count/tests/word-count.rs | 165 ++++++++++++------ 4 files changed, 185 insertions(+), 60 deletions(-) create mode 100644 exercises/practice/word-count/.meta/test_template.tera diff --git a/exercises/practice/word-count/.meta/example.rs b/exercises/practice/word-count/.meta/example.rs index 7e78fa1fd..f2074ec0c 100644 --- a/exercises/practice/word-count/.meta/example.rs +++ b/exercises/practice/word-count/.meta/example.rs @@ -6,8 +6,8 @@ pub fn word_count(input: &str) -> HashMap { let slice: &str = lower.as_ref(); for word in slice .split(|c: char| !c.is_alphanumeric() && c != '\'') - .filter(|s| !s.is_empty()) .map(|s| s.trim_matches('\'')) + .filter(|s| !s.is_empty()) { *map.entry(word.to_string()).or_insert(0) += 1; } diff --git a/exercises/practice/word-count/.meta/test_template.tera b/exercises/practice/word-count/.meta/test_template.tera new file mode 100644 index 000000000..20d887ede --- /dev/null +++ b/exercises/practice/word-count/.meta/test_template.tera @@ -0,0 +1,27 @@ +use std::collections::HashMap; + +fn check_word_count(mut output: HashMap, pairs: &[(&str, u32)]) { + // The reason for the awkward code in here is to ensure that the failure + // message for assert_eq! is as informative as possible. A simpler + // solution would simply check the length of the map, and then + // check for the presence and value of each key in the given pairs vector. + for &(k, v) in pairs.iter() { + assert_eq!((k, output.remove(&k.to_string()).unwrap_or(0)), (k, v)); + } + // may fail with a message that clearly shows all extra pairs in the map + assert_eq!(output.iter().collect::>(), vec![]); +} +{% for test in cases %} +#[test] +{% if loop.index != 1 -%} +#[ignore] +{% endif -%} +fn {{ test.description | slugify | replace(from="-", to="_") }}() { + let input = {{ test.input.sentence | json_encode() }}; + let output = {{ crate_name }}::{{ fn_names[0] }}(input); + let expected = &[{% for key, value in test.expected -%} + ({{ key | json_encode() }}, {{ value }}), + {%- endfor %}]; + check_word_count(output, expected); +} +{% endfor -%} diff --git a/exercises/practice/word-count/.meta/tests.toml b/exercises/practice/word-count/.meta/tests.toml index 46d1b6fa5..1be425b33 100644 --- a/exercises/practice/word-count/.meta/tests.toml +++ b/exercises/practice/word-count/.meta/tests.toml @@ -1,12 +1,57 @@ -# 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. + +[61559d5f-2cad-48fb-af53-d3973a9ee9ef] +description = "count one word" + +[5abd53a3-1aed-43a4-a15a-29f88c09cbbd] +description = "count one of each word" + +[2a3091e5-952e-4099-9fac-8f85d9655c0e] +description = "multiple occurrences of a word" + +[e81877ae-d4da-4af4-931c-d923cd621ca6] +description = "handles cramped lists" + +[7349f682-9707-47c0-a9af-be56e1e7ff30] +description = "handles expanded lists" + +[a514a0f2-8589-4279-8892-887f76a14c82] +description = "ignore punctuation" + +[d2e5cee6-d2ec-497b-bdc9-3ebe092ce55e] +description = "include numbers" + +[dac6bc6a-21ae-4954-945d-d7f716392dbf] +description = "normalize case" [4185a902-bdb0-4074-864c-f416e42a0f19] description = "with apostrophes" +include = false + +[4ff6c7d7-fcfc-43ef-b8e7-34ff1837a2d3] +description = "with apostrophes" +reimplements = "4185a902-bdb0-4074-864c-f416e42a0f19" [be72af2b-8afe-4337-b151-b297202e4a7b] description = "with quotations" +[8d6815fe-8a51-4a65-96f9-2fb3f6dc6ed6] +description = "substrings from the beginning" + [c5f4ef26-f3f7-4725-b314-855c04fb4c13] description = "multiple spaces not detected as a word" + +[50176e8a-fe8e-4f4c-b6b6-aa9cf8f20360] +description = "alternating word separators not detected as a word" + +[6d00f1db-901c-4bec-9829-d20eb3044557] +description = "quotation for word with apostrophe" diff --git a/exercises/practice/word-count/tests/word-count.rs b/exercises/practice/word-count/tests/word-count.rs index fdbd37f06..c37a3282f 100644 --- a/exercises/practice/word-count/tests/word-count.rs +++ b/exercises/practice/word-count/tests/word-count.rs @@ -1,116 +1,169 @@ use std::collections::HashMap; -fn check_word_count(s: &str, pairs: &[(&str, u32)]) { +fn check_word_count(mut output: HashMap, pairs: &[(&str, u32)]) { // The reason for the awkward code in here is to ensure that the failure // message for assert_eq! is as informative as possible. A simpler // solution would simply check the length of the map, and then // check for the presence and value of each key in the given pairs vector. - let mut m: HashMap = word_count::word_count(s); for &(k, v) in pairs.iter() { - assert_eq!((k, m.remove(&k.to_string()).unwrap_or(0)), (k, v)); + assert_eq!((k, output.remove(&k.to_string()).unwrap_or(0)), (k, v)); } // may fail with a message that clearly shows all extra pairs in the map - assert_eq!(m.iter().collect::>(), vec![]); + assert_eq!(output.iter().collect::>(), vec![]); } #[test] fn count_one_word() { - check_word_count("word", &[("word", 1)]); + let input = "word"; + let output = word_count::word_count(input); + let expected = &[("word", 1)]; + check_word_count(output, expected); } #[test] #[ignore] -fn count_one_of_each() { - check_word_count("one of each", &[("one", 1), ("of", 1), ("each", 1)]); +fn count_one_of_each_word() { + let input = "one of each"; + let output = word_count::word_count(input); + let expected = &[("one", 1), ("of", 1), ("each", 1)]; + check_word_count(output, expected); } #[test] #[ignore] -fn count_multiple_occurrences() { - check_word_count( - "one fish two fish red fish blue fish", - &[("one", 1), ("fish", 4), ("two", 1), ("red", 1), ("blue", 1)], - ); +fn multiple_occurrences_of_a_word() { + let input = "one fish two fish red fish blue fish"; + let output = word_count::word_count(input); + let expected = &[("one", 1), ("fish", 4), ("two", 1), ("red", 1), ("blue", 1)]; + check_word_count(output, expected); } #[test] #[ignore] -fn cramped_lists() { - check_word_count("one,two,three", &[("one", 1), ("two", 1), ("three", 1)]); +fn handles_cramped_lists() { + let input = "one,two,three"; + let output = word_count::word_count(input); + let expected = &[("one", 1), ("two", 1), ("three", 1)]; + check_word_count(output, expected); } #[test] #[ignore] -fn expanded_lists() { - check_word_count("one\ntwo\nthree", &[("one", 1), ("two", 1), ("three", 1)]); +fn handles_expanded_lists() { + let input = "one,\ntwo,\nthree"; + let output = word_count::word_count(input); + let expected = &[("one", 1), ("two", 1), ("three", 1)]; + check_word_count(output, expected); } #[test] #[ignore] fn ignore_punctuation() { - check_word_count( - "car : carpet as java : javascript!!&@$%^&", - &[ - ("car", 1), - ("carpet", 1), - ("as", 1), - ("java", 1), - ("javascript", 1), - ], - ); + let input = "car: carpet as java: javascript!!&@$%^&"; + let output = word_count::word_count(input); + let expected = &[ + ("car", 1), + ("carpet", 1), + ("as", 1), + ("java", 1), + ("javascript", 1), + ]; + check_word_count(output, expected); } #[test] #[ignore] fn include_numbers() { - check_word_count( - "testing, 1, 2 testing", - &[("testing", 2), ("1", 1), ("2", 1)], - ); + let input = "testing, 1, 2 testing"; + let output = word_count::word_count(input); + let expected = &[("testing", 2), ("1", 1), ("2", 1)]; + check_word_count(output, expected); } #[test] #[ignore] fn normalize_case() { - check_word_count("go Go GO Stop stop", &[("go", 3), ("stop", 2)]); + let input = "go Go GO Stop stop"; + let output = word_count::word_count(input); + let expected = &[("go", 3), ("stop", 2)]; + check_word_count(output, expected); } #[test] #[ignore] fn with_apostrophes() { - check_word_count( - "First: don't laugh. Then: don't cry.", - &[ - ("first", 1), - ("don't", 2), - ("laugh", 1), - ("then", 1), - ("cry", 1), - ], - ); + let input = "'First: don't laugh. Then: don't cry. You're getting it.'"; + let output = word_count::word_count(input); + let expected = &[ + ("first", 1), + ("don't", 2), + ("laugh", 1), + ("then", 1), + ("cry", 1), + ("you're", 1), + ("getting", 1), + ("it", 1), + ]; + check_word_count(output, expected); } #[test] #[ignore] fn with_quotations() { - check_word_count( - "Joe can't tell between 'large' and large.", - &[ - ("joe", 1), - ("can't", 1), - ("tell", 1), - ("between", 1), - ("large", 2), - ("and", 1), - ], - ); + let input = "Joe can't tell between 'large' and large."; + let output = word_count::word_count(input); + let expected = &[ + ("joe", 1), + ("can't", 1), + ("tell", 1), + ("between", 1), + ("large", 2), + ("and", 1), + ]; + check_word_count(output, expected); +} + +#[test] +#[ignore] +fn substrings_from_the_beginning() { + let input = "Joe can't tell between app, apple and a."; + let output = word_count::word_count(input); + let expected = &[ + ("joe", 1), + ("can't", 1), + ("tell", 1), + ("between", 1), + ("app", 1), + ("apple", 1), + ("and", 1), + ("a", 1), + ]; + check_word_count(output, expected); } #[test] #[ignore] fn multiple_spaces_not_detected_as_a_word() { - check_word_count( - " multiple whitespaces", - &[("multiple", 1), ("whitespaces", 1)], - ); + let input = " multiple whitespaces"; + let output = word_count::word_count(input); + let expected = &[("multiple", 1), ("whitespaces", 1)]; + check_word_count(output, expected); +} + +#[test] +#[ignore] +fn alternating_word_separators_not_detected_as_a_word() { + let input = ",\n,one,\n ,two \n 'three'"; + let output = word_count::word_count(input); + let expected = &[("one", 1), ("two", 1), ("three", 1)]; + check_word_count(output, expected); +} + +#[test] +#[ignore] +fn quotation_for_word_with_apostrophe() { + let input = "can, can't, 'can't'"; + let output = word_count::word_count(input); + let expected = &[("can", 1), ("can't", 2)]; + check_word_count(output, expected); }