diff options
-rw-r--r-- | requirements_test.txt | 2 | ||||
-rw-r--r-- | setup_testenv.bash | 4 | ||||
-rw-r--r-- | testing/test_abdl.py | 192 | ||||
-rw-r--r-- | testing/test_examples.py | 9 |
4 files changed, 207 insertions, 0 deletions
diff --git a/requirements_test.txt b/requirements_test.txt new file mode 100644 index 0000000..58f4947 --- /dev/null +++ b/requirements_test.txt @@ -0,0 +1,2 @@ +hypothesis==4.42.7 +pytest==5.2.2 diff --git a/setup_testenv.bash b/setup_testenv.bash new file mode 100644 index 0000000..baf9414 --- /dev/null +++ b/setup_testenv.bash @@ -0,0 +1,4 @@ +python -m venv vtestenv && +. vtestenv/bin/activate && +pip install -r requirements.txt && +pip install -r requirements_test.txt diff --git a/testing/test_abdl.py b/testing/test_abdl.py new file mode 100644 index 0000000..9317643 --- /dev/null +++ b/testing/test_abdl.py @@ -0,0 +1,192 @@ +# Tests abdl.py + +import abdl + +import hypothesis +import hypothesis.strategies as st + +import collections.abc + +import re + +import traceback + +# use abdl's _pairs for consistency. +pairs = abdl._pairs + +# do not put integers, floats, etc here +# do not put bytes, they iterate as integers +hashables = st.deferred(lambda: st.text() | st.frozensets(hashables) | st.lists(hashables).map(tuple)) +values = st.deferred(lambda: hashables | objtree) +objtree = st.deferred(lambda: st.text() | st.dictionaries(hashables, values) | st.lists(values) | st.sets(hashables) | st.lists(hashables).map(tuple)) + +# note: use all() so as to not eat all the RAM :p + +class LogAndCompare: + def __init__(self, left, right): + self._itl = left + self._itr = right + self.left = [] + self.right = [] + def __iter__(self): + return self + def __next__(self): + try: + left = next(self._itl) + except abdl.ValidationError as e: + e.tb = traceback.format_exc() + left = e + try: + right = next(self._itr) + except abdl.ValidationError as e: + e.tb = traceback.format_exc() + right = e + self.left.append(left) + self.right.append(right) + return left == right or (type(left), type(right)) == (abdl.ValidationError,)*2 + def __repr__(self): + return "LogAndCompare(left=" + repr(self.left) + ", right=" + repr(self.right) + ")" + + +@hypothesis.given(objtree, st.just(abdl.compile("->X"))) +def test_basic_iterator(foo, pat): + assert all(LogAndCompare(pat.match(foo), map(lambda x: {"X": x}, pairs(foo)))) + +@hypothesis.given(objtree, st.just(abdl.compile("->X->Y"))) +def test_two_depths(foo, pat): + def deep(foo): + for x in pairs(foo): + for y in pairs(x[1]): + yield {"X": x, "Y": y} + assert all(LogAndCompare(pat.match(foo), deep(foo))) + +@hypothesis.given(objtree, st.just(abdl.compile("->X->Y->Z->W"))) +def test_four_depths(foo, pat): + def deep(foo): + for x in pairs(foo): + for y in pairs(x[1]): + for z in pairs(y[1]): + for w in pairs(z[1]): + yield {"X": x, "Y": y, "Z": z, "W": w} + assert all(LogAndCompare(pat.match(foo), deep(foo))) + +@hypothesis.given(st.dictionaries(st.text(), st.text()) | st.sets(st.text()), st.just(abdl.compile("->/.../"))) +def test_regex(foo, pat): + # no bindings on this one :< + def deep(foo): + for x in pairs(foo): + if re.search("...", x[0]): + yield {} + else: + raise abdl.ValidationError + assert all(LogAndCompare(pat.match(foo), deep(foo))) + +@hypothesis.given(objtree, st.just(abdl.compile("->/.../?"))) +def test_regex_skippable_vs_objtree(foo, pat): + assert all(LogAndCompare(pat.match(foo), ({} for x in pairs(foo) if isinstance(x[0], str) and re.search("...", x[0])))) + +@hypothesis.given(st.dictionaries(st.text(), st.text()) | st.sets(st.text()), st.just(abdl.compile("->/.../->Y"))) +def test_regex_and_bind(foo, pat): + def deep(foo): + for x in pairs(foo): + if re.search("...", x[0]): + for y in pairs(x[1]): + yield {"Y": y} + else: + raise abdl.ValidationError + assert all(LogAndCompare(pat.match(foo), deep(foo))) + +@hypothesis.given(objtree, st.just(abdl.compile("->/.../?->Y"))) +def test_regex_skippable_and_bind_vs_objtree(foo, pat): + def deep(foo): + for x in pairs(foo): + if isinstance(x[0], str) and re.search("...", x[0]): + for y in pairs(x[1]): + yield {"Y": y} + assert all(LogAndCompare(pat.match(foo), deep(foo))) + +@hypothesis.given(objtree, st.just(abdl.compile("->/^...$/?->Y"))) +def test_regex_anchored_skippable_and_bind_vs_objtree(foo, pat): + def deep(foo): + for x in pairs(foo): + if isinstance(x[0], str) and re.search("^...$", x[0]): + for y in pairs(x[1]): + yield {"Y": y} + assert all(LogAndCompare(pat.match(foo), deep(foo))) + +@hypothesis.given(objtree, st.just(abdl.compile("->''?->Y"))) +def test_empty_literal_vs_objtree(foo, pat): + def deep(foo): + for x in pairs(foo): + if x[0] == '': + for y in pairs(x[1]): + yield {"Y": y} + assert all(LogAndCompare(pat.match(foo), deep(foo))) + +defs = {'a': (dict, list, set)} +@hypothesis.given(objtree, st.just(abdl.compile("->X:?$a->Y", defs=defs))) +def test_type(foo, pat): + def deep(foo): + for x in pairs(foo): + if isinstance(x[1], (dict, list, set)): + for y in pairs(x[1]): + yield {"X": x, "Y": y} + assert all(LogAndCompare(pat.match(foo), deep(foo))) + +defs = {'a': (dict, list, set), 'b': (dict, set), 'c': dict} +@hypothesis.given(objtree, st.just(abdl.compile("->X:?$a:?$b:?$c->Y", defs=defs))) +def test_multi_type(foo, pat): + def deep(foo): + for x in pairs(foo): + if isinstance(x[1], dict): + for y in pairs(x[1]): + yield {"X": x, "Y": y} + assert all(LogAndCompare(pat.match(foo), deep(foo))) + +defs = {'a': (dict, list, set), 'b': (dict, set), 'c': dict} +@hypothesis.given(objtree, st.just(abdl.compile("->X:$a:$b:$c->Y", defs=defs))) +@hypothesis.settings(suppress_health_check=[hypothesis.HealthCheck.too_slow]) +def test_multi_type_with_validation_errors(foo, pat): + def deep(foo): + for x in pairs(foo): + if isinstance(x[1], dict): + for y in pairs(x[1]): + yield {"X": x, "Y": y} + else: + raise abdl.ValidationError + assert all(LogAndCompare(pat.match(foo), deep(foo))) + +@hypothesis.given(st.dictionaries(st.frozensets(st.text()), st.text()), st.just(abdl.compile("->(:?$sets->A)->D", {'sets': collections.abc.Set}))) +def test_subtree_partial(foo, pat): + def deep(foo): + for x in pairs(foo): + if isinstance(x[0], collections.abc.Set): + for a in pairs(x[0]): + for d in pairs(x[1]): + yield {"A": a, "D": d} + assert all(LogAndCompare(pat.match(foo), deep(foo))) + +@hypothesis.given(objtree, st.just(abdl.compile("->X->$a->Z", {'a': '0'}))) +def test_param(foo, pat): + def deep(foo): + for x in pairs(foo): + try: + y = x['0'] + except (TypeError, IndexError, KeyError): + raise abdl.ValidationError + else: + for z in pairs(y): + yield {"X": x, "Z": z} + assert all(LogAndCompare(pat.match(foo), deep(foo))) + +# FIXME +#@hypothesis.given(objtree, st.text()) +#def test_exhaustive(foo, pat): +# hypothesis.assume(not re.match("^%s+$", pat)) +# hypothesis.assume(pat) +# try: +# compiled = abdl.compile(pat) +# print(pat) +# except abdl.PatternError: +# hypothesis.assume(False) +# compiled.match(foo) diff --git a/testing/test_examples.py b/testing/test_examples.py new file mode 100644 index 0000000..7341341 --- /dev/null +++ b/testing/test_examples.py @@ -0,0 +1,9 @@ +import abdl + +def test_basic_example(): + for m in abdl.match("->X:?$dict->Y", {"foo": 1, "bar": {"baz": 2}}, {'dict': dict}): + assert m['X'][0] == 'bar' and m['Y'][0] == 'baz' and m['Y'][1] == 2 + +def test_basic_2(): + for m in abdl.match("->'projects':?$d->P/[0-9a-fA-F]{40}|[0-9a-fA-F]{64}/?:?$d->U:?$d->B", {"projects": {"385e734a52e13949a7a5c71827f6de920dbfea43": {"https://soniex2.autistic.space/git-repos/ganarchy.git": {"HEAD": {"active": True}}}}}, {'d': dict}): + assert m['P'][0] == "385e734a52e13949a7a5c71827f6de920dbfea43" and m['U'][0] == "https://soniex2.autistic.space/git-repos/ganarchy.git" and m['B'][0] == "HEAD" and m['B'][1] == {"active": True} |