# Tests abdl.py import abdl import hypothesis import hypothesis.strategies as st import collections.abc import itertools import re import traceback abdl.DeprecationError.enable_key_match_compat = False # use abdl's _pairs for consistency. pairs = abdl._vm._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, *iterables): self.log = [] self._iters = iterables def __iter__(self): def wrap_exc(it): try: for x in it: yield x except abdl.ValidationError as e: yield e for res in itertools.zip_longest(*map(wrap_exc, self._iters), fillvalue=object()): self.log.append(res) yield all(x == res[0] for x in res) or all(type(x) == abdl.ValidationError for x in res) def __repr__(self): return "" @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))) defs = {'a': (dict, list, set), 'b': (dict, set), 'c': dict} @hypothesis.given(objtree, st.just(abdl.compile("->X:?$a:?$b:?$c", defs=defs))) def test_multi_type_at_end(foo, pat): def deep(foo): for x in pairs(foo): if isinstance(x[1], dict): yield {"X": x} 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("->tag[]"))) def test_tagged_key(foo, pat): def deep(foo): for x in pairs(foo): yield {"tag": x} assert all(LogAndCompare(pat.match(foo), deep(foo))) @hypothesis.given(objtree, st.just(abdl.compile("->[]->D"))) def test_key_dummy(foo, pat): def deep(foo): for x in pairs(foo): for d in pairs(x[1]): yield {"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[1]['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))) @hypothesis.given(objtree, st.just(abdl.compile("->tag/.../?"))) def test_tagged_regex(foo, pat): def deep(foo): for x in pairs(foo): if isinstance(x[0], str) and re.search("...", x[0]): yield {"tag": x} assert all(LogAndCompare(pat.match(foo), deep(foo))) @hypothesis.given(objtree, st.just(abdl.compile("->tag'0'?"))) def test_tagged_str(foo, pat): def deep(foo): try: yield {"tag": ('0', foo['0'])} except (TypeError, IndexError, KeyError): pass assert all(LogAndCompare(pat.match(foo), deep(foo))) @hypothesis.given(objtree, st.just(abdl.compile("->tag$?a", {'a': '0'}))) def test_tagged_param(foo, pat): def deep(foo): try: yield {"tag": ('0', foo['0'])} except (TypeError, IndexError, KeyError): pass assert all(LogAndCompare(pat.match(foo), deep(foo))) def test_basic_value_subtree(): matcher = abdl.match("(->foo'foo')(->bar'bar')", {'foo': 1, 'bar': 2}) assert list(matcher) == [{'foo': ('foo', 1), 'bar': ('bar', 2)}] @hypothesis.given(objtree, st.just(abdl.compile("->[:?$sets]->D", {'sets': collections.abc.Set}))) def test_key_predicate(foo, pat): def deep(foo): for x in pairs(foo): if isinstance(x[0], collections.abc.Set): for d in pairs(x[1]): yield {"D": d} assert all(LogAndCompare(pat.match(foo), deep(foo))) @hypothesis.given(objtree, st.just(abdl.compile("->[:?$sets:?$sets->V]->D", {'sets': collections.abc.Set}))) def test_multi_key_predicate_with_values(foo, pat): def deep(foo): for x in pairs(foo): if isinstance(x[0], collections.abc.Set): if isinstance(x[0], collections.abc.Set): for v in pairs(x[0]): for d in pairs(x[1]): yield {"V": v, "D": d} assert all(LogAndCompare(pat.match(foo), deep(foo))) @hypothesis.given(objtree, st.just(abdl.compile("->[:?$sets:?$sets]->D", {'sets': collections.abc.Set}))) def test_multi_key_predicate(foo, pat): def deep(foo): for x in pairs(foo): if isinstance(x[0], collections.abc.Set): if isinstance(x[0], collections.abc.Set): for d in pairs(x[1]): yield {"D": d} assert all(LogAndCompare(pat.match(foo), deep(foo))) @hypothesis.given(objtree, st.just(abdl.compile(""))) def test_empty(foo, pat): def deep(foo): yield {} 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)