summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--requirements_test.txt2
-rw-r--r--setup_testenv.bash4
-rw-r--r--testing/test_abdl.py192
-rw-r--r--testing/test_examples.py9
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}