diff --git a/test_avl.py b/test_avl.py new file mode 100644 index 0000000..8b1970e --- /dev/null +++ b/test_avl.py @@ -0,0 +1,228 @@ +from avl import AVLTree + +import hypothesis +import hypothesis.strategies as st +import logging +import pytest +import random + +logger = logging.getLogger(__name__) + + +def test_empty() -> None: + tree = AVLTree() + + assert tree.root is None + assert tree.is_correct + + +def test_one_node() -> None: + tree = AVLTree() + tree.insert(1) + + assert tree.root is not None + assert 1 == tree.root.value + assert 0 == tree.root.rank + assert tree.root.left is None + assert tree.root.right is None + + assert tree.is_correct + + +@pytest.mark.parametrize("values", [[1, 2], [1, 2, 0]]) +def test_no_rebalance_needed(values): + tree = AVLTree() + + for value in values: + tree.insert(value) + assert tree.is_correct + + +def test_three_nodes_rebalanced(): + tree = AVLTree() + + for value in (1, 2, 3): + print(tree) + tree.insert(value) + + assert tree.is_correct + + +def test_bigger_tree(): + tree = AVLTree() + + for i in range(50): + tree.insert(i) + assert tree.is_correct + + +def test_bigger_tree_reversed(): + tree = AVLTree() + + for i in range(50): + tree.insert(-i) + assert tree.is_correct + + +def test_promote(): + tree = AVLTree() + + for value in (0, 1, -1): + tree.insert(value) + assert tree.is_correct + + +@pytest.mark.parametrize( + "values", + [ + [0, 1, -2, -1, -3], + [0, 1, -2, -1, -3, -4, 4, 9, 7], + [0, 1, -2, -1, -3, -4, 4, 9, 7, 5, -5, 8], + ], +) +def test_rotate(values): + tree = AVLTree() + + for value in values: + tree.insert(value) + assert tree.is_correct + + +@hypothesis.settings(max_examples=1000, deadline=None) +@hypothesis.given(values=st.sets(st.integers())) +def test_insert_random(values): + tree = AVLTree() + + for value in values: + tree.insert(value) + assert tree.is_correct + assert tree.search(value) is not None + + +@st.composite +def delete_strategy(draw): + values = list(draw(st.sets(st.integers()))) + delete_order = values.copy() + random.shuffle(delete_order) + return (values, delete_order) + + +def _report(t_before: str, t_after: str) -> None: + with open("before.dot", "w") as before, open("after.dot", "w") as after: + print(t_before, file=before) + print(t_after, file=after) + + +@hypothesis.settings(max_examples=100000, deadline=None) +@hypothesis.given(config=delete_strategy()) +def test_delete_random(config): + values, delete_order = config + + tree = AVLTree() + + for value in values: + tree.insert(value) + + for value in delete_order: + before = str(tree) + tree.delete(value) + after = str(tree) + + try: + assert tree.is_correct + except AssertionError: + logger.info( + f"[FAIL] Delete {value} from {values} in order {delete_order}" + ) + _report(before, after) + logger.info(f"Before:\n{before}") + logger.info(f"After:\n{after}") + raise + + +# @unittest.skip("Only for replicating hypothesis") +@pytest.mark.parametrize( + "values, delete_order", + [ + ([4, 2, 5, 0, 3], [5, 0, 4, 2, 3]), + ([1, 2, 3, 4, 5], [5]), + ([0, 1, 2, -15, 17, -2, -1], [17, 2, 1, 0, -2, -15, -1]), + ( + [ + 0, + 1, + 2, + 3, + 4, + 128, + 1536, + 7, + 25223, + 1159, + 16, + 32915, + 148, + 147, + 24, + 26, + 220, + -1, + -9, + -8, + -6, + -5, + -4, + -3, + -2, + ], + [ + 24, + 0, + -3, + -9, + 1536, + 128, + 7, + 16, + -4, + -6, + 4, + 2, + -5, + 3, + -1, + 32915, + -8, + 1159, + -2, + 26, + 220, + 25223, + 1, + 147, + 148, + ], + ), + ], +) +def test_delete_minimal(values, delete_order): + tree = AVLTree() + + for value in values: + tree.insert(value) + + for value in delete_order: + before = str(tree) + tree.delete(value) + after = str(tree) + + try: + assert tree.is_correct + except AssertionError: + logger.info( + f"[FAIL] Delete {value} from {values} in order {delete_order}" + ) + _report(before, after) + logger.info(f"Before:\n{before}") + logger.info(f"After:\n{after}") + raise diff --git a/test_node.py b/test_node.py index 373c6c3..849e47a 100644 --- a/test_node.py +++ b/test_node.py @@ -1,4 +1,4 @@ -from node import Node, NodeType +from node import Node def test_default_rank(): @@ -10,6 +10,6 @@ def test_default_rank(): assert node.right is None assert 0 == node.rank - assert 0 == Node.rank(node) + assert 0 == Node.get_rank(node) assert -1 == Node.difference(node) assert (1, 1) == Node.differences(node) diff --git a/test_wavl.py b/test_wavl.py index 50bc276..5d182f5 100644 --- a/test_wavl.py +++ b/test_wavl.py @@ -1,4 +1,3 @@ -from node import Node, NodeType from wavl import WAVLTree import hypothesis @@ -6,7 +5,6 @@ import hypothesis.strategies as st import logging import pytest import random -import unittest logger = logging.getLogger(__name__) @@ -126,7 +124,7 @@ def delete_strategy(draw): return (values, delete_order) -@hypothesis.settings(max_examples=100000, deadline=None) +@hypothesis.settings(max_examples=10000, deadline=None) @hypothesis.given(config=delete_strategy()) def test_delete_random(config): values, delete_order = config @@ -145,7 +143,7 @@ def test_delete_random(config): assert tree.is_correct except AssertionError: logger.info( - f"[FAIL] Deleting of {value} from {values} in order {delete_order}" + f"[FAIL] Delete {value} from {values} in order {delete_order}" ) logger.info(f"Before:\n{before}") logger.info(f"After:\n{after}") @@ -178,7 +176,7 @@ def test_delete_minimal(values, delete_order): assert tree.is_correct except AssertionError: logger.info( - f"[FAIL] Deleting of {value} from {values} in order {delete_order}" + f"[FAIL] Delete {value} from {values} in order {delete_order}" ) logger.info(f"Before:\n{before}") logger.info(f"After:\n{after}")