Skip to content

Commit

Permalink
Merge branch 'master' into drop-python2
Browse files Browse the repository at this point in the history
  • Loading branch information
cmccandless authored Nov 7, 2019
2 parents 3f9f309 + 274f0cb commit c5998dd
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 62 deletions.
28 changes: 28 additions & 0 deletions config/master_template.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{%- import "generator_macros.j2" as macros with context -%}
{{ macros.header(imports=imports, ignore=ignore) }}

{%- macro test_cases_recursive(cases) -%}
{% for case in cases -%}
{% if "cases" in case %}
# {{ case["description"] }}
{{ test_cases_recursive(case["cases"]) }}
{% else %}
{{ test_case(case) }}
{% endif -%}
{% endfor -%}
{% endmacro %}

{% if not additional_tests -%}
{%- macro additional_tests() -%}
{{ test_cases_recursive(additional_cases) }}
{% endmacro %}
{%- endif %}

class {{ exercise | camel_case }}Test(unittest.TestCase):
{{ test_cases_recursive(cases) }}

{% if additional_cases | length -%}
# Additional tests for this track
{{ additional_tests() }}
{%- endif %}
{{ macros.footer() }}
22 changes: 22 additions & 0 deletions exercises/list-ops/.meta/additional_tests.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"cases": [
{
"description": "foldr add string",
"property": "foldr",
"input": {
"list": ["e", "x", "e", "r", "c", "i", "s", "m"],
"initial": "!",
"function": "(x, y) -> x + y"
},
"expected": "exercism!"
},
{
"description": "reverse mixed types",
"property": "reverse",
"input": {
"list": ["xyz", 4.0, "cat", 1]
},
"expected": [1, "cat", 4.0, "xyz"]
}
]
}
58 changes: 58 additions & 0 deletions exercises/list-ops/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{%- import "generator_macros.j2" as macros with context -%}
{% macro lambdify(function) -%}
{% set function = function.replace("(", "", 1).replace(")", "", 1).replace(" ->", ":") %}
{% set function = function.replace("modulo", "%") %}
{% set function = function.replace("/", "//") %}
lambda {{function}}
{%- endmacro %}

{% macro stringify(elem) -%}
{% if elem is string %}
"{{ elem }}"
{%- else -%}
{{ elem }}
{%- endif -%}
{%- endmacro %}

{% macro test_case(case) -%}
{%- set input = case["input"] -%}
def test_{{ case["property"] | to_snake }}_{{ case["description"] | to_snake }}(self):
self.assertEqual(
{%- if case["property"] == "filter" or case["property"] == "map" -%}
list_ops_
{%- endif -%}
{{ case["property"] | to_snake }}(
{%- if case["property"] == "append" -%}
{{ input["list1"] }}, {{ input["list2"] }}
{%- elif case["property"] == "concat" -%}
{{ input["lists"] }}
{%- elif case["property"] == "filter" or case["property"] == "map" -%}
{{ lambdify(input["function"]) }}, {{ input["list"] }}
{%- elif case["property"] == "length" or case["property"] == "reverse" -%}
{{ input["list"] }}
{%- elif case["property"] == "foldl" or case["property"] == "foldr" -%}
{{ lambdify(input["function"]) }}, {{ input["list"] }}, {{ stringify(input["initial"]) }}
{%- endif -%}
),
{{ stringify(case["expected"]) }}
)
{%- endmacro %}
{{ macros.header(imports=["append", "concat", "foldl", "foldr", "length", "reverse", "filter as list_ops_filter", "map as list_ops_map"]) }}

class {{ exercise | camel_case }}Test(unittest.TestCase):
{% for casegroup in cases -%}
{% for case in casegroup["cases"] -%}
{{ test_case(case) }}
{% endfor %}
{% endfor %}
{% if additional_cases | length -%}

# Additional tests for this track

{% for case in additional_cases -%}
{{ test_case(case) }}
{% endfor %}
{%- endif %}


{{ macros.footer() }}
119 changes: 57 additions & 62 deletions exercises/list-ops/list_ops_test.py
Original file line number Diff line number Diff line change
@@ -1,104 +1,99 @@
import unittest
import operator

import list_ops
from list_ops import (
append,
concat,
foldl,
foldr,
length,
reverse,
filter as list_ops_filter,
map as list_ops_map,
)

# Tests adapted from `problem-specifications//canonical-data.json` @ v2.4.1

# Tests adapted from `problem-specifications//canonical-data.json` @ v2.4.0

class ListOpsTest(unittest.TestCase):

# test for append
def test_append_empty_lists(self):
self.assertEqual(list_ops.append([], []), [])
self.assertEqual(append([], []), [])

def test_append_empty_list_to_list(self):
self.assertEqual(list_ops.append([], [1, 2, 3, 4]), [1, 2, 3, 4])
def test_append_list_to_empty_list(self):
self.assertEqual(append([], [1, 2, 3, 4]), [1, 2, 3, 4])

def test_append_nonempty_lists(self):
self.assertEqual(list_ops.append([1, 2], [2, 3, 4, 5]),
[1, 2, 2, 3, 4, 5])
def test_append_non_empty_lists(self):
self.assertEqual(append([1, 2], [2, 3, 4, 5]), [1, 2, 2, 3, 4, 5])

# tests for concat
def test_concat_empty_list(self):
self.assertEqual(list_ops.concat([]), [])
self.assertEqual(concat([]), [])

