Skip to content

Commit

Permalink
Sync exercise word-count with problem spec
Browse files Browse the repository at this point in the history
new tests found a bug in the example solution with apostrophe handling
  • Loading branch information
senekor committed Sep 16, 2023
1 parent 7328ece commit 713e0c1
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 60 deletions.
2 changes: 1 addition & 1 deletion exercises/practice/word-count/.meta/example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ pub fn word_count(input: &str) -> HashMap<String, u32> {
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;
}
Expand Down
27 changes: 27 additions & 0 deletions exercises/practice/word-count/.meta/test_template.tera
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use std::collections::HashMap;

fn check_word_count(mut output: HashMap<String, u32>, 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<(&String, &u32)>>(), 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 -%}
51 changes: 48 additions & 3 deletions exercises/practice/word-count/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -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"
165 changes: 109 additions & 56 deletions exercises/practice/word-count/tests/word-count.rs
Original file line number Diff line number Diff line change
@@ -1,116 +1,169 @@
use std::collections::HashMap;

fn check_word_count(s: &str, pairs: &[(&str, u32)]) {
fn check_word_count(mut output: HashMap<String, u32>, 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<String, u32> = 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<(&String, &u32)>>(), vec![]);
assert_eq!(output.iter().collect::<Vec<(&String, &u32)>>(), 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);
}

0 comments on commit 713e0c1

Please sign in to comment.