diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/algorithms/__init__.py b/tests/algorithms/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/algorithms/searching_test.py b/tests/algorithms/searching_test.py new file mode 100644 index 0000000..af47821 --- /dev/null +++ b/tests/algorithms/searching_test.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +"""Tests for `datastructures._searching`.""" +from __future__ import annotations + +from typing import Iterator + +import pytest + +from nrw.algorithms._seraching import ( + breadth_first_search, + depth_first_search, + linear_search, +) +from nrw.datastructures import Edge, Graph, List, Vertex + + +def test_linear_search() -> None: + lst: List[int] = List() + for i in range(5): + lst.append(i) + + assert linear_search(lst, 3) == 3 + assert lst.content == 3 + assert linear_search(lst, 10) == 5 + assert not lst.has_access + + +def test_depth_first_search() -> None: + graph: Graph = Graph() + vertex1: Vertex = Vertex("A") + graph.add_vertex(vertex1) + vertex2: Vertex = Vertex("B") + graph.add_vertex(vertex2) + vertex3: Vertex = Vertex("C") + graph.add_vertex(vertex3) + edge1: Edge = Edge(vertex1, vertex2, 1) + graph.add_edge(edge1) + edge2: Edge = Edge(vertex1, vertex3, 1) + graph.add_edge(edge2) + edge3: Edge = Edge(vertex2, vertex3, 1) + graph.add_edge(edge3) + + expected_result: Iterator[Vertex] = iter((vertex1, vertex2, vertex3)) + result: List[Vertex] = depth_first_search(graph, vertex1) + result.to_first() + while result.has_access: + assert result.content is next(expected_result) + result.next() + + +def test_breadth_first_search() -> None: + graph: Graph = Graph() + vertex1: Vertex = Vertex("A") + graph.add_vertex(vertex1) + vertex2: Vertex = Vertex("B") + graph.add_vertex(vertex2) + vertex3: Vertex = Vertex("C") + graph.add_vertex(vertex3) + edge1: Edge = Edge(vertex1, vertex2, 1) + graph.add_edge(edge1) + edge2: Edge = Edge(vertex1, vertex3, 1) + graph.add_edge(edge2) + edge3: Edge = Edge(vertex2, vertex3, 1) + graph.add_edge(edge3) + + expected_result: Iterator[Vertex] = iter((vertex1, vertex2, vertex3)) + result: List[Vertex] = breadth_first_search(graph, vertex1) + result.to_first() + while result.has_access: + assert result.content is next(expected_result) + result.next() + + +if __name__ == "__main__": + raise SystemExit(pytest.main()) diff --git a/tests/algorithms/sorting_test.py b/tests/algorithms/sorting_test.py new file mode 100644 index 0000000..79ebd3d --- /dev/null +++ b/tests/algorithms/sorting_test.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +"""Tests for `datastructures._sorting`.""" +from __future__ import annotations + +from typing import Callable + +import pytest + +from nrw.algorithms._sorting import ( + bubble_sort, + insertion_sort, + merge_sort, + quick_sort, + selection_sort, +) +from nrw.datastructures import ComparableContentT, List + + +@pytest.fixture() +def empty_list() -> List[int]: + return List() + + +@pytest.fixture() +def sorted_list() -> List[int]: + lst: List[int] = List() + lst.append(1) + lst.append(2) + lst.append(3) + lst.append(3) + lst.append(5) + lst.append(10) + return lst + + +@pytest.fixture() +def unsorted_list() -> List[int]: + lst: List[int] = List() + lst.append(3) + lst.append(1) + lst.append(3) + lst.append(10) + lst.append(5) + return lst + + +def _is_sorted(lst: List[ComparableContentT]) -> bool: + lst.to_first() + while lst.has_access and lst._current.next_node is not None: + previous: ComparableContentT | None = lst.content + lst.next() + if previous > lst.content: # type: ignore[operator] + return False + return True + + +def _have_same_elements( + lst1: List[ComparableContentT], + lst2: List[ComparableContentT], +) -> bool: + elements_of_lst1: list[ComparableContentT] = [] + lst1.to_first() + while lst1.has_access: + elements_of_lst1.append(lst1.content) # type: ignore[arg-type] + lst1.next() + + elements_of_lst2: list[ComparableContentT] = [] + lst2.to_first() + while lst2.has_access: + elements_of_lst2.append(lst2.content) # type: ignore[arg-type] + lst2.next() + + return sorted(elements_of_lst1) == sorted(elements_of_lst2) + + +def _copy(lst: List[ComparableContentT]) -> List[ComparableContentT]: + copy: List[ComparableContentT] = List() + lst.to_first() + while lst.has_access: + copy.append(lst.content) + lst.next() + return copy + + +def test_is_sorted_on_empty_list(empty_list: List[int]) -> None: + assert _is_sorted(empty_list) + + +def test_is_sorted_on_sorted_list(sorted_list: List[int]) -> None: + assert _is_sorted(sorted_list) + + +def test_is_sorted_on_unsorted_list(unsorted_list: List[int]) -> None: + assert not _is_sorted(unsorted_list) + + +def test_have_same_elements() -> None: + lst1: List[int] = List() + lst1.append(1) + lst1.append(2) + lst1.append(3) + + lst2: List[int] = List() + lst2.append(3) + lst2.append(1) + lst2.append(2) + + assert _have_same_elements(lst1, lst2) + + assert _have_same_elements(List[int](), List[int]()) + assert not _have_same_elements(lst1, List[int]()) + + +@pytest.mark.parametrize( + "sorting_algorithm", + [bubble_sort, insertion_sort, merge_sort, quick_sort, selection_sort], +) +def test_sorting_algorithm_on_empty_list( + sorting_algorithm: Callable[[List[int]], List[int]], + empty_list: List[int], +) -> None: + newly_sorted_list: List[int] = sorting_algorithm(empty_list) + assert _is_sorted(newly_sorted_list) + assert _have_same_elements(newly_sorted_list, empty_list) + + +@pytest.mark.parametrize( + "sorting_algorithm", + [bubble_sort, insertion_sort, merge_sort, quick_sort, selection_sort], +) +def test_sorting_algorithm_on_sorted_list( + sorting_algorithm: Callable[[List[int]], List[int]], + sorted_list: List[int], +) -> None: + newly_sorted_list: List[int] = sorting_algorithm(_copy(sorted_list)) + assert _is_sorted(newly_sorted_list) + assert _have_same_elements(newly_sorted_list, sorted_list) + + +@pytest.mark.parametrize( + "sorting_algorithm", + [bubble_sort, insertion_sort, merge_sort, quick_sort, selection_sort], +) +def test_sorting_algorithm_on_unsorted_list( + sorting_algorithm: Callable[[List[int]], List[int]], + unsorted_list: List[int], +) -> None: + newly_sorted_list: List[int] = sorting_algorithm(_copy(unsorted_list)) + assert _is_sorted(newly_sorted_list) + assert _have_same_elements(newly_sorted_list, unsorted_list) + + +if __name__ == "__main__": + raise SystemExit(pytest.main()) diff --git a/tests/algorithms/traversal_test.py b/tests/algorithms/traversal_test.py new file mode 100644 index 0000000..30256a4 --- /dev/null +++ b/tests/algorithms/traversal_test.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +"""Tests for `datastructures._traversal`.""" +from __future__ import annotations + +from typing import TYPE_CHECKING, Callable, Iterator + +import pytest + +from nrw.algorithms._traversal import ( + inorder, + levelorder, + postorder, + preorder, + reverse_inorder, +) +from nrw.datastructures import BinarySearchTree, BinaryTree + +if TYPE_CHECKING: + from nrw.datastructures import List + + +@pytest.fixture() +def bst() -> BinarySearchTree[int]: + tree: BinarySearchTree[int] = BinarySearchTree() + tree.insert(1) + tree.insert(0) + tree.insert(2) + return tree + + +@pytest.fixture() +def binary_tree() -> BinaryTree[int]: + tree: BinaryTree[int] = BinaryTree() + tree.content = 1 + tree.left_tree.content = 0 + tree.right_tree.content = 2 + return tree + + +@pytest.mark.parametrize("empty_tree", [BinaryTree[int](), BinarySearchTree[int]()]) +@pytest.mark.parametrize( + "traverse", + [inorder, postorder, preorder, reverse_inorder, levelorder], +) +def test_traversal_on_empty_tree( + empty_tree: BinaryTree[int] | BinarySearchTree[int], + traverse: Callable[[BinaryTree[int] | BinarySearchTree[int]], List[int]], +) -> None: + assert traverse(empty_tree).is_empty + + +def test_inorder_traversal_on_binary_tree(binary_tree: BinaryTree[int]) -> None: + expected_result: Iterator[int] = iter((0, 1, 2)) + result: List[int] = inorder(binary_tree) + + result.to_first() + while result.has_access: + assert result.content == next(expected_result) + result.next() + + +def test_postorder_traversal_on_binary_tree(binary_tree: BinaryTree[int]) -> None: + expected_result: Iterator[int] = iter((0, 2, 1)) + result: List[int] = postorder(binary_tree) + + result.to_first() + while result.has_access: + assert result.content == next(expected_result) + result.next() + + +def test_preorder_traversal_on_binary_tree(binary_tree: BinaryTree[int]) -> None: + expected_result: Iterator[int] = iter((1, 0, 2)) + result: List[int] = preorder(binary_tree) + + result.to_first() + while result.has_access: + assert result.content == next(expected_result) + result.next() + + +def test_reverse_inorder_traversal_on_binary_tree(binary_tree: BinaryTree[int]) -> None: + expected_result: Iterator[int] = iter((2, 1, 0)) + result: List[int] = reverse_inorder(binary_tree) + + result.to_first() + while result.has_access: + assert result.content == next(expected_result) + result.next() + + +def test_levelorder_traversal_on_binary_tree(binary_tree: BinaryTree[int]) -> None: + expected_result: Iterator[int] = iter((1, 0, 2)) + result: List[int] = levelorder(binary_tree) + + result.to_first() + while result.has_access: + assert result.content == next(expected_result) + result.next() + + +def test_inorder_traversal_on_binary_search_tree(bst: BinarySearchTree[int]) -> None: + expected_result: Iterator[int] = iter((0, 1, 2)) + result: List[int] = inorder(bst) + + result.to_first() + while result.has_access: + assert result.content == next(expected_result) + result.next() + + +def test_postorder_traversal_on_binary_search_tree(bst: BinarySearchTree[int]) -> None: + expected_result: Iterator[int] = iter((0, 2, 1)) + result: List[int] = postorder(bst) + + result.to_first() + while result.has_access: + assert result.content == next(expected_result) + result.next() + + +def test_preorder_traversal_on_binary_search_tree(bst: BinarySearchTree[int]) -> None: + expected_result: Iterator[int] = iter((1, 0, 2)) + result: List[int] = preorder(bst) + + result.to_first() + while result.has_access: + assert result.content == next(expected_result) + result.next() + + +def test_reverse_inorder_traversal_on_binary_search_tree( + bst: BinarySearchTree[int], +) -> None: + expected_result: Iterator[int] = iter((2, 1, 0)) + result: List[int] = reverse_inorder(bst) + + result.to_first() + while result.has_access: + assert result.content == next(expected_result) + result.next() + + +def test_levelorder_traversal_on_binary_search_tree( + bst: BinarySearchTree[int], +) -> None: + expected_result: Iterator[int] = iter((1, 0, 2)) + result: List[int] = levelorder(bst) + + result.to_first() + while result.has_access: + assert result.content == next(expected_result) + result.next() + + +if __name__ == "__main__": + raise SystemExit(pytest.main()) diff --git a/tests/datastructures/__init__.py b/tests/datastructures/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/datastructures/binary_search_tree_test.py b/tests/datastructures/binary_search_tree_test.py new file mode 100644 index 0000000..b05cd45 --- /dev/null +++ b/tests/datastructures/binary_search_tree_test.py @@ -0,0 +1,298 @@ +#!/usr/bin/env python3 +"""Tests for `datastructures._binary_search_tree`.""" +from __future__ import annotations + +import pytest + +from nrw.datastructures._binary_search_tree import BinarySearchTree, _BSTNode + + +@pytest.fixture() +def empty_bst() -> BinarySearchTree[int]: + return BinarySearchTree() + + +@pytest.fixture() +def sample_bst() -> BinarySearchTree[int]: + bst: BinarySearchTree[int] = BinarySearchTree() + bst._node = _BSTNode(1) + bst._node._left._node = _BSTNode(0) + bst._node._right._node = _BSTNode(2) + return bst + + +def test_bstnode_slots() -> None: + assert _BSTNode.__slots__ == ("_content", "_left", "_right") + + +def test_bstnode_construction() -> None: + node: _BSTNode[int] = _BSTNode(1) + + assert node._content == 1 + + assert isinstance(node._left, BinarySearchTree) + assert node._left.is_empty + + assert isinstance(node._right, BinarySearchTree) + assert node._right.is_empty + + +def test_bst_slots() -> None: + assert BinarySearchTree.__slots__ == ("_node",) + + +def test_bst_construction(empty_bst: BinarySearchTree[int]) -> None: + assert empty_bst._node is None + + +def test_is_empty_on_empty_bst(empty_bst: BinarySearchTree[int]) -> None: + assert empty_bst.is_empty + + +def test_is_empty_on_non_empty_bst(sample_bst: BinarySearchTree[int]) -> None: + assert not sample_bst.is_empty + + +def test_content_property_on_empty_bst(empty_bst: BinarySearchTree[int]) -> None: + assert empty_bst.content is None + + +def test_content_property_on_non_empty_bst( + sample_bst: BinarySearchTree[int], +) -> None: + assert sample_bst.content == 1 + + +def test_left_tree_property_on_empty_bst(empty_bst: BinarySearchTree[int]) -> None: + assert empty_bst.left_tree is None + + +def test_left_tree_property_on_non_empty_bst( + sample_bst: BinarySearchTree[int], +) -> None: + assert isinstance(sample_bst.left_tree, BinarySearchTree) + assert sample_bst.left_tree.content == 0 + + +def test_right_tree_property_on_empty_bst(empty_bst: BinarySearchTree[int]) -> None: + assert empty_bst.right_tree is None + + +def test_right_tree_property_on_non_empty_bst( + sample_bst: BinarySearchTree[int], +) -> None: + assert isinstance(sample_bst.right_tree, BinarySearchTree) + assert sample_bst.right_tree.content == 2 + + +def test_insert(empty_bst: BinarySearchTree[int]) -> None: + assert empty_bst.content is None + assert empty_bst.left_tree is None + assert empty_bst.right_tree is None + + empty_bst.insert(1) + assert empty_bst.content == 1 + assert empty_bst.left_tree.is_empty + assert empty_bst.right_tree.is_empty + + empty_bst.insert(0) + assert empty_bst.content == 1 + assert empty_bst.left_tree.content == 0 + assert empty_bst.left_tree.left_tree.is_empty + assert empty_bst.left_tree.right_tree.is_empty + assert empty_bst.right_tree.is_empty + + empty_bst.insert(2) + assert empty_bst.content == 1 + assert empty_bst.left_tree.content == 0 + assert empty_bst.left_tree.left_tree.is_empty + assert empty_bst.left_tree.right_tree.is_empty + assert empty_bst.right_tree.content == 2 + assert empty_bst.right_tree.left_tree.is_empty + assert empty_bst.right_tree.right_tree.is_empty + + empty_bst.insert(0) + assert empty_bst.content == 1 + assert empty_bst.left_tree.content == 0 + assert empty_bst.left_tree.left_tree.is_empty + assert empty_bst.left_tree.right_tree.is_empty + assert empty_bst.right_tree.content == 2 + assert empty_bst.right_tree.left_tree.is_empty + assert empty_bst.right_tree.right_tree.is_empty + + empty_bst.insert(None) + assert empty_bst.content == 1 + assert empty_bst.left_tree.content == 0 + assert empty_bst.left_tree.left_tree.is_empty + assert empty_bst.left_tree.right_tree.is_empty + assert empty_bst.right_tree.content == 2 + assert empty_bst.right_tree.left_tree.is_empty + assert empty_bst.right_tree.right_tree.is_empty + + +def test_search_in_non_empty_bst( + sample_bst: BinarySearchTree[int], +) -> None: + assert sample_bst.search(1) == 1 + assert sample_bst.search(0) == 0 + assert sample_bst.search(2) == 2 + + assert sample_bst.search(3) is None + + assert sample_bst.search(None) is None + + +def test_search_in_empty_bst( + empty_bst: BinarySearchTree[int], +) -> None: + assert empty_bst.search(1) is None + assert empty_bst.search(None) is None + + +def test_remove_left_leaf(sample_bst: BinarySearchTree[int]) -> None: + assert sample_bst.search(0) == 0 + sample_bst.remove(0) + assert sample_bst.search(0) is None + assert sample_bst.content == 1 + assert sample_bst.left_tree.content is None + assert sample_bst.right_tree.content == 2 + + +def test_remove_right_leaf(sample_bst: BinarySearchTree[int]) -> None: + assert sample_bst.search(2) == 2 + sample_bst.remove(2) + assert sample_bst.search(2) is None + assert sample_bst.content == 1 + assert sample_bst.left_tree.content == 0 + assert sample_bst.right_tree.content is None + + +def test_remove_node_with_only_left_successor( + empty_bst: BinarySearchTree[int], +) -> None: + empty_bst.insert(2) + empty_bst.insert(1) + + assert empty_bst.search(2) == 2 + empty_bst.remove(2) + assert empty_bst.search(2) is None + assert empty_bst.content == 1 + assert empty_bst.right_tree.is_empty + assert empty_bst.left_tree.is_empty + + +def test_remove_node_with_only_right_successor( + empty_bst: BinarySearchTree[int], +) -> None: + empty_bst.insert(1) + empty_bst.insert(2) + + assert empty_bst.search(1) == 1 + empty_bst.remove(1) + assert empty_bst.search(1) is None + assert empty_bst.content == 2 + assert empty_bst.right_tree.is_empty + assert empty_bst.left_tree.is_empty + + +def test_remove_node_with_left_and_right_successors_but_the_right_successors_has_no_left_successors( # noqa: E501 # pylint: disable=C0301 + empty_bst: BinarySearchTree[int], +) -> None: + empty_bst.insert(1) + empty_bst.insert(0) + empty_bst.insert(2) + empty_bst.insert(3) + + assert empty_bst.search(1) == 1 + empty_bst.remove(1) + assert empty_bst.search(1) is None + assert empty_bst.content == 2 + + assert empty_bst.left_tree.content == 0 + assert empty_bst.left_tree.left_tree.is_empty + assert empty_bst.left_tree.right_tree.is_empty + + assert empty_bst.right_tree.content == 3 + assert empty_bst.right_tree.left_tree.is_empty + assert empty_bst.right_tree.right_tree.is_empty + + +def test_remove_node_with_left_and_right_successors_and_the_right_successors_has_a_left_successor( # noqa: E501 # pylint: disable=C0301 + empty_bst: BinarySearchTree[int], +) -> None: + empty_bst.insert(1) + empty_bst.insert(0) + empty_bst.insert(3) + empty_bst.insert(4) + empty_bst.insert(2) + + assert empty_bst.search(1) == 1 + empty_bst.remove(1) + assert empty_bst.search(1) is None + assert empty_bst.content == 2 + + assert empty_bst.left_tree.content == 0 + assert empty_bst.left_tree.left_tree.is_empty + assert empty_bst.left_tree.right_tree.is_empty + + assert empty_bst.right_tree.content == 3 + assert empty_bst.right_tree.right_tree.content == 4 + assert empty_bst.right_tree.left_tree.is_empty + + +def test_remove_from_empty_bst_changes_nothing( + empty_bst: BinarySearchTree[int], +) -> None: + empty_bst.remove(1) + assert empty_bst.is_empty + assert empty_bst.content is None + assert empty_bst.left_tree is None + assert empty_bst.right_tree is None + + +def test_remove_none(sample_bst: BinarySearchTree[int]) -> None: + sample_bst.remove(None) + assert sample_bst.content == 1 + + assert sample_bst.left_tree.content == 0 + assert sample_bst.left_tree.left_tree.is_empty + assert sample_bst.left_tree.right_tree.is_empty + + assert sample_bst.right_tree.content == 2 + assert sample_bst.right_tree.left_tree.is_empty + assert sample_bst.right_tree.right_tree.is_empty + + +def test_ancestor_of_small_right_raises_type_error_on_empty_bst( + empty_bst: BinarySearchTree[int], +) -> None: + with pytest.raises(AttributeError): + empty_bst._ancestor_of_small_right() + + +def test_ancestor_of_small_right_on_non_empty_tree( + sample_bst: BinarySearchTree[int], +) -> None: + assert sample_bst._ancestor_of_small_right() is sample_bst + + sample_bst.insert(-1) + sample_bst.insert(-2) + assert sample_bst._ancestor_of_small_right().content == -1 + + +def test_node_of_left_successor(sample_bst: BinarySearchTree[int]) -> None: + sample_bst.insert(1) + sample_bst.insert(0) + assert sample_bst._node_of_left_successor is sample_bst.left_tree._node + assert sample_bst._node_of_left_successor._content == 0 + + +def test_node_of_right_successor(sample_bst: BinarySearchTree[int]) -> None: + sample_bst.insert(1) + sample_bst.insert(2) + assert sample_bst._node_of_right_successor is sample_bst.right_tree._node + assert sample_bst._node_of_right_successor._content == 2 + + +if __name__ == "__main__": + raise SystemExit(pytest.main()) diff --git a/tests/datastructures/binary_tree_test.py b/tests/datastructures/binary_tree_test.py new file mode 100644 index 0000000..127e3a8 --- /dev/null +++ b/tests/datastructures/binary_tree_test.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +"""Tests for `datastructures._binary_tree`.""" +from __future__ import annotations + +import pytest + +from nrw.datastructures._binary_tree import BinaryTree, _BTNode + + +def test_slots_of_btnode() -> None: + assert _BTNode.__slots__ == ("_content", "_left", "_right") + + +def test_btnode() -> None: + node: _BTNode[int] = _BTNode(42) + + assert node._content == 42 + + assert isinstance(node._left, BinaryTree) + assert node._left.content is None + assert node._left.left_tree is None + assert node._left.right_tree is None + + assert isinstance(node._right, BinaryTree) + assert node._right.content is None + assert node._right.left_tree is None + assert node._right.right_tree is None + + +def test_slots_of_binary_tree() -> None: + assert BinaryTree.__slots__ == ("_node",) + + +def test_binary_tree_construction_with_no_params_and_getters() -> None: + tree: BinaryTree[int] = BinaryTree() + assert tree.content is None + assert tree.left_tree is None + assert tree.right_tree is None + + +def test_binary_tree_construction_with_only_content_as_param_and_getters() -> None: + tree: BinaryTree[int] = BinaryTree(0) + + assert tree.content == 0 + + assert isinstance(tree.left_tree, BinaryTree) + assert tree.left_tree.is_empty + + assert isinstance(tree.right_tree, BinaryTree) + assert tree.right_tree.is_empty + + +def test_binary_tree_construction_with_all_param_and_getters() -> None: + tree: BinaryTree[int] = BinaryTree(0, BinaryTree(1), BinaryTree(2)) + + assert tree.content == 0 + + assert isinstance(tree.left_tree, BinaryTree) + assert tree.left_tree.content == 1 + assert isinstance(tree.left_tree.left_tree, BinaryTree) + assert tree.left_tree.left_tree.is_empty + assert isinstance(tree.left_tree.right_tree, BinaryTree) + assert tree.left_tree.right_tree.is_empty + + assert isinstance(tree.right_tree, BinaryTree) + assert tree.right_tree.content == 2 + assert isinstance(tree.right_tree.left_tree, BinaryTree) + assert tree.right_tree.left_tree.is_empty + assert isinstance(tree.right_tree.right_tree, BinaryTree) + assert tree.right_tree.right_tree.is_empty + + +def test_binary_tree_is_empty() -> None: + tree: BinaryTree[int] = BinaryTree() + assert tree.content is None + assert tree.left_tree is None + assert tree.right_tree is None + assert tree.is_empty + + +def test_set_content_of_empty_binary_tree() -> None: + tree: BinaryTree[int] = BinaryTree() + assert tree.is_empty + + tree.content = 42 + assert not tree.is_empty + assert tree.content == 42 + assert isinstance(tree.left_tree, BinaryTree) + assert tree.left_tree.is_empty + assert isinstance(tree.right_tree, BinaryTree) + assert tree.right_tree.is_empty + + +def test_set_content_of_non_empty_binary_tree_without_subtrees() -> None: + tree: BinaryTree[int] = BinaryTree(0, BinaryTree(1), BinaryTree(2)) + assert not tree.is_empty + assert tree.content == 0 + + tree.content = 42 + assert tree.content == 42 + + assert tree.left_tree.content == 1 + assert tree.left_tree.left_tree.is_empty + assert tree.left_tree.right_tree.is_empty + + assert tree.right_tree.content == 2 + assert tree.right_tree.left_tree.is_empty + assert tree.right_tree.right_tree.is_empty + + +def test_set_content_of_empty_binary_tree_to_none_has_no_effect() -> None: + tree: BinaryTree[int] = BinaryTree() + assert tree.is_empty + assert tree.content is None + tree.content = None + assert tree.content is None + + +@pytest.mark.parametrize( + "tree", + [BinaryTree(0), BinaryTree(0, BinaryTree(1), BinaryTree(2))], +) +def test_set_content_of_non_empty_binary_tree_to_none_has_no_effect( + tree: BinaryTree[int], +) -> None: + assert not tree.is_empty + assert tree.content == 0 + tree.content = None + assert tree.content == 0 + + +def test_set_tree_of_empty_binary_tree() -> None: + tree: BinaryTree[int] = BinaryTree() + assert tree.is_empty + + tree.left_tree = BinaryTree(1) + assert tree.left_tree is None + + tree.right_tree = BinaryTree(2) + assert tree.right_tree is None + + +def test_set_left_tree_of_binary_tree_without_subtrees() -> None: + tree: BinaryTree[int] = BinaryTree(0) + assert isinstance(tree.left_tree, BinaryTree) + assert tree.left_tree.is_empty + + tree.left_tree = BinaryTree(1) + assert isinstance(tree.left_tree, BinaryTree) + assert not tree.left_tree.is_empty + assert tree.left_tree.content == 1 + + +def test_set_right_tree_of_binary_tree_without_subtrees() -> None: + tree: BinaryTree[int] = BinaryTree(0) + assert isinstance(tree.right_tree, BinaryTree) + assert tree.right_tree.is_empty + + tree.right_tree = BinaryTree(1) + assert isinstance(tree.right_tree, BinaryTree) + assert not tree.right_tree.is_empty + assert tree.right_tree.content == 1 + + +def test_set_left_tree_of_binary_tree_with_subtrees() -> None: + tree: BinaryTree[int] = BinaryTree(0, BinaryTree(1), None) + assert isinstance(tree.left_tree, BinaryTree) + assert tree.left_tree.content == 1 + + tree.left_tree = BinaryTree(2) + assert isinstance(tree.left_tree, BinaryTree) + assert tree.left_tree.content == 2 + + +def test_set_right_tree_of_binary_tree_with_subtrees() -> None: + tree: BinaryTree[int] = BinaryTree(0, None, BinaryTree(1)) + assert isinstance(tree.right_tree, BinaryTree) + assert tree.right_tree.content == 1 + + tree.right_tree = BinaryTree(2) + assert isinstance(tree.right_tree, BinaryTree) + assert tree.right_tree.content == 2 + + +if __name__ == "__main__": + raise SystemExit(pytest.main()) diff --git a/tests/datastructures/edge_test.py b/tests/datastructures/edge_test.py new file mode 100644 index 0000000..c800f17 --- /dev/null +++ b/tests/datastructures/edge_test.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +"""Tests for `datastructures._edge`.""" +from __future__ import annotations + +import pytest + +from nrw.datastructures._edge import Edge +from nrw.datastructures._vertex import Vertex + + +def test_edge_construction_and_getters() -> None: + vertex_b: Vertex = Vertex("A") + vertex_a: Vertex = Vertex("B") + edge: Edge = Edge(vertex_a, vertex_b, 1) + assert edge.vertices == (vertex_a, vertex_b) + assert edge.weight == 1 + assert not edge.mark + + +def test_is_marked_property() -> None: + vertex_b: Vertex = Vertex("A") + vertex_a: Vertex = Vertex("B") + edge: Edge = Edge(vertex_a, vertex_b, 1) + assert edge.mark is edge.is_marked + edge.mark = True + assert edge.mark is edge.is_marked + + +def test_set_mark_on_edge() -> None: + vertex_b: Vertex = Vertex("A") + vertex_a: Vertex = Vertex("B") + edge: Edge = Edge(vertex_a, vertex_b, 1) + assert not edge.mark + edge.mark = True + assert edge.mark + + +def test_set_weight_of_edge() -> None: + vertex_b: Vertex = Vertex("A") + vertex_a: Vertex = Vertex("B") + edge: Edge = Edge(vertex_a, vertex_b, 1) + assert edge.weight == 1 + edge.weight = 2 + assert edge.weight == 2 + + +if __name__ == "__main__": + raise SystemExit(pytest.main()) diff --git a/tests/datastructures/graph_test.py b/tests/datastructures/graph_test.py new file mode 100644 index 0000000..92786e4 --- /dev/null +++ b/tests/datastructures/graph_test.py @@ -0,0 +1,407 @@ +#!/usr/bin/env python3 +"""Tests for `datastructures._graph`.""" +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest + +from nrw.datastructures._edge import Edge +from nrw.datastructures._graph import Graph +from nrw.datastructures._vertex import Vertex + +if TYPE_CHECKING: + from nrw.datastructures._list import List + + +@pytest.fixture() +def graph() -> Graph: + return Graph() + + +def test_graph_slots() -> None: + assert Graph.__slots__ == ("_vertices", "_edges") + + +def test_graph_construction(graph: Graph) -> None: + assert graph._vertices.is_empty + assert graph._edges.is_empty + + +def test_is_empty_on_empty_graph(graph: Graph) -> None: + assert graph.is_empty + + +def test_is_empty_on_non_empty_graph(graph: Graph) -> None: + graph._vertices.append(Vertex("A")) + assert not graph.is_empty + + +def test_add_vertex(graph: Graph) -> None: + vertex: Vertex = Vertex("A") + graph.add_vertex(vertex) + graph._vertices.to_first() + assert graph._vertices.content is vertex + + graph.add_vertex(vertex) + graph._vertices.to_first() + assert graph._vertices.content is vertex + graph._vertices.next() + assert not graph._vertices.has_access + + +def test_dont_add_vertex_when_id_is_none(graph: Graph) -> None: + graph.add_vertex(Vertex(None)) # type: ignore[arg-type] + graph._vertices.to_first() + assert graph._vertices.content is None + + +def test_dont_add_vertex_when_vertex_is_none(graph: Graph) -> None: + graph.add_vertex(None) + graph._vertices.to_first() + assert graph._vertices.content is None + + +def test_vertices_property(graph: Graph) -> None: + vertex1: Vertex = Vertex("A") + graph.add_vertex(vertex1) + vertex2: Vertex = Vertex("B") + graph.add_vertex(vertex2) + + copy: List[Vertex] = graph.vertices + copy.to_first() + assert copy.content is vertex1 + copy.next() + assert copy.content is vertex2 + copy.next() + assert not copy.has_access + + assert copy is not graph.vertices + + +def test_get_vertex(graph: Graph) -> None: + vertex1: Vertex = Vertex("A") + graph.add_vertex(vertex1) + + assert graph.get_vertex("A") is vertex1 + assert graph.get_vertex("B") is None + + +def test_add_edge_when_all_conditions_are_meet(graph: Graph) -> None: + vertex1: Vertex = Vertex("A") + graph.add_vertex(vertex1) + vertex2: Vertex = Vertex("B") + graph.add_vertex(vertex2) + edge: Edge = Edge(vertex1, vertex2, 1) + + assert graph._edges.is_empty + graph.add_edge(edge) + + assert not graph._edges.is_empty + graph._edges.to_first() + assert graph._edges.content is edge + + +def test_dont_add_edge_when_one_vertex_is_none(graph: Graph) -> None: + vertex: Vertex = Vertex("A") + graph.add_vertex(vertex) + edge: Edge = Edge(vertex, None, 1) # type: ignore[arg-type] + + assert graph._edges.is_empty + graph.add_edge(edge) + assert graph._edges.is_empty + + +def test_dont_add_edge_when_both_vertecies_are_the_same(graph: Graph) -> None: + vertex: Vertex = Vertex("A") + graph.add_vertex(vertex) + edge: Edge = Edge(vertex, vertex, 1) + + assert graph._edges.is_empty + graph.add_edge(edge) + assert graph._edges.is_empty + + +def test_dont_add_edge_when_one_vertex_is_not_in_graph(graph: Graph) -> None: + vertex1: Vertex = Vertex("A") + graph.add_vertex(vertex1) + vertex2: Vertex = Vertex("B") + edge: Edge = Edge(vertex1, vertex2, 1) + + assert graph._edges.is_empty + graph.add_edge(edge) + assert graph._edges.is_empty + + +def test_dont_add_edge_when_edge_is_none(graph: Graph) -> None: + assert graph._edges.is_empty + graph.add_edge(None) + assert graph._edges.is_empty + graph._edges.to_first() + assert graph._edges.content is None + + +def test_edges_property(graph: Graph) -> None: + vertex1: Vertex = Vertex("A") + graph.add_vertex(vertex1) + vertex2: Vertex = Vertex("B") + graph.add_vertex(vertex2) + edge: Edge = Edge(vertex1, vertex2, 1) + graph.add_edge(edge) + + copy: List[Edge] = graph.edges + copy.to_first() + assert copy.content is edge + copy.next() + assert not copy.has_access + + assert copy is not graph.edges + + +def test_get_edge(graph: Graph) -> None: + vertex1: Vertex = Vertex("A") + graph.add_vertex(vertex1) + vertex2: Vertex = Vertex("B") + graph.add_vertex(vertex2) + edge1: Edge = Edge(vertex1, vertex2, 1) + graph.add_edge(edge1) + assert graph.get_edge(vertex1, vertex2) is edge1 + + +def test_dont_get_edge_when_one_vertex_is_not_in_graph(graph: Graph) -> None: + vertex1: Vertex = Vertex("A") + graph.add_vertex(vertex1) + vertex2: Vertex = Vertex("B") + edge1: Edge = Edge(vertex1, vertex2, 1) + graph.add_edge(edge1) + assert graph.get_edge(vertex1, vertex2) is None + + +def test_dont_get_edge_when_both_vertices_are_not_in_graph(graph: Graph) -> None: + vertex1: Vertex = Vertex("A") + vertex2: Vertex = Vertex("B") + edge1: Edge = Edge(vertex1, vertex2, 1) + graph.add_edge(edge1) + assert graph.get_edge(vertex1, vertex2) is None + + +def test_dont_get_edge_when_one_vertex_is_none(graph: Graph) -> None: + vertex1: Vertex = Vertex("A") + graph.add_vertex(vertex1) + edge1: Edge = Edge(vertex1, None, 1) # type: ignore[arg-type] + graph.add_edge(edge1) + assert graph.get_edge(vertex1, None) is None # type: ignore[arg-type] + + +def test_dont_get_edge_when_vertices_are_none(graph: Graph) -> None: + assert graph.get_edge(None, None) is None # type: ignore[arg-type] + + +def test_remove_vertex_without_edge(graph: Graph) -> None: + vertex: Vertex = Vertex("A") + graph.add_vertex(vertex) + + assert graph.get_vertex("A") is vertex + assert not graph.is_empty + + graph.remove_vertex(vertex) + + assert graph.get_vertex("A") is None + assert graph.is_empty + + +def test_remove_vertex_with_edge(graph: Graph) -> None: + vertex1: Vertex = Vertex("A") + graph.add_vertex(vertex1) + vertex2: Vertex = Vertex("B") + graph.add_vertex(vertex2) + vertex3: Vertex = Vertex("C") + graph.add_vertex(vertex3) + edge: Edge = Edge(vertex1, vertex2, 1) + graph.add_edge(edge) + + assert graph.get_vertex("A") is vertex1 + assert graph.get_vertex("B") is vertex2 + assert graph.get_edge(vertex1, vertex2) is edge + assert not graph.is_empty + + graph.remove_vertex(vertex2) + + assert graph.get_vertex("A") is vertex1 + assert graph.get_vertex("B") is None + assert graph.get_edge(vertex1, vertex2) is None + assert not graph.is_empty + + +def test_remove_vertex_with_edge_that_is_not_connected_to_the_vertex( + graph: Graph, +) -> None: + vertex1: Vertex = Vertex("A") + graph.add_vertex(vertex1) + vertex2: Vertex = Vertex("B") + graph.add_vertex(vertex2) + vertex3: Vertex = Vertex("C") + graph.add_vertex(vertex3) + edge: Edge = Edge(vertex1, vertex2, 1) + graph.add_edge(edge) + + assert graph.get_vertex("C") is vertex3 + graph.remove_vertex(vertex3) + assert graph.get_vertex("C") is None + + +def test_dont_remove_vertex_when_vertex_is_not_in_graph(graph: Graph) -> None: + assert graph.is_empty + graph.remove_vertex(Vertex("A")) + assert graph.is_empty + + +def test_remove_edge_when_edge_in_graph(graph: Graph) -> None: + vertex1: Vertex = Vertex("A") + graph.add_vertex(vertex1) + vertex2: Vertex = Vertex("B") + graph.add_vertex(vertex2) + vertex3: Vertex = Vertex("C") + graph.add_vertex(vertex3) + edge1: Edge = Edge(vertex1, vertex2, 1) + graph.add_edge(edge1) + edge2: Edge = Edge(vertex1, vertex3, 1) + graph.add_edge(edge2) + + assert graph.get_edge(vertex1, vertex2) is edge1 + + graph.remove_edge(edge1) + assert graph.get_edge(vertex1, vertex2) is None + assert graph.get_vertex("A") is vertex1 + assert graph.get_vertex("B") is vertex2 + + +def test_dont_remove_edge_when_edge_is_not_in_graph(graph: Graph) -> None: + assert graph.is_empty + graph.remove_edge(Edge(Vertex("A"), Vertex("B"), 1)) + assert graph.is_empty + + +def test_set_all_vertex_marks(graph: Graph) -> None: + graph.add_vertex(Vertex("A")) + graph.add_vertex(Vertex("B")) + graph.add_vertex(Vertex("C")) + + graph.set_all_vertex_marks(True) + graph._vertices.to_first() + while graph._vertices.has_access: + assert graph._vertices.content.is_marked + graph._vertices.next() + + graph.set_all_vertex_marks(False) + graph._vertices.to_first() + while graph._vertices.has_access: + assert not graph._vertices.content.is_marked + graph._vertices.next() + + +def test_all_vertices_marked(graph: Graph) -> None: + graph.add_vertex(Vertex("A")) + graph.add_vertex(Vertex("B")) + graph.add_vertex(Vertex("C")) + + assert not graph.all_vertices_marked() + + graph.get_vertex("A").mark = True + assert not graph.all_vertices_marked() + + graph.set_all_vertex_marks(True) + assert graph.all_vertices_marked() + + +def test_set_all_edge_marks(graph: Graph) -> None: + vertex1: Vertex = Vertex("A") + graph.add_vertex(vertex1) + vertex2: Vertex = Vertex("B") + graph.add_vertex(vertex2) + vertex3: Vertex = Vertex("C") + graph.add_vertex(vertex3) + + graph.add_edge(Edge(vertex1, vertex2, 1)) + graph.add_edge(Edge(vertex1, vertex3, 1)) + graph.add_edge(Edge(vertex2, vertex3, 1)) + + graph.set_all_edge_marks(True) + graph._edges.to_first() + while graph._edges.has_access: + assert graph._edges.content.is_marked + graph._edges.next() + + graph.set_all_edge_marks(False) + graph._edges.to_first() + while graph._edges.has_access: + assert not graph._edges.content.is_marked + graph._edges.next() + + +def test_all_edges_marked(graph: Graph) -> None: + vertex1: Vertex = Vertex("A") + graph.add_vertex(vertex1) + vertex2: Vertex = Vertex("B") + graph.add_vertex(vertex2) + vertex3: Vertex = Vertex("C") + graph.add_vertex(vertex3) + + graph.add_edge(Edge(vertex1, vertex2, 1)) + graph.add_edge(Edge(vertex1, vertex3, 1)) + graph.add_edge(Edge(vertex2, vertex3, 1)) + + assert not graph.all_edges_marked() + + graph.get_edge(vertex1, vertex2).mark = True + assert not graph.all_edges_marked() + + graph.set_all_edge_marks(True) + assert graph.all_edges_marked() + + +def test_get_neighbours(graph: Graph) -> None: + vertex1: Vertex = Vertex("A") + graph.add_vertex(vertex1) + vertex2: Vertex = Vertex("B") + graph.add_vertex(vertex2) + vertex3: Vertex = Vertex("C") + graph.add_vertex(vertex3) + + graph.add_edge(Edge(vertex1, vertex2, 1)) + graph.add_edge(Edge(vertex3, vertex1, 1)) + graph.add_edge(Edge(vertex2, vertex3, 1)) + + neighbours_of_vertex1: List[Vertex] = graph.get_neighbours(vertex1) + neighbours_of_vertex1.to_first() + while neighbours_of_vertex1.has_access: + assert neighbours_of_vertex1.content is not vertex1 + assert neighbours_of_vertex1.content in {vertex2, vertex3} + neighbours_of_vertex1.next() + + +def test_get_edges(graph: Graph) -> None: + vertex1: Vertex = Vertex("A") + graph.add_vertex(vertex1) + vertex2: Vertex = Vertex("B") + graph.add_vertex(vertex2) + vertex3: Vertex = Vertex("C") + graph.add_vertex(vertex3) + + edge1: Edge = Edge(vertex1, vertex2, 1) + graph.add_edge(edge1) + edge2: Edge = Edge(vertex1, vertex3, 1) + graph.add_edge(edge2) + edge3: Edge = Edge(vertex2, vertex3, 1) + graph.add_edge(edge3) + + edges_of_vertex1: List[Edge] = graph.get_edges(vertex1) + edges_of_vertex1.to_first() + while edges_of_vertex1.has_access: + assert edges_of_vertex1.content is not edge3 + assert edges_of_vertex1.content in {edge1, edge2} + edges_of_vertex1.next() + + +if __name__ == "__main__": + raise SystemExit(pytest.main()) diff --git a/tests/datastructures/list_test.py b/tests/datastructures/list_test.py new file mode 100644 index 0000000..4a18013 --- /dev/null +++ b/tests/datastructures/list_test.py @@ -0,0 +1,323 @@ +#!/usr/bin/env python3 +"""Tests for `datastructures._list`.""" +from __future__ import annotations + +import pytest + +from nrw.datastructures._list import List, _ListNode + + +@pytest.fixture() +def sample_node() -> _ListNode[int]: + return _ListNode(1) + + +@pytest.fixture() +def empty_list() -> List[int]: + return List() + + +@pytest.fixture() +def sample_list() -> List[int]: + lst: List[int] = List() + lst.append(1) + lst.append(2) + lst.append(3) + return lst + + +def test_slots_of_list_node() -> None: + assert _ListNode.__slots__ == ("_content", "_next_node") + + +def test_list_node_creation_and_properties(sample_node: _ListNode[int]) -> None: + assert sample_node.content == 1 + assert sample_node.next_node is None + + +def test_list_node_content_setter(sample_node: _ListNode[int]) -> None: + sample_node.content = 42 + assert sample_node.content == 42 + + +def test_list_node_next_node_property(sample_node: _ListNode[int]) -> None: + assert sample_node.next_node is None + + +def test_list_node_next_node_setter(sample_node: _ListNode[int]) -> None: + new_node = _ListNode(42) + sample_node.next_node = new_node + assert sample_node.next_node == new_node + + +def test_slots_of_list() -> None: + assert List.__slots__ == ("_first", "_last", "_current") + + +def test_is_empty_on_empty_list(empty_list: List[int]) -> None: + assert empty_list.is_empty + assert empty_list.content is None + + +def test_is_empty_on_non_empty_list(sample_list: List[int]) -> None: + assert not sample_list.is_empty + + +def test_next_has_no_effect_on_empty_list(empty_list: List[int]) -> None: + empty_list.next() + assert empty_list.content is None + + +def test_to_first_has_no_effect_on_empty_list(empty_list: List[int]) -> None: + empty_list.to_first() + assert not empty_list.has_access + assert empty_list.content is None + + +def test_to_last_has_no_effect_on_empty_list(empty_list: List[int]) -> None: + empty_list.to_last() + assert not empty_list.has_access + assert empty_list.content is None + + +def test_list_has_no_access_by_default(sample_list: List[int]) -> None: + assert not sample_list.is_empty + assert not sample_list.has_access + assert sample_list.content is None + + +def test_to_first(sample_list: List[int]) -> None: + assert not sample_list.has_access + assert sample_list.content is None + sample_list.to_first() + assert sample_list.has_access + assert sample_list.content == 1 + + +def test_to_last(sample_list: List[int]) -> None: + assert not sample_list.has_access + assert sample_list.content is None + sample_list.to_last() + assert sample_list.has_access + assert sample_list.content == 3 + + +def test_next_from_first(sample_list: List[int]) -> None: + sample_list.to_first() + assert sample_list.has_access + assert sample_list.content == 1 + sample_list.next() + assert sample_list.has_access + assert sample_list.content == 2 + + +def test_list_next_from_last(sample_list: List[int]) -> None: + sample_list.to_last() + assert sample_list.has_access + assert sample_list.content == 3 + sample_list.next() + assert not sample_list.has_access + assert sample_list.content is None + + +def test_while_has_accsess_next_loop(sample_list: List[int]) -> None: + sample_list.to_first() + content: int = 1 + while sample_list.has_access: + assert sample_list.content == content + content += 1 + sample_list.next() + assert not sample_list.has_access + + +def test_set_content_of_list(sample_list: List[int]) -> None: + sample_list.to_first() + sample_list.content = 42 + assert sample_list.content == 42 + + +def test_set_content_of_list_without_access(sample_list: List[int]) -> None: + sample_list.content = 42 + assert not sample_list.has_access + assert sample_list.content is None + sample_list.to_first() + assert sample_list.content == 1 + + +def test_set_content_of_list_to_none(sample_list: List[int]) -> None: + sample_list.to_first() + assert sample_list.content == 1 + sample_list.content = None + assert sample_list.content == 1 + + +def test_insert_at_beginning_of_list(sample_list: List[int]) -> None: + sample_list.to_first() + assert sample_list.content == 1 + sample_list.insert(42) + assert sample_list.content == 1 + sample_list.to_first() + assert sample_list.content == 42 + + +def test_insert_middle_element_of_list(sample_list: List[int]) -> None: + sample_list.to_first() + sample_list.next() + assert sample_list.content == 2 + sample_list.insert(42) + assert sample_list.content == 2 + sample_list.to_first() + sample_list.next() + assert sample_list.content == 42 + + +def test_insert_at_end_of_list(sample_list: List[int]) -> None: + sample_list.to_last() + assert sample_list.content == 3 + sample_list.insert(42) + assert sample_list.content == 3 + sample_list.to_first() + sample_list.next() + sample_list.next() + assert sample_list.content == 42 + + +def test_insert_none_into_list(sample_list: List[int]) -> None: + sample_list.to_first() + sample_list.insert(None) + sample_list.to_first() + assert sample_list.content == 1 + + +def test_insert_into_empty_list(empty_list: List[int]) -> None: + empty_list.insert(42) + assert empty_list.content is None + empty_list.to_first() + assert empty_list.content == 42 + + +def test_insert_into_non_empty_list_without_access(sample_list: List[int]) -> None: + assert not sample_list.has_access + assert not sample_list.is_empty + sample_list.insert(42) + assert sample_list.content is None + + +def test_append_to_empty_list(empty_list: List[int]) -> None: + empty_list.append(1) + empty_list.to_first() + assert empty_list.content == 1 + + +def test_append_to_non_empty_list(sample_list: List[int]) -> None: + sample_list.append(42) + sample_list.to_last() + assert sample_list.content == 42 + + +def test_append_none_to_list(sample_list: List[int]) -> None: + sample_list.append(None) + sample_list.to_last() + assert sample_list.content == 3 + + +def test_concat_non_empty_list_with_non_empty_list(sample_list: List[int]) -> None: + other_list: List[int] = List() + other_list.append(42) + sample_list.concat(other_list) + sample_list.to_last() + assert sample_list.content == 42 + + +def test_concat_empty_list_with_non_empty_list( + empty_list: List[int], + sample_list: List[int], +) -> None: + assert empty_list.is_empty + empty_list.concat(sample_list) + assert not empty_list.is_empty + assert sample_list.is_empty + + +def test_concat_self(sample_list: List[int]) -> None: + sample_list.concat(sample_list) + sample_list.to_last() + assert sample_list.content == 3 + + +def test_concat_none(sample_list: List[int]) -> None: + sample_list.concat(None) + sample_list.to_last() + assert sample_list.content == 3 + + +def test_concat_non_empty_list_with_empty_list( + sample_list: List[int], + empty_list: List[int], +) -> None: + sample_list.concat(empty_list) + sample_list.to_last() + assert sample_list.content == 3 + + +def test_remove_from_list_without_access(sample_list: List[int]) -> None: + assert not sample_list.has_access + assert not sample_list.is_empty + sample_list.remove() + assert not sample_list.has_access + assert not sample_list.is_empty + sample_list.to_first() + assert sample_list.content == 1 + + +def test_remove_from_empty_list(empty_list: List[int]) -> None: + assert empty_list.is_empty + empty_list.remove() + assert empty_list.is_empty + + +def test_remove_first_item_of_list(sample_list: List[int]) -> None: + sample_list.to_first() + sample_list.remove() + assert sample_list.content == 2 + + +def test_remove_item_from_the_middle_of_list(sample_list: List[int]) -> None: + sample_list.to_first() + sample_list.next() + assert sample_list.content == 2 + sample_list.remove() + assert sample_list.content == 3 + + +def test_remove_from_last_item_of_list(sample_list: List[int]) -> None: + sample_list.to_last() + sample_list.remove() + assert not sample_list.has_access + assert sample_list.content is None + + sample_list.to_last() + assert sample_list.content == 2 + + +def test_remove_from_list_with_one_element() -> None: + lst: List[int] = List() + lst.append(42) + lst.to_first() + assert lst.content == 42 + lst.remove() + assert lst.is_empty + assert not lst.has_access + assert lst.content is None + + +def test_parse_none_to_get_previos(sample_list: List[int]) -> None: + assert sample_list._get_previous(None) is None + + +def test_get_previos_on_empty_list(empty_list: List[int]) -> None: + assert empty_list._get_previous(None) is None + + +if __name__ == "__main__": + raise SystemExit(pytest.main()) diff --git a/tests/datastructures/queue_test.py b/tests/datastructures/queue_test.py new file mode 100644 index 0000000..5bf7576 --- /dev/null +++ b/tests/datastructures/queue_test.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +"""Tests for `datastructures._queue`.""" +from __future__ import annotations + +import pytest + +from nrw.datastructures._queue import Queue, _QueueNode + + +def test_slots_of_queue_node() -> None: + assert _QueueNode.__slots__ == ("_content", "_next_node") + + +def test_queue_node_creation_and_content() -> None: + content: str = "test" + node: _QueueNode[str] = _QueueNode(content) + assert node.content == content + assert node.next_node is None + + +def test_queue_node_next_node() -> None: + content1: str = "test1" + content2: str = "test2" + node1: _QueueNode[str] = _QueueNode(content1) + node2: _QueueNode[str] = _QueueNode(content2) + node1.next_node = node2 + assert node1.next_node == node2 + + +def test_slots_of_queue() -> None: + assert Queue.__slots__ == ("_head", "_tail") + + +def test_is_empty_on_empty_queue() -> None: + q: Queue[int] = Queue() + assert q.is_empty + + +def test_is_empty_on_non_empty_queue() -> None: + q: Queue[int] = Queue() + q.enqueue(1) + assert not q.is_empty + + +def test_front_on_empty_queue() -> None: + q: Queue[int] = Queue() + assert q.front is None + + +def test_queue_functionality_on_non_empty_queue() -> None: + q: Queue[int] = Queue() + q.enqueue(1) + q.enqueue(2) + q.enqueue(3) + assert not q.is_empty + assert q.front == 1 + q.dequeue() + assert q.front == 2 + q.dequeue() + assert q.front == 3 + q.dequeue() + assert q.is_empty + assert q.front is None + + +def test_enqueue_none_on_non_empty_queue() -> None: + q: Queue[int] = Queue() + assert q.is_empty + q.enqueue(1) + assert not q.is_empty + assert q.front == 1 + q.enqueue(None) + assert not q.is_empty + assert q.front == 1 + + +def test_enqueue_none_on_empty_queue() -> None: + q: Queue[int] = Queue() + assert q.is_empty + q.enqueue(None) # type: ignore[arg-type] + assert q.is_empty + assert q.front is None + + +def test_dequeue_on_empty_queue() -> None: + q: Queue[int] = Queue() + assert q.is_empty + q.dequeue() + assert q.is_empty + assert q.front is None + + +if __name__ == "__main__": + raise SystemExit(pytest.main()) diff --git a/tests/datastructures/stack_test.py b/tests/datastructures/stack_test.py new file mode 100644 index 0000000..30c282a --- /dev/null +++ b/tests/datastructures/stack_test.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +"""Tests for `datastructures._stack`.""" +from __future__ import annotations + +import pytest + +from nrw.datastructures._stack import Stack, _StackNode + + +def test_slots_of_stack_node() -> None: + assert _StackNode.__slots__ == ("_content", "_next_node") + + +def test_queue_node_creation_and_content() -> None: + content: str = "test" + node: _StackNode[str] = _StackNode(content) + assert node.content == content + assert node.next_node is None + + +def test_queue_node_next_node() -> None: + content1: str = "test1" + content2: str = "test2" + node1: _StackNode[str] = _StackNode(content1) + node2: _StackNode[str] = _StackNode(content2) + node1.next_node = node2 + assert node1.next_node == node2 + + +def test_slots_of_stack() -> None: + assert Stack.__slots__ == ("_head",) + + +def test_is_empty_on_empty_stack() -> None: + s: Stack[int] = Stack() + assert s.is_empty + + +def test_is_empty_on_non_empty_stack() -> None: + s: Stack[int] = Stack() + s.push(1) + assert not s.is_empty + + +def test_front_on_empty_stack() -> None: + s: Stack[int] = Stack() + assert s.top is None + + +def test_stack_functionality_on_non_empty_stack() -> None: + s: Stack[int] = Stack() + s.push(1) + s.push(2) + s.push(3) + assert not s.is_empty + assert s.top == 3 + s.pop() + assert s.top == 2 + s.pop() + assert s.top == 1 + s.pop() + assert s.is_empty + assert s.top is None + + +def test_push_none_on_non_empty_stack() -> None: + s: Stack[int] = Stack() + assert s.is_empty + s.push(1) + assert not s.is_empty + assert s.top == 1 + s.push(None) + assert not s.is_empty + assert s.top == 1 + + +def test_push_none_on_empty_stack() -> None: + s: Stack[int] = Stack() + assert s.is_empty + s.push(None) # type: ignore[arg-type] + assert s.is_empty + assert s.top is None + + +def test_pop_on_empty_stack() -> None: + s: Stack[int] = Stack() + assert s.is_empty + s.pop() + assert s.is_empty + assert s.top is None + + +if __name__ == "__main__": + raise SystemExit(pytest.main()) diff --git a/tests/datastructures/vertex_test.py b/tests/datastructures/vertex_test.py new file mode 100644 index 0000000..2a6f929 --- /dev/null +++ b/tests/datastructures/vertex_test.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +"""Tests for `datastructures._vertex`.""" +from __future__ import annotations + +import pytest + +from nrw.datastructures._vertex import Vertex + + +def test_vertex_construction_and_getters() -> None: + vertex: Vertex = Vertex("A") + assert vertex.id == "A" + assert not vertex.mark + + +def test_is_marked_property() -> None: + vertex: Vertex = Vertex("A") + assert vertex.mark is vertex.is_marked + vertex.mark = True + assert vertex.mark is vertex.is_marked + + +def test_set_mark_on_vertex() -> None: + vertex: Vertex = Vertex("A") + assert not vertex.mark + vertex.mark = True + assert vertex.mark + + +if __name__ == "__main__": + raise SystemExit(pytest.main())