def test_concat_list_of_lists(self):
self.assertEqual(list_ops.concat([[1, 2], [3], [], [4, 5, 6]]),
[1, 2, 3, 4, 5, 6])
self.assertEqual(concat([[1, 2], [3], [], [4, 5, 6]]), [1, 2, 3, 4, 5, 6])

def test_concat_list_of_nested_lists(self):
self.assertEqual(
list_ops.concat([[[1], [2]], [[3]], [[]], [[4, 5, 6]]]),
[[1], [2], [3], [], [4, 5, 6]])
concat([[[1], [2]], [[3]], [[]], [[4, 5, 6]]]),
[[1], [2], [3], [], [4, 5, 6]],
)

# tests for filter
def test_filter_empty_list(self):
self.assertEqual(list_ops.filter(lambda x: x % 2 == 1, []), [])
self.assertEqual(list_ops_filter(lambda x: x % 2 == 1, []), [])

def test_filter_nonempty_list(self):
self.assertEqual(
list_ops.filter(lambda x: x % 2 == 1, [1, 2, 3, 4, 5]),
[1, 3, 5])
def test_filter_non_empty_list(self):
self.assertEqual(list_ops_filter(lambda x: x % 2 == 1, [1, 2, 3, 5]), [1, 3, 5])

# tests for length
def test_length_empty_list(self):
self.assertEqual(list_ops.length([]), 0)
self.assertEqual(length([]), 0)

def test_length_nonempty_list(self):
self.assertEqual(list_ops.length([1, 2, 3, 4]), 4)
def test_length_non_empty_list(self):
self.assertEqual(length([1, 2, 3, 4]), 4)

# tests for map
def test_map_empty_list(self):
self.assertEqual(list_ops.map(lambda x: x + 1, []), [])
self.assertEqual(list_ops_map(lambda x: x + 1, []), [])

def test_map_nonempty_list(self):
self.assertEqual(list_ops.map(lambda x: x + 1, [1, 3, 5, 7]),
[2, 4, 6, 8])
def test_map_non_empty_list(self):
self.assertEqual(list_ops_map(lambda x: x + 1, [1, 3, 5, 7]), [2, 4, 6, 8])

# tests for foldl
def test_foldl_empty_list(self):
self.assertEqual(list_ops.foldl(operator.mul, [], 2), 2)
self.assertEqual(foldl(lambda x, y: x * y, [], 2), 2)

def test_foldl_nonempty_list_addition(self):
self.assertEqual(list_ops.foldl(operator.add, [1, 2, 3, 4], 5), 15)
def test_foldl_direction_independent_function_applied_to_non_empty_list(self):
self.assertEqual(foldl(lambda x, y: x + y, [1, 2, 3, 4], 5), 15)

def test_foldl_nonempty_list_floordiv(self):
self.assertEqual(list_ops.foldl(operator.floordiv, [2, 5], 5), 0)
def test_foldl_direction_dependent_function_applied_to_non_empty_list(self):
self.assertEqual(foldl(lambda x, y: x // y, [2, 5], 5), 0)

# tests for foldr
def test_foldr_empty_list(self):
self.assertEqual(list_ops.foldr(operator.mul, [], 2), 2)
self.assertEqual(foldr(lambda x, y: x * y, [], 2), 2)

def test_foldr_nonempty_list_addition(self):
self.assertEqual(list_ops.foldr(operator.add, [1, 2, 3, 4], 5), 15)
def test_foldr_direction_independent_function_applied_to_non_empty_list(self):
self.assertEqual(foldr(lambda x, y: x + y, [1, 2, 3, 4], 5), 15)

def test_foldr_nonempty_list_floordiv(self):
self.assertEqual(list_ops.foldr(operator.floordiv, [2, 5], 5), 2)

# additional test for foldr
def test_foldr_add_str(self):
self.assertEqual(
list_ops.foldr(operator.add,
["e", "x", "e", "r", "c", "i", "s", "m"], "!"),
"exercism!")
def test_foldr_direction_dependent_function_applied_to_non_empty_list(self):
self.assertEqual(foldr(lambda x, y: x // y, [2, 5], 5), 2)

# tests for reverse
def test_reverse_empty_list(self):
self.assertEqual(list_ops.reverse([]), [])
self.assertEqual(reverse([]), [])

def test_reverse_nonempty_list(self):
self.assertEqual(list_ops.reverse([1, 3, 5, 7]), [7, 5, 3, 1])
def test_reverse_non_empty_list(self):
self.assertEqual(reverse([1, 3, 5, 7]), [7, 5, 3, 1])

def test_reverse_list_of_lists_not_flattened(self):
self.assertEqual(list_ops.reverse([[1, 2], [3], [], [4, 5, 6]]),
[[4, 5, 6], [], [3], [1, 2]])
def test_reverse_list_of_lists_is_not_flattened(self):
self.assertEqual(
reverse([[1, 2], [3], [], [4, 5, 6]]), [[4, 5, 6], [], [3], [1, 2]]
)

# Additional tests for this track

# additional test for reverse
def test_reverse_mixed_types(self):
def test_foldr_foldr_add_string(self):
self.assertEqual(
list_ops.reverse(["xyz", 4.0, "cat", 1]), [1, "cat", 4.0, "xyz"])
foldr(lambda x, y: x + y, ["e", "x", "e", "r", "c", "i", "s", "m"], "!"),
"exercism!",
)

def test_reverse_reverse_mixed_types(self):
self.assertEqual(reverse(["xyz", 4.0, "cat", 1]), [1, "cat", 4.0, "xyz"])


if __name__ == '__main__':
if __name__ == "__main__":
unittest.main()

0 comments on commit c5998dd

Please sign in to comment.