From a2d89322a15c795d1cb8f72c360e3e132896f555 Mon Sep 17 00:00:00 2001 From: Denis Looby Date: Thu, 26 Oct 2017 19:31:19 +0100 Subject: [PATCH 01/15] two-fer: Update test suite to match canonical-data.json and resolve #1010 --- exercises/two-fer/two_fer_test.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/exercises/two-fer/two_fer_test.py b/exercises/two-fer/two_fer_test.py index ceef22c2782..5c0cbb02396 100644 --- a/exercises/two-fer/two_fer_test.py +++ b/exercises/two-fer/two_fer_test.py @@ -2,13 +2,16 @@ import two_fer +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0 + class Two_Fer_test(unittest.TestCase): def test_empty(self): self.assertEqual(two_fer.two_fer(), 'One for you, one for me.') - def test_eve(self): - self.assertEqual(two_fer.two_fer("Eve"), "One for Eve, one for me.") + def test_alice(self): + self.assertEqual(two_fer.two_fer("Alice"), + "One for Alice, one for me.") def test_bob(self): self.assertEqual(two_fer.two_fer("Bob"), "One for Bob, one for me.") From da99ef6e4d428cc07865d4c7f56cab94811b816e Mon Sep 17 00:00:00 2001 From: stefan setyadi Date: Fri, 27 Oct 2017 02:50:18 +0800 Subject: [PATCH 02/15] Add exercise tree-building to resolve #741 --- config.json | 14 ++ exercises/tree-building/README.md | 25 +++ exercises/tree-building/example.py | 46 +++++ exercises/tree-building/tree_building.py | 50 +++++ exercises/tree-building/tree_building_test.py | 183 ++++++++++++++++++ 5 files changed, 318 insertions(+) create mode 100644 exercises/tree-building/README.md create mode 100644 exercises/tree-building/example.py create mode 100644 exercises/tree-building/tree_building.py create mode 100644 exercises/tree-building/tree_building_test.py diff --git a/config.json b/config.json index 1ef09578105..11c81ab345b 100644 --- a/config.json +++ b/config.json @@ -771,6 +771,20 @@ "transforming" ] }, + { + "uuid": "6aacc273-0a33-8180-679e-c12c440c0fcf89e1fb6", + "slug": "tree-building", + "core": false, + "unlocked_by": null, + "difficulty": 3, + "topics": [ + "refactoring", + "sorting", + "trees", + "records", + "maps" + ] + }, { "uuid": "dcc0ee26-e384-4bd4-8c4b-613fa0bb8188", "slug": "poker", diff --git a/exercises/tree-building/README.md b/exercises/tree-building/README.md new file mode 100644 index 00000000000..02fc81706b6 --- /dev/null +++ b/exercises/tree-building/README.md @@ -0,0 +1,25 @@ +Refactor a tree building algorithm. + +Some web-forums have a tree layout, so posts are presented as a tree. However +the posts are typically stored in a database as an unsorted set of records. Thus +when presenting the posts to the user the tree structure has to be +reconstructed. + +Your job will be to refactor a working but slow and ugly piece of code that +implements the tree building logic for highly abstracted records. The records +only contain an ID number and a parent ID number. The ID number is always +between 0 (inclusive) and the length of the record list (exclusive). All records +have a parent ID lower than their own ID, except for the root record, which has +a parent ID that's equal to its own ID. + +An example tree: + +```text +root (ID: 0, parent ID: 0) +|-- child1 (ID: 1, parent ID: 0) +| |-- grandchild1 (ID: 2, parent ID: 1) +| +-- grandchild2 (ID: 4, parent ID: 1) ++-- child2 (ID: 3, parent ID: 0) +| +-- grandchild3 (ID: 6, parent ID: 3) ++-- child3 (ID: 5, parent ID: 0) +``` diff --git a/exercises/tree-building/example.py b/exercises/tree-building/example.py new file mode 100644 index 00000000000..ff25bc84f5a --- /dev/null +++ b/exercises/tree-building/example.py @@ -0,0 +1,46 @@ +class Record(): + def __init__(self, record_id, parent_id): + self.record_id = record_id + self.parent_id = parent_id + + def equal_id(self): + return self.record_id == self.parent_id + + +class Node(): + def __init__(self, node_id): + self.node_id = node_id + self.children = [] + + +def validateRecord(record): + if record.equal_id() and record.record_id != 0: + # only root(id=0) should have equal id + raise ValueError + elif not record.equal_id() and record.parent_id >= record.record_id: + # non-root node id should be smaller than its parent_id + raise ValueError + + +def BuildTree(records): + parent_dict = {} + node_dict = {} + ordered_id = sorted((i.record_id for i in records)) + for record in records: + validateRecord(record) + parent_dict[record.record_id] = record.parent_id + node_dict[record.record_id] = Node(record.record_id) + + root_id = 0 + root = None + + for index, record_id in enumerate(ordered_id): + if index != record_id: + raise ValueError + if record_id == root_id: + root = node_dict[record_id] + else: + parent_id = parent_dict[record_id] + node_dict[parent_id].children.append(node_dict[record_id]) + + return root diff --git a/exercises/tree-building/tree_building.py b/exercises/tree-building/tree_building.py new file mode 100644 index 00000000000..ace2cdc0cec --- /dev/null +++ b/exercises/tree-building/tree_building.py @@ -0,0 +1,50 @@ +class Record(): + def __init__(self, record_id, parent_id): + self.record_id = record_id + self.parent_id = parent_id + + +class Node(): + def __init__(self, node_id): + self.node_id = node_id + self.children = [] + + +def BuildTree(records): + root = None + records.sort(key=lambda x: x.record_id) + ordered_id = [i.record_id for i in records] + if records: + if ordered_id[-1] != len(ordered_id) - 1: + raise ValueError + if ordered_id[0] != 0: + raise ValueError + trees = [] + parent = {} + for i in range(len(ordered_id)): + for j in records: + if ordered_id[i] == j.record_id: + if j.record_id == 0: + if j.parent_id != 0: + raise ValueError + if j.record_id < j.parent_id: + raise ValueError + if j.record_id == j.parent_id: + if j.record_id != 0: + raise ValueError + trees.append(Node(ordered_id[i])) + for i in range(len(ordered_id)): + for j in trees: + if i == j.node_id: + parent = j + for j in records: + if j.parent_id == i: + for k in trees: + if k.node_id == 0: + continue + if j.record_id == k.node_id: + child = k + parent.children.append(child) + if len(trees) > 0: + root = trees[0] + return root diff --git a/exercises/tree-building/tree_building_test.py b/exercises/tree-building/tree_building_test.py new file mode 100644 index 00000000000..47b9e2a2445 --- /dev/null +++ b/exercises/tree-building/tree_building_test.py @@ -0,0 +1,183 @@ +import unittest + +from tree_building import Record, BuildTree + + +class TestBuildingTest(unittest.TestCase): + """ + Record(record_id, parent_id): records given to be processed + Node(node_id): Node in tree + BuildTree(records): records as argument and returns tree + BuildTree should raise ValueError if given records are invalid + """ + + def test_empty_list_input(self): + records = [] + root = BuildTree(records) + self.assertIsNone(root) + + def test_one_node(self): + records = [ + Record(0, 0) + ] + root = BuildTree(records) + + self.assert_node_is_leaf(root, node_id=0) + + def test_three_nodes_in_order(self): + records = [ + Record(0, 0), + Record(1, 0), + Record(2, 0) + ] + root = BuildTree(records) + + self.assert_node_is_branch(root, node_id=0, children_count=2) + self.assert_node_is_leaf(root.children[0], node_id=1) + self.assert_node_is_leaf(root.children[1], node_id=2) + + def test_three_nodes_in_reverse_order(self): + records = [ + Record(2, 0), + Record(1, 0), + Record(0, 0) + ] + root = BuildTree(records) + + self.assert_node_is_branch(root, node_id=0, children_count=2) + self.assert_node_is_leaf(root.children[0], node_id=1) + self.assert_node_is_leaf(root.children[1], node_id=2) + + def test_more_than_two_children(self): + records = [ + Record(0, 0), + Record(1, 0), + Record(2, 0), + Record(3, 0) + ] + root = BuildTree(records) + + self.assert_node_is_branch(root, node_id=0, children_count=3) + self.assert_node_is_leaf(root.children[0], node_id=1) + self.assert_node_is_leaf(root.children[1], node_id=2) + self.assert_node_is_leaf(root.children[2], node_id=3) + + def test_binary_tree(self): + records = [ + Record(6, 2), + Record(0, 0), + Record(3, 1), + Record(2, 0), + Record(4, 1), + Record(5, 2), + Record(1, 0) + ] + root = BuildTree(records) + + self.assert_node_is_branch(root, 0, 2) + self.assert_node_is_branch(root.children[0], 1, 2) + self.assert_node_is_branch(root.children[1], 2, 2) + self.assert_node_is_leaf(root.children[0].children[0], 3) + self.assert_node_is_leaf(root.children[0].children[1], 4) + self.assert_node_is_leaf(root.children[1].children[0], 5) + self.assert_node_is_leaf(root.children[1].children[1], 6) + + def test_unbalanced_tree(self): + records = [ + Record(0, 0), + Record(1, 0), + Record(2, 0), + Record(3, 1), + Record(4, 1), + Record(5, 1), + Record(6, 2), + ] + root = BuildTree(records) + + self.assert_node_is_branch(root, 0, 2) + self.assert_node_is_branch(root.children[0], 1, 3) + self.assert_node_is_branch(root.children[1], 2, 1) + self.assert_node_is_leaf(root.children[0].children[0], 3) + self.assert_node_is_leaf(root.children[0].children[1], 4) + self.assert_node_is_leaf(root.children[0].children[2], 5) + self.assert_node_is_leaf(root.children[1].children[0], 6) + + def test_root_node_has_parent(self): + records = [ + Record(0, 1), + Record(1, 0) + ] + # Root parent_id should be equal to record_id(0) + with self.assertRaises(ValueError): + BuildTree(records) + + def test_no_root_node(self): + records = [ + Record(1, 0), + Record(2, 0) + ] + # Record with record_id 0 (root) is missing + with self.assertRaises(ValueError): + BuildTree(records) + + def test_non_continuous(self): + records = [ + Record(2, 0), + Record(4, 2), + Record(1, 0), + Record(0, 0) + ] + # Record with record_id 3 is missing + with self.assertRaises(ValueError): + BuildTree(records) + + def test_cycle_directly(self): + records = [ + Record(5, 2), + Record(3, 2), + Record(2, 2), + Record(4, 1), + Record(1, 0), + Record(0, 0), + Record(6, 3) + ] + # Cycle caused by Record 2 with parent_id pointing to itself + with self.assertRaises(ValueError): + BuildTree(records) + + def test_cycle_indirectly(self): + records = [ + Record(5, 2), + Record(3, 2), + Record(2, 6), + Record(4, 1), + Record(1, 0), + Record(0, 0), + Record(6, 3) + ] + # Cycle caused by Record 2 with parent_id(6) greater than record_id(2) + with self.assertRaises(ValueError): + BuildTree(records) + + def test_higher_id_parent_of_lower_id(self): + records = [ + Record(0, 0), + Record(2, 0), + Record(1, 2) + ] + # Record 1 have parent_id(2) greater than record_id(1) + with self.assertRaises(ValueError): + BuildTree(records) + + def assert_node_is_branch(self, node, node_id, children_count): + self.assertEqual(node.node_id, node_id) + self.assertNotEqual(len(node.children), 0) + self.assertEqual(len(node.children), children_count) + + def assert_node_is_leaf(self, node, node_id): + self.assertEqual(node.node_id, node_id) + self.assertEqual(len(node.children), 0) + + +if __name__ == '__main__': + unittest.main() From a5e1aee8ec2d0379c32d570e66a445bfb65bee6e Mon Sep 17 00:00:00 2001 From: kishan Date: Fri, 27 Oct 2017 00:41:30 +0530 Subject: [PATCH 03/15] rna-transcription: Update tests to version 1.0.1 to resolve #1020 --- exercises/rna-transcription/rna_transcription_test.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/exercises/rna-transcription/rna_transcription_test.py b/exercises/rna-transcription/rna_transcription_test.py index d0788494ae2..e413daae36d 100644 --- a/exercises/rna-transcription/rna_transcription_test.py +++ b/exercises/rna-transcription/rna_transcription_test.py @@ -3,15 +3,16 @@ from rna_transcription import to_rna -# test cases adapted from `x-common//canonical-data.json` @ version: 1.0.0 +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.1 class DNATests(unittest.TestCase): - def test_transcribes_guanine_to_cytosine(self): - self.assertEqual(to_rna('G'), 'C') def test_transcribes_cytosine_to_guanine(self): self.assertEqual(to_rna('C'), 'G') + def test_transcribes_guanine_to_cytosine(self): + self.assertEqual(to_rna('G'), 'C') + def test_transcribes_thymine_to_adenine(self): self.assertEqual(to_rna('T'), 'A') From 424f7fc1182f2c0a0c7b1b123aa7c41996aef0a5 Mon Sep 17 00:00:00 2001 From: Jack Mustacato Date: Thu, 26 Oct 2017 16:52:35 -0400 Subject: [PATCH 04/15] wordy: add test version and updated tests to match canonical data --- exercises/wordy/wordy_test.py | 68 ++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/exercises/wordy/wordy_test.py b/exercises/wordy/wordy_test.py index 588f1904816..e967e50586b 100644 --- a/exercises/wordy/wordy_test.py +++ b/exercises/wordy/wordy_test.py @@ -3,54 +3,62 @@ from wordy import calculate +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0 + class WordyTest(unittest.TestCase): - def test_simple_add_1(self): - self.assertEqual(calculate("What is 5 plus 13?"), 18) + def test_addition(self): + self.assertEqual(calculate("What is 1 plus 1?"), 2) - def test_simple_add_2(self): - self.assertEqual(calculate("What is 5 plus -13?"), -8) + def test_more_addition(self): + self.assertEqual(calculate("What is 53 plus 2?"), 55) - def test_simple_sub_1(self): - self.assertEqual(calculate("What is 103 minus 97?"), 6) + def test_addition_with_negative_numbers(self): + self.assertEqual(calculate("What is -1 plus -10?"), -11) - def test_simple_sub_2(self): - self.assertEqual(calculate("What is 97 minus 103?"), -6) + def test_large_addition(self): + self.assertEqual(calculate("What is 123 plus 45678?"), 45801) - def test_simple_mult(self): - self.assertEqual(calculate("What is 7 multiplied by 3?"), 21) + def test_subtraction(self): + self.assertEqual(calculate("What is 4 minus -12?"), 16) - def test_simple_div(self): - self.assertEqual(calculate("What is 45 divided by 5?"), 9) + def test_multiplication(self): + self.assertEqual(calculate("What is -3 multiplied by 25?"), -75) - def test_add_negative_numbers(self): - self.assertEqual(calculate("What is -1 plus -10?"), -11) + def test_division(self): + self.assertEqual(calculate("What is 33 divided by -3?"), -11) - def test_add_more_digits(self): - self.assertEqual(calculate("What is 123 plus 45678?"), 45801) + def test_multiple_addition(self): + self.assertEqual(calculate("What is 1 plus 1 plus 1?"), 3) - def test_add_twice(self): - self.assertEqual(calculate("What is 1 plus 2 plus 1?"), 4) + def test_addition_then_subtraction(self): + self.assertEqual(calculate("What is 1 plus 5 minus -2?"), 8) - def test_add_then_subtract(self): - self.assertEqual(calculate("What is 1 plus 5 minus -8?"), 14) + def test_multiple_subtraction(self): + self.assertEqual(calculate("What is 20 minus 4 minus 13?"), 3) - def test_subtract_twice(self): - self.assertEqual(calculate("What is 20 minus 14 minus 13?"), -7) + def test_subtraction_then_addition(self): + self.assertEqual(calculate("What is 17 minus 6 plus 3?"), 14) - def test_multiply_twice(self): + def test_multiple_multiplication(self): self.assertEqual( calculate("What is 2 multiplied by -2 multiplied by 3?"), -12) - def test_add_then_multiply(self): + def test_addition_then_multiplication(self): self.assertEqual(calculate("What is -3 plus 7 multiplied by -2?"), -8) - def test_divide_twice(self): + def test_multiple_division(self): self.assertEqual( - calculate("What is -12000 divided by 25 divided by -30?"), 16) + calculate("What is -12 divided by 2 divided by -3?"), 2) - def test_invalid_operation(self): + def test_unknown_operation(self): with self.assertRaises(ValueError): - calculate("What is 4 xor 7?") + calculate("What is 52 cubed?") + + def test_non_math_question(self): + with self.assertRaises(ValueError): + calculate("Who is the President of the United States?") + + # Additional tests for this track def test_missing_operation(self): with self.assertRaises(ValueError): @@ -60,10 +68,6 @@ def test_missing_number(self): with self.assertRaises(ValueError): calculate("What is 7 plus multiplied by -2?") - def test_irrelevant_question(self): - with self.assertRaises(ValueError): - calculate("Which is greater, 3 or 2?") - if __name__ == '__main__': unittest.main() From ed6ef784b7e826e1f1c198e44fe6e8cd63fc71b3 Mon Sep 17 00:00:00 2001 From: Josix Date: Fri, 27 Oct 2017 08:05:35 -0500 Subject: [PATCH 05/15] word-search: Update tests to version 1.1.0(#1012) (#1053) * word-search: Update tests to version 1.1.0(#1012) * Remove redundant and misplaced assertions --- exercises/word-search/word_search_test.py | 116 +++++++++++++++++++--- 1 file changed, 104 insertions(+), 12 deletions(-) diff --git a/exercises/word-search/word_search_test.py b/exercises/word-search/word_search_test.py index f279638c44c..e2b5f3f3b55 100644 --- a/exercises/word-search/word_search_test.py +++ b/exercises/word-search/word_search_test.py @@ -3,8 +3,111 @@ from word_search import WordSearch, Point +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0 + class WordSearchTests(unittest.TestCase): + def test_initial_game_grid(self): + puzzle = 'jefblpepre' + searchAnswer = WordSearch(puzzle).search('clojure') + self.assertIsNone(searchAnswer) + + def test_left_to_right_word(self): + puzzle = 'clojurermt' + searchAnswer = WordSearch(puzzle).search('clojure') + self.assertEqual(searchAnswer, (Point(0, 0), Point(6, 0))) + + def test_left_to_right_word_different_position(self): + puzzle = 'mtclojurer' + searchAnswer = WordSearch(puzzle).search('clojure') + self.assertEqual(searchAnswer, (Point(2, 0), Point(8, 0))) + + def test_different_left_to_right_word(self): + puzzle = 'coffeelplx' + searchAnswer = WordSearch(puzzle).search('coffee') + self.assertEqual(searchAnswer, (Point(0, 0), Point(5, 0))) + + def test_different_left_to_right_word_different_position(self): + puzzle = 'xcoffeezlp' + searchAnswer = WordSearch(puzzle).search('coffee') + self.assertEqual(searchAnswer, (Point(1, 0), Point(6, 0))) + + def test_left_to_right_word_two_lines(self): + puzzle = ('jefblpepre\n' + 'tclojurerm\n') + searchAnswer = WordSearch(puzzle).search('clojure') + self.assertEqual(searchAnswer, (Point(1, 1), Point(7, 1))) + + def test_left_to_right_word_three_lines(self): + puzzle = ('camdcimgtc\n' + 'jefblpepre\n' + 'clojurermt\n') + searchAnswer = WordSearch(puzzle).search('clojure') + self.assertEqual(searchAnswer, (Point(0, 2), Point(6, 2))) + + def test_left_to_right_word_ten_lines(self): + puzzle = ('jefblpepre\n' + 'camdcimgtc\n' + 'oivokprjsm\n' + 'pbwasqroua\n' + 'rixilelhrs\n' + 'wolcqlirpc\n' + 'screeaumgr\n' + 'alxhpburyi\n' + 'jalaycalmp\n' + 'clojurermt\n') + searchAnswer = WordSearch(puzzle).search('clojure') + self.assertEqual(searchAnswer, (Point(0, 9), Point(6, 9))) + + def test_left_to_right_word_ten_lines_different_position(self): + puzzle = ('jefblpepre\n' + 'camdcimgtc\n' + 'oivokprjsm\n' + 'pbwasqroua\n' + 'rixilelhrs\n' + 'wolcqlirpc\n' + 'screeaumgr\n' + 'alxhpburyi\n' + 'clojurermt\n' + 'jalaycalmp\n') + searchAnswer = WordSearch(puzzle).search('clojure') + self.assertEqual(searchAnswer, (Point(0, 8), Point(6, 8))) + + def test_different_left_to_right_word_ten_lines(self): + puzzle = ('jefblpepre\n' + 'camdcimgtc\n' + 'oivokprjsm\n' + 'pbwasqroua\n' + 'rixilelhrs\n' + 'wolcqlirpc\n' + 'fortranftw\n' + 'alxhpburyi\n' + 'clojurermt\n' + 'jalaycalmp\n') + searchAnswer = WordSearch(puzzle).search('fortran') + self.assertEqual(searchAnswer, (Point(0, 6), Point(6, 6))) + + def test_multiple_words(self): + puzzle = ('jefblpepre\n' + 'camdcimgtc\n' + 'oivokprjsm\n' + 'pbwasqroua\n' + 'rixilelhrs\n' + 'wolcqlirpc\n' + 'fortranftw\n' + 'alxhpburyi\n' + 'jalaycalmp\n' + 'clojurermt\n') + searchAnswer = WordSearch(puzzle).search('fortran') + self.assertEqual(searchAnswer, (Point(0, 6), Point(6, 6))) + searchAnswer = WordSearch(puzzle).search('clojure') + self.assertEqual(searchAnswer, (Point(0, 9), Point(6, 9))) + + def test_single_word_right_to_left(self): + puzzle = 'rixilelhrs' + searchAnswer = WordSearch(puzzle).search('elixir') + self.assertEqual(searchAnswer, (Point(5, 0), Point(0, 0))) + @classmethod def setUpClass(self): puzzle = ('jefblpepre\n' @@ -19,13 +122,11 @@ def setUpClass(self): 'clojurermt') self.example = WordSearch(puzzle) - def test_horizontal_words_left_to_right(self): + def test_horizontal_words_different_directions(self): self.assertEqual( self.example.search('clojure'), (Point(0, 9), Point(6, 9)) ) - - def test_horizontal_words_right_to_left(self): self.assertEqual( self.example.search('elixir'), (Point(5, 4), Point(0, 4)) @@ -70,15 +171,6 @@ def test_diagonal_upper_top_right_to_bottom_left(self): def test_words_that_are_not_in_the_puzzle(self): self.assertIsNone(self.example.search('haskell')) - def test_search_differently_sized_puzzles(self): - puzzle = ('qwertyuiopz\n' - 'luamsicrexe\n' - 'abcdefghijk') - self.assertEqual( - WordSearch(puzzle).search('exercism'), - (Point(10, 1), Point(3, 1)) - ) - if __name__ == '__main__': unittest.main() From eb689a59be252296d9791ed6915ef05c1e7ee7be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89lysson=20MR?= Date: Fri, 27 Oct 2017 12:28:45 -0200 Subject: [PATCH 06/15] circular-buffer: Update the tests following the canonical data (#1047) * circular-buffer: Update the tests following the canonical data --- .../circular-buffer/circular_buffer_test.py | 106 +++++++++--------- 1 file changed, 56 insertions(+), 50 deletions(-) diff --git a/exercises/circular-buffer/circular_buffer_test.py b/exercises/circular-buffer/circular_buffer_test.py index 8e49bf77ae1..54cc4a7f039 100644 --- a/exercises/circular-buffer/circular_buffer_test.py +++ b/exercises/circular-buffer/circular_buffer_test.py @@ -7,43 +7,41 @@ ) +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.1 + class CircularBufferTest(unittest.TestCase): def test_read_empty_buffer(self): buf = CircularBuffer(1) with self.assertRaises(BufferEmptyException): buf.read() + def test_read_just_written_item(self): + buf = CircularBuffer(1) + buf.write('1') + self.assertEqual(buf.read(), '1') + def test_write_and_read_back_one_item(self): buf = CircularBuffer(1) buf.write('1') - self.assertEqual('1', buf.read()) + self.assertEqual(buf.read(), '1') with self.assertRaises(BufferEmptyException): buf.read() - def test_write_and_read_back_multiple_items(self): + def test_write_and_read_back_multiple_items_ordered(self): buf = CircularBuffer(2) buf.write('1') buf.write('2') self.assertEqual(buf.read(), '1') self.assertEqual(buf.read(), '2') - with self.assertRaises(BufferEmptyException): - buf.read() - def test_clearing_buffer(self): - buf = CircularBuffer(3) - for c in '123': - buf.write(c) - buf.clear() - with self.assertRaises(BufferEmptyException): - buf.read() + def test_full_buffer_cant_written(self): + buf = CircularBuffer(1) buf.write('1') - buf.write('2') - self.assertEqual(buf.read(), '1') - buf.write('3') - self.assertEqual(buf.read(), '2') + with self.assertRaises(BufferFullException): + buf.write('2') def test_alternate_write_and_read(self): - buf = CircularBuffer(2) + buf = CircularBuffer(1) buf.write('1') self.assertEqual(buf.read(), '1') buf.write('2') @@ -53,56 +51,64 @@ def test_read_back_oldest_item(self): buf = CircularBuffer(3) buf.write('1') buf.write('2') - buf.read() + self.assertEqual(buf.read(), '1') buf.write('3') - buf.read() + self.assertEqual(buf.read(), '2') self.assertEqual(buf.read(), '3') - def test_write_full_buffer(self): - buf = CircularBuffer(2) + def test_clearing_buffer(self): + buf = CircularBuffer(1) buf.write('1') - buf.write('2') - with self.assertRaises(BufferFullException): - buf.write('A') + buf.clear() + with self.assertRaises(BufferEmptyException): + buf.read() - def test_overwrite_full_buffer(self): - buf = CircularBuffer(2) + def test_clear_free_buffer_for_write(self): + buf = CircularBuffer(1) buf.write('1') + buf.clear() buf.write('2') - buf.overwrite('A') self.assertEqual(buf.read(), '2') - self.assertEqual(buf.read(), 'A') - with self.assertRaises(BufferEmptyException): - buf.read() + + def test_clear_does_nothin_empty_buffer(self): + buf = CircularBuffer(1) + buf.clear() + buf.write('1') + self.assertEqual(buf.read(), '1') def test_overwrite_non_full_buffer(self): buf = CircularBuffer(2) - buf.overwrite('1') + buf.write('1') buf.overwrite('2') self.assertEqual(buf.read(), '1') self.assertEqual(buf.read(), '2') - with self.assertRaises(BufferEmptyException): - buf.read() - def test_alternate_read_and_overwrite(self): - buf = CircularBuffer(5) - for c in '123': - buf.write(c) - buf.read() - buf.read() + def test_overwrite_replaces_oldest_item(self): + buf = CircularBuffer(2) + buf.write('1') + buf.write('2') + buf.overwrite('3') + self.assertEqual(buf.read(), '2') + self.assertEqual(buf.read(), '3') + + def test_write_full_buffer(self): + buf = CircularBuffer(2) + buf.write('1') + buf.write('2') + with self.assertRaises(BufferFullException): + buf.write('A') + + def test_over_write_replaces_oldest_remaning_item(self): + buf = CircularBuffer(3) + buf.write('1') + buf.write('2') + buf.write('3') + self.assertEqual(buf.read(), '1') buf.write('4') - buf.read() - for c in '5678': - buf.write(c) - buf.overwrite('A') - buf.overwrite('B') - self.assertEqual(buf.read(), '6') - self.assertEqual(buf.read(), '7') - self.assertEqual(buf.read(), '8') - self.assertEqual(buf.read(), 'A') - self.assertEqual(buf.read(), 'B') - with self.assertRaises(BufferEmptyException): - buf.read() + buf.overwrite('5') + self.assertEqual(buf.read(), '3') + self.assertEqual(buf.read(), '4') + self.assertEqual(buf.read(), '5') if __name__ == '__main__': From ce1b386169b65f38605eee4ce507db1aff5e4de4 Mon Sep 17 00:00:00 2001 From: CSJedi Date: Fri, 27 Oct 2017 17:53:25 +0300 Subject: [PATCH 07/15] binary-search: Add test version string (#1024) * Add test version string (tests already up-to-date) --- exercises/binary-search/binary_search_test.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/exercises/binary-search/binary_search_test.py b/exercises/binary-search/binary_search_test.py index c539d15f289..581e2fbc483 100644 --- a/exercises/binary-search/binary_search_test.py +++ b/exercises/binary-search/binary_search_test.py @@ -3,6 +3,8 @@ from binary_search import binary_search +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0 + class BinarySearchTests(unittest.TestCase): def test_finds_value_in_array_with_one_element(self): self.assertEqual(binary_search([6], 6), 0) From 985e099f960588d54a69dd68824f61e2c7525d3e Mon Sep 17 00:00:00 2001 From: Nathan Parsons Date: Fri, 27 Oct 2017 16:46:37 +0100 Subject: [PATCH 08/15] forth: Fix errors in tests (#1063) * forth: Fix errors in tests * forth: Add empty line test * forth: Update example to work with new tests --- exercises/forth/example.py | 4 +- exercises/forth/forth_test.py | 69 ++++++++++++++++++++++------------- 2 files changed, 46 insertions(+), 27 deletions(-) diff --git a/exercises/forth/example.py b/exercises/forth/example.py index 8d653f4c843..39936283ee9 100644 --- a/exercises/forth/example.py +++ b/exercises/forth/example.py @@ -11,8 +11,10 @@ def is_integer(string): def evaluate(input_data): + if not input_data: + return [] defines = {} - while input_data[0][0] == ':': + while input_data[0][:1] == ':': values = input_data.pop(0).split() values.pop() values.pop(0) diff --git a/exercises/forth/forth_test.py b/exercises/forth/forth_test.py index 53fff3b0918..0cc7563b033 100644 --- a/exercises/forth/forth_test.py +++ b/exercises/forth/forth_test.py @@ -4,13 +4,30 @@ # Tests adapted from `problem-specifications//canonical-data.json` @ v1.2.0 +# Tests for case-insensitivity are track-specific + +class ForthParsingTest(unittest.TestCase): + def test_empty_input_empty_stack(self): + input_data = [] + expected = [] + self.assertEqual(evaluate(input_data), expected) + + def test_empty_line_empty_stack(self): + input_data = [""] + expected = [] + self.assertEqual(evaluate(input_data), expected) + + def test_numbers_just_get_pushed_to_stack(self): + input_data = ["1 2 3 4 5"] + expected = [1, 2, 3, 4, 5] + self.assertEqual(evaluate(input_data), expected) class ForthAdditionTest(unittest.TestCase): def test_can_add_two_numbers(self): input_data = ["1 2 +"] expected = [3] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_errors_if_there_is_nothing_on_the_stack(self): input_data = ["+"] @@ -27,7 +44,7 @@ class ForthSubtractionTest(unittest.TestCase): def test_can_subtract_two_numbers(self): input_data = ["3 4 -"] expected = [-1] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_errors_if_there_is_nothing_on_the_stack(self): input_data = ["-"] @@ -44,7 +61,7 @@ class ForthMultiplicationTest(unittest.TestCase): def test_can_multiply_two_numbers(self): input_data = ["2 4 *"] expected = [8] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_errors_if_there_is_nothing_on_the_stack(self): input_data = ["*"] @@ -59,14 +76,14 @@ def test_errors_if_there_is_only_one_value_on_the_stack(self): class ForthDivisionTest(unittest.TestCase): def test_can_divide_two_numbers(self): - input_data = ["3 4 -"] - expected = [-1] - self.assertEqual(expected, evaluate(input_data)) + input_data = ["12 3 /"] + expected = [4] + self.assertEqual(evaluate(input_data), expected) def test_performs_integer_division(self): input_data = ["8 3 /"] expected = [2] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_errors_if_dividing_by_zero(self): input_data = ["4 0 /"] @@ -88,24 +105,24 @@ class ForthCombinedArithmeticTest(unittest.TestCase): def test_addition_and_subtraction(self): input_data = ["1 2 + 4 -"] expected = [-1] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_multiplication_and_division(self): input_data = ["2 4 * 3 /"] expected = [2] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) class ForthDupTest(unittest.TestCase): def test_copies_the_top_value_on_the_stack(self): input_data = ["1 DUP"] expected = [1, 1] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_is_case_insensitive(self): input_data = ["1 2 Dup"] expected = [1, 2, 2] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_errors_if_there_is_nothing_on_the_stack(self): input_data = ["dup"] @@ -117,17 +134,17 @@ class ForthDropTest(unittest.TestCase): def test_removes_the_top_value_on_the_stack_if_it_is_the_only_one(self): input_data = ["1 DROP"] expected = [] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_removes_the_top_value_on_the_stack_if_it_not_the_only_one(self): input_data = ["3 4 DROP"] expected = [3] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_is_case_insensitive(self): input_data = ["1 2 Drop"] expected = [1] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_errors_if_there_is_nothing_on_the_stack(self): input_data = ["drop"] @@ -139,17 +156,17 @@ class ForthSwapTest(unittest.TestCase): def test_swaps_only_two_values_on_stack(self): input_data = ["1 2 SWAP"] expected = [2, 1] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_swaps_two_two_values_on_stack(self): input_data = ["1 2 3 SWAP"] expected = [1, 3, 2] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_is_case_insensitive(self): input_data = ["3 4 Swap"] expected = [4, 3] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_errors_if_there_is_nothing_on_the_stack(self): input_data = ["swap"] @@ -166,17 +183,17 @@ class ForthOverTest(unittest.TestCase): def test_copies_the_second_element_if_there_are_only_two(self): input_data = ["1 2 OVER"] expected = [1, 2, 1] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_copies_the_second_element_if_there_are_more_than_two(self): input_data = ["1 2 3 OVER"] expected = [1, 2, 3, 2] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_is_case_insensitive(self): input_data = ["3 4 Over"] expected = [3, 4, 3] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_errors_if_there_is_nothing_on_the_stack(self): input_data = ["over"] @@ -196,7 +213,7 @@ def test_can_consist_of_built_in_words(self): "1 dup-twice" ] expected = [1, 1, 1] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_execute_in_the_right_order(self): input_data = [ @@ -204,7 +221,7 @@ def test_execute_in_the_right_order(self): "countup" ] expected = [1, 2, 3] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_can_override_other_user_defined_words(self): input_data = [ @@ -213,7 +230,7 @@ def test_can_override_other_user_defined_words(self): "1 foo" ] expected = [1, 1, 1] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_can_override_built_in_words(self): input_data = [ @@ -221,7 +238,7 @@ def test_can_override_built_in_words(self): "1 swap" ] expected = [1, 1] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_can_override_built_in_operators(self): input_data = [ @@ -229,7 +246,7 @@ def test_can_override_built_in_operators(self): "3 4 +" ] expected = [12] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_is_case_insensitive(self): input_data = [ @@ -237,7 +254,7 @@ def test_is_case_insensitive(self): "1 FOO" ] expected = [1, 1] - self.assertEqual(expected, evaluate(input_data)) + self.assertEqual(evaluate(input_data), expected) def test_cannot_redefine_numbers(self): input_data = [": 1 2 ;"] From ccfd5dc7e218ad3ef3926a0342267369cec0d258 Mon Sep 17 00:00:00 2001 From: Nathan Parsons Date: Fri, 27 Oct 2017 17:24:10 +0100 Subject: [PATCH 09/15] word-count: Update example to work with new tests (#1059) --- exercises/word-count/example.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/exercises/word-count/example.py b/exercises/word-count/example.py index 995a7d6ca83..9e93a159111 100644 --- a/exercises/word-count/example.py +++ b/exercises/word-count/example.py @@ -1,8 +1,10 @@ +import re + from collections import Counter +WORDS = re.compile("[a-z0-9]+(['][a-z]+)?") + + def word_count(text): - def replace_nonalpha(char): - return char.lower() if char.isalnum() else ' ' - text = ''.join(replace_nonalpha(c) for c in text) - return Counter(text.split()) + return Counter(word.group(0) for word in WORDS.finditer(text.lower())) From 3dcbc2e57682febc82e8a98d73e459c84d4db828 Mon Sep 17 00:00:00 2001 From: Jack Mustacato Date: Fri, 27 Oct 2017 12:38:59 -0400 Subject: [PATCH 10/15] word-count: added test version and updated tests to match canonical (#1044) * Added version number and updated tests to match canonical data. * Changed argument order for the tests. * Updated mixed case test --- exercises/word-count/word_count_test.py | 71 +++++++++++++++++-------- 1 file changed, 49 insertions(+), 22 deletions(-) diff --git a/exercises/word-count/word_count_test.py b/exercises/word-count/word_count_test.py index bb261e47133..73fe562c81c 100644 --- a/exercises/word-count/word_count_test.py +++ b/exercises/word-count/word_count_test.py @@ -7,66 +7,93 @@ class WordCountTests(unittest.TestCase): def test_count_one_word(self): self.assertEqual( - {'word': 1}, - word_count('word') + word_count('word'), + {'word': 1} ) def test_count_one_of_each(self): self.assertEqual( - {'one': 1, 'of': 1, 'each': 1}, - word_count('one of each') + word_count('one of each'), + {'one': 1, 'of': 1, 'each': 1} ) def test_count_multiple_occurences(self): self.assertEqual( - {'one': 1, 'fish': 4, 'two': 1, 'red': 1, 'blue': 1}, - word_count('one fish two fish red fish blue fish') + word_count('one fish two fish red fish blue fish'), + {'one': 1, 'fish': 4, 'two': 1, 'red': 1, 'blue': 1} + ) + + def test_cramped_list(self): + self.assertEqual( + word_count('one,two,three'), + {'one': 1, 'two': 1, 'three': 1} + ) + + def test_expanded_list(self): + self.assertEqual( + word_count('one,\ntwo,\nthree'), + {'one': 1, 'two': 1, 'three': 1} ) def test_ignores_punctuation(self): self.assertEqual( - {'car': 1, 'carpet': 1, 'as': 1, 'java': 1, 'javascript': 1}, - word_count('car : carpet as java : javascript!!&@$%^&') + word_count('car : carpet as java : javascript!!&@$%^&'), + {'car': 1, 'carpet': 1, 'as': 1, 'java': 1, 'javascript': 1} ) def test_include_numbers(self): self.assertEqual( - {'testing': 2, '1': 1, '2': 1}, - word_count('testing 1 2 testing') + word_count('testing 1 2 testing'), + {'testing': 2, '1': 1, '2': 1} ) def test_mixed_case(self): self.assertEqual( - [2, 3], - sorted(list(word_count('go Go GO Stop stop').values())) + word_count('go Go GO Stop stop'), + {'go': 3, 'stop': 2} + ) + + def test_apostrophes(self): + self.assertEqual( + word_count("First: don't laugh. Then: don't cry."), + {'first': 1, "don't": 2, 'laugh': 1, 'then': 1, 'cry': 1} ) + def test_quotations(self): + self.assertEqual( + word_count("Joe can't tell between 'large' and large."), + {'joe': 1, "can't": 1, 'tell': 1, 'between': 1, 'large': 2, + 'and': 1} + ) + + # Additional tests for this track + def test_multiple_spaces(self): self.assertEqual( - {'wait': 1, 'for': 1, 'it': 1}, - word_count('wait for it') + word_count('wait for it'), + {'wait': 1, 'for': 1, 'it': 1} ) def test_newlines(self): self.assertEqual( - {'rah': 2, 'ah': 3, 'roma': 2, 'ma': 1, 'ga': 2, 'oh': 1, 'la': 2, - 'want': 1, 'your': 1, 'bad': 1, 'romance': 1}, word_count('rah rah ah ah ah\nroma roma ma\n' - 'ga ga oh la la\nwant your bad romance') + 'ga ga oh la la\nwant your bad romance'), + {'rah': 2, 'ah': 3, 'roma': 2, 'ma': 1, 'ga': 2, 'oh': 1, 'la': 2, + 'want': 1, 'your': 1, 'bad': 1, 'romance': 1} ) def test_tabs(self): self.assertEqual( - {'rah': 2, 'ah': 3, 'roma': 2, 'ma': 1, 'ga': 2, 'oh': 1, 'la': 2, - 'want': 1, 'your': 1, 'bad': 1, 'romance': 1}, word_count('rah rah ah ah ah\troma roma ma\tga ga oh la la\t' - 'want your bad romance') + 'want your bad romance'), + {'rah': 2, 'ah': 3, 'roma': 2, 'ma': 1, 'ga': 2, 'oh': 1, 'la': 2, + 'want': 1, 'your': 1, 'bad': 1, 'romance': 1} ) def test_non_alphanumeric(self): self.assertEqual( - {'hey': 1, 'my': 1, 'spacebar': 1, 'is': 1, 'broken': 1}, - word_count('hey,my_spacebar_is_broken.') + word_count('hey,my_spacebar_is_broken.'), + {'hey': 1, 'my': 1, 'spacebar': 1, 'is': 1, 'broken': 1} ) From 5eaee37e3e91394252720668b1ae3369dbac0af0 Mon Sep 17 00:00:00 2001 From: Sean Malley Date: Thu, 26 Oct 2017 21:29:14 -0700 Subject: [PATCH 11/15] change: update tests to version 1.1.0 Added missing test version comment using format for current test cases. Verified test content order and completion. Closes #990 --- exercises/change/change_test.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/exercises/change/change_test.py b/exercises/change/change_test.py index 44e4f914b11..62da0dbab82 100644 --- a/exercises/change/change_test.py +++ b/exercises/change/change_test.py @@ -2,6 +2,8 @@ from change import find_minimum_coins +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0 + class ChangeTest(unittest.TestCase): def test_single_coin_change(self): From bea5167dbca10390c11217039790fc631988e295 Mon Sep 17 00:00:00 2001 From: Denis Looby Date: Fri, 27 Oct 2017 20:46:14 +0100 Subject: [PATCH 12/15] robot-simulator: Updated test order to match canonical-data.json (#1043) * robot-simulator: Updated test order to match canonical-data.json --- .../robot-simulator/robot_simulator_test.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/exercises/robot-simulator/robot_simulator_test.py b/exercises/robot-simulator/robot_simulator_test.py index 024a3ab5039..687ea8c8622 100644 --- a/exercises/robot-simulator/robot_simulator_test.py +++ b/exercises/robot-simulator/robot_simulator_test.py @@ -3,6 +3,8 @@ from robot_simulator import Robot, NORTH, EAST, SOUTH, WEST +# Tests adapted from `problem-specifications//canonical-data.json` @ v2.0.0 + class RobotTests(unittest.TestCase): def test_init(self): robot = Robot() @@ -32,19 +34,19 @@ def test_advance_positive_north(self): self.assertEqual(robot.coordinates, (0, 1)) self.assertEqual(robot.bearing, NORTH) - def test_advance_positive_east(self): - robot = Robot(EAST, 0, 0) - robot.advance() - self.assertEqual(robot.coordinates, (1, 0)) - self.assertEqual(robot.bearing, EAST) - def test_advance_negative_south(self): robot = Robot(SOUTH, 0, 0) robot.advance() self.assertEqual(robot.coordinates, (0, -1)) self.assertEqual(robot.bearing, SOUTH) - def test_advance_positive_west(self): + def test_advance_positive_east(self): + robot = Robot(EAST, 0, 0) + robot.advance() + self.assertEqual(robot.coordinates, (1, 0)) + self.assertEqual(robot.bearing, EAST) + + def test_advance_negative_west(self): robot = Robot(WEST, 0, 0) robot.advance() self.assertEqual(robot.coordinates, (-1, 0)) From ae8122c5b07a285920cde6faf4b0414c6494be95 Mon Sep 17 00:00:00 2001 From: Mohamed Elsharnouby Date: Fri, 27 Oct 2017 16:35:05 -0400 Subject: [PATCH 13/15] simple-cipher: Add parameters to exercise placeholder to resolve #640 --- exercises/simple-cipher/simple_cipher.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/exercises/simple-cipher/simple_cipher.py b/exercises/simple-cipher/simple_cipher.py index 6c7bbd1ad43..07b7b5cc311 100644 --- a/exercises/simple-cipher/simple_cipher.py +++ b/exercises/simple-cipher/simple_cipher.py @@ -1,5 +1,11 @@ class Cipher(object): - def __init__(self): + def __init__(self, key=None): + pass + + def encode(self, text): + pass + + def decode(self, text): pass From 5d867eb0e94230955e4d9ca576502b647a98fcbb Mon Sep 17 00:00:00 2001 From: James McMurray Date: Sat, 28 Oct 2017 00:12:57 +0200 Subject: [PATCH 14/15] rna-transcription: Added explanation for handling invalid inputs (#916) --- exercises/rna-transcription/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/exercises/rna-transcription/README.md b/exercises/rna-transcription/README.md index 8be5c1e7568..862e195e3d1 100644 --- a/exercises/rna-transcription/README.md +++ b/exercises/rna-transcription/README.md @@ -18,6 +18,8 @@ each nucleotide with its complement: * `T` -> `A` * `A` -> `U` +Your function will need to be able to handle invalid inputs, both partial (a few invalid bases) and completely invalid strings. In these cases it should return an empty string. + ## Submitting Exercises Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/` directory. @@ -31,5 +33,7 @@ please see the [help page](http://exercism.io/languages/python). Rosalind [http://rosalind.info/problems/rna](http://rosalind.info/problems/rna) +Note that in this problem we are finding the complement, so we also swap for the complementary bases - unlike the Rosalind problem which is just swapping T for U. + ## Submitting Incomplete Solutions It's possible to submit an incomplete solution so you can see how others have completed the exercise. From f28fb0c1a5314f1698b3320a5aaf7ea17d523645 Mon Sep 17 00:00:00 2001 From: Nathan Parsons Date: Sat, 28 Oct 2017 09:40:36 +0100 Subject: [PATCH 15/15] rna-transcription: Handle invalid input by raising a ValueError (#1067) --- exercises/rna-transcription/README.md | 3 ++- exercises/rna-transcription/example.py | 2 +- exercises/rna-transcription/rna_transcription_test.py | 11 +++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/exercises/rna-transcription/README.md b/exercises/rna-transcription/README.md index 862e195e3d1..641b0568cfc 100644 --- a/exercises/rna-transcription/README.md +++ b/exercises/rna-transcription/README.md @@ -18,7 +18,8 @@ each nucleotide with its complement: * `T` -> `A` * `A` -> `U` -Your function will need to be able to handle invalid inputs, both partial (a few invalid bases) and completely invalid strings. In these cases it should return an empty string. +Your function will need to be able to handle invalid inputs by raising a +`ValueError` with a meaningful message. ## Submitting Exercises diff --git a/exercises/rna-transcription/example.py b/exercises/rna-transcription/example.py index a883347fed7..0c882b05c6a 100644 --- a/exercises/rna-transcription/example.py +++ b/exercises/rna-transcription/example.py @@ -13,6 +13,6 @@ def to_rna(dna_strand): valid_chars = set(DNA_CHARS) if any(char not in valid_chars for char in dna_strand): - return '' + raise ValueError("Input DNA strand contains invalid bases") return dna_strand.translate(DNA_TO_RNA) diff --git a/exercises/rna-transcription/rna_transcription_test.py b/exercises/rna-transcription/rna_transcription_test.py index e413daae36d..ca0771005f3 100644 --- a/exercises/rna-transcription/rna_transcription_test.py +++ b/exercises/rna-transcription/rna_transcription_test.py @@ -20,16 +20,19 @@ def test_transcribes_adenine_to_uracil(self): self.assertEqual(to_rna('A'), 'U') def test_transcribes_all_occurences(self): - self.assertMultiLineEqual(to_rna('ACGTGGTCTTAA'), 'UGCACCAGAAUU') + self.assertEqual(to_rna('ACGTGGTCTTAA'), 'UGCACCAGAAUU') def test_correctly_handles_single_invalid_input(self): - self.assertEqual(to_rna('U'), '') + with self.assertRaises(ValueError): + to_rna('U') def test_correctly_handles_completely_invalid_input(self): - self.assertMultiLineEqual(to_rna('XXX'), '') + with self.assertRaises(ValueError): + to_rna('XXX') def test_correctly_handles_partially_invalid_input(self): - self.assertMultiLineEqual(to_rna('ACGTXXXCTTAA'), '') + with self.assertRaises(ValueError): + to_rna('ACGTXXXCTTAA') if __name__ == '__main__':