diff --git a/src/py/empyc/empyc/dataclasses/BUILD b/src/py/empyc/empyc/dataclasses/BUILD index db46e8d6..0eea8b1c 100644 --- a/src/py/empyc/empyc/dataclasses/BUILD +++ b/src/py/empyc/empyc/dataclasses/BUILD @@ -1 +1,5 @@ python_sources() + +python_tests( + name="tests", +) diff --git a/src/py/empyc/empyc/dataclasses/__init__.py b/src/py/empyc/empyc/dataclasses/__init__.py index c71ee2ad..3cc4903f 100644 --- a/src/py/empyc/empyc/dataclasses/__init__.py +++ b/src/py/empyc/empyc/dataclasses/__init__.py @@ -4,6 +4,7 @@ """ +import copy import dataclasses import enum import json @@ -111,9 +112,9 @@ def recursive_from_dict( :param data: the mapping to use as constructor arguments :skip_unknown: if ``True``, will prune invalid keys :returns: an instantiated dataclass object - :raises TypeError: If the instantiation was unsuccesful + :raises TypeError: If the instantiation was unsuccessful """ - return _recursive_instantiate(cls, data, skip_unknown) + return _recursive_instantiate(cls, copy.deepcopy(data), skip_unknown) def _handle_tuple(field, obj, key, value): diff --git a/src/py/empyc/empyc/dataclasses/tests.py b/src/py/empyc/empyc/dataclasses/tests.py new file mode 100644 index 00000000..fac1f41a --- /dev/null +++ b/src/py/empyc/empyc/dataclasses/tests.py @@ -0,0 +1,76 @@ +import copy +import json +import string +from dataclasses import dataclass +from typing import Dict, List + +import pytest + +import empyc.dataclasses + + +def compare_no_ws(a, b): + remove = {ord(c): None for c in string.punctuation + string.whitespace} + + assert a.translate(remove) == b.translate(remove) + + +@dataclass +class Inner: + list_data: List[int] + + +@dataclass +class Outer: + inner_data: Inner + something_else: Dict[str, str] + + +@pytest.fixture +def outer() -> Outer: + return Outer(inner_data=Inner(list_data=[1, 2, 3]), something_else={"hello": "world"}) + + +@pytest.fixture +def outer_dict() -> Outer: + return { + "inner_data": {"list_data": [1, 2, 3]}, + "something_else": {"hello": "world"}, + } + + +@pytest.fixture +def outer_json() -> Outer: + return """{ + "inner_data": {"list_data": [1,2,3]}, + "something_else": {"hello": "world"} +}""" + + +def test_outer_to_json(outer, outer_json): + compare_no_ws(empyc.dataclasses.to_json(outer), outer_json) + + +def test_from_dict_no_modify(outer_dict): + indata = copy.deepcopy(outer_dict) + _ = empyc.dataclasses.recursive_from_dict(Outer, indata) + + assert indata == outer_dict + + +def test_outer_from_dict(outer, outer_dict): + other = empyc.dataclasses.recursive_from_dict(Outer, outer_dict) + assert other == outer + + +def test_outer_from_json(outer, outer_json): + other = empyc.dataclasses.recursive_from_dict(Outer, json.loads(outer_json)) + assert other == outer + + +def test_outer_roundtrip(outer): + other = empyc.dataclasses.recursive_from_dict( + Outer, json.loads(empyc.dataclasses.to_json(outer)) + ) + + assert other == outer diff --git a/src/py/empyc/empyc/tests/BUILD b/src/py/empyc/empyc/tests/BUILD deleted file mode 100644 index dabf212d..00000000 --- a/src/py/empyc/empyc/tests/BUILD +++ /dev/null @@ -1 +0,0 @@ -python_tests() diff --git a/src/py/empyc/empyc/tests/test_functools.py b/src/py/empyc/empyc/tests/test_functools.py deleted file mode 100644 index 6bd0cba0..00000000 --- a/src/py/empyc/empyc/tests/test_functools.py +++ /dev/null @@ -1,56 +0,0 @@ -from dataclasses import dataclass - -from empyc.functools import each, find_by, map_greedy -from empyc.types import Ref - - -def test_greedy(): - assert map_greedy(lambda v: v + 1, range(5)) == list(range(1, 6)) - - -def test_each_modifies(): - d = Ref(0) - assert ( - each( - lambda v: d.__iadd__(v), - range(10), - ) - is None - ) - assert d == 45 - - -def test_find_by(): - data = [1, 2, 3] - found = find_by(data, lambda v: v == 2) - - assert found == 2 - - -def test_find_by_miss(): - data = [1, 2, 3] - found = find_by(data, lambda v: v == 4) - - assert found is None - - -def test_find_by_dc(): - @dataclass - class Foo: - bar: int - - data = [Foo(1), Foo(2), Foo(3)] - found = find_by(data, lambda v: v.bar == 2) - - assert found == data[1] - - -def test_find_by_dc_miss(): - @dataclass - class Foo: - bar: int - - data = [Foo(1), Foo(2), Foo(3)] - found = find_by(data, lambda v: v.bar == 5) - - assert found is None diff --git a/src/py/empyc/empyc/tests/test_itertools.py b/src/py/empyc/empyc/tests/test_itertools.py deleted file mode 100644 index 599b3123..00000000 --- a/src/py/empyc/empyc/tests/test_itertools.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python3 - -""" - -""" - -from empyc import itertools - - -def test_nested(): - data = [[1, 2], [3, 4]] - assert len(list(itertools.flatten(data))) == 4 - - -def test_nested_iter(): - data = [[1, 2], [3, 4]] - assert len(list(itertools.flatten(iter(data)))) == 4 - - -def test_nested_order(): - data = [[1, 2], [3, 4]] - res = itertools.flatten(data) - - assert next(res) == 1 - assert next(res) == 2 - assert next(res) == 3 - assert next(res) == 4 - - -def test_flatten_twice(): - data = [[[1, 2], [3, 4]], [[1, 2], [3, 4]]] - res = itertools.flatten(itertools.flatten(data)) - assert len(list(res)) == 8 - - -def test_flat_map(): - data = [[[1, 2], [3, 4]], [[1, 2], [3, 4]]] - res = itertools.flatmap(lambda v: v + [5], data) - assert list(res) == [[1, 2], [3, 4], 5, [1, 2], [3, 4], 5] - - -def test_map_flat(): - data = [[[1, 2], [3, 4]], [[1, 2], [3, 4]]] - res = itertools.mapflat(lambda v: v + [5], data) - assert list(res) == [[1, 2, 5], [3, 4, 5], [1, 2, 5], [3, 4, 5]] - - -def test_collapse(): - data = [[[1, 2], [3, 4]], [[1, 2], [3, 4]]] - res = itertools.collapse(data) - assert len(list(res)) == 8 - - -def test_collapse_map(): - data = [[[1, 2], [3, 4]], [[1, 2], [3, 4]]] - res = itertools.collapsemap(lambda v: v + [5], data) - assert list(res) == [1, 2, 3, 4, 5, 1, 2, 3, 4, 5] - - -def test_map_collapse(): - data = [[[1, 2], [3, 4]], [[1, 2], [3, 4]]] - res = itertools.mapcollapse(lambda v: v + 1, data) - assert list(res) == [2, 3, 4, 5, 2, 3, 4, 5] - - -def test_first(): - data = [1, 2, 3, 4, 5, 100, 0, 23] - assert itertools.first(lambda k: k == 5, data) == 5 - assert itertools.first(lambda k: k == 72, data) is None - assert itertools.first(lambda k: k == 1, data) == 1 diff --git a/src/py/empyc/empyc/tests/test_random.py b/src/py/empyc/empyc/tests/test_random.py deleted file mode 100644 index 42a374ab..00000000 --- a/src/py/empyc/empyc/tests/test_random.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python3 - -""" - -""" - -from empyc.random import biased_coin_flip, coin_flip - - -def test_coin_flip(): - results = {False: 0, True: 0} - - for _ in range(3000000): - results[coin_flip()] += 1 - - assert abs(results[True] / results[False] - 1.0) < 1e-2 - - -def test_biased_coin_flip_high(): - results = {False: 0, True: 0} - - for _ in range(2500000): - results[biased_coin_flip(0.8)] += 1 - - assert (abs(results[False] / results[True]) - 0.25) < 1e-2 - - -def test_biased_coin_flip_low(): - results = {False: 0, True: 0} - - for _ in range(2500000): - results[biased_coin_flip(0.2)] += 1 - - assert (abs(results[True] / results[False]) - 0.25) < 1e-2 - - -def test_biased_coin_flip_balanced(): - results = {False: 0, True: 0} - - for _ in range(2500000): - results[biased_coin_flip(0.5)] += 1 - - assert (abs(results[True] / results[False]) - 1.0) < 1e-2 diff --git a/src/py/empyc/empyc/tests/test_ref.py b/src/py/empyc/empyc/tests/test_ref.py deleted file mode 100644 index c1e9a36c..00000000 --- a/src/py/empyc/empyc/tests/test_ref.py +++ /dev/null @@ -1,60 +0,0 @@ -import pytest -from empyc.types import Ref - - -@pytest.fixture() -def intref(): - yield Ref(0) - - -class TestIntRef: - def test_increment(self, intref): - data = intref - data += 1 - assert intref.data == 1 - - def test_increment_twice(self, intref): - data = intref - data += 1 - assert intref.data == 1 - - data += 1 - assert intref.data == 2 - - def test_increment_and_add(self, intref): - data = intref - data += 1 - assert intref.data == 1 - - val = data + 1 - assert intref.data == 1 - assert val == 2, "expected 2, got %d" % val - - def test_sub(self, intref): - data = intref - data -= 1 - assert intref.data == -1 - - -@pytest.fixture() -def listref(): - yield Ref([]) - - -class TestListRef: - def test_append(self, listref): - data = listref - data.append(1) - assert len(listref.data) == 1 - - def test_length(self, listref): - assert len(listref) == 0 - - def test_append_length(self, listref): - listref.append(0) - assert len(listref) == 1 - - def test_append_pop(self, listref): - listref.append(1) - listref.pop(0) - assert len(listref) == 0