CSC148-Winter-2023-A2 / a2_sample_test.py
a2_sample_test.py
Raw
"""Assignment 2 - Starter Tests

=== CSC148 Winter 2023 ===
Department of Computer Science,
University of Toronto

Authors: David Liu, Bogdan Simion, Diane Horton, Sophia Huynh, Tom Ginsberg,
Jonathan Calver, Jacqueline Smith, and Misha Schwartz

All of the files in this directory and all subdirectories are:
Copyright (c) 2023 David Liu, Bogdan Simion, Diane Horton, Sophia Huynh,
Jonathan Calver, Jacqueline Smith, and Misha Schwartz

=== Module Description ===
This module contains sample tests for Assignment 2.
The tests use the provided example-directory.
This test suite is _very_ small. You should plan to add to it to
thoroughly test your code.

IMPORTANT NOTES:
    - Depending on your operating system or other system settings, you
      may end up with incorrect results when running the provided doctests
      and pytests that rely on the sizes of files stored on your computer.
      Make sure to run the self-tests on MarkUs, once they are
      posted, to confirm your code runs correctly there.
"""
import os
import pytest
from hypothesis import given
from hypothesis.strategies import integers
from typing import Tuple
from tm_trees import DIRECTORYTREE_EXAMPLE_RESULT, FileTree, TMTree, \
    DirectoryTree, dir_tree_from_nested_tuple, path_to_nested_tuple, \
    ChessTree, get_worksheet_tree, moves_to_nested_dict, \
    OperationNotSupportedError

# This should be the path to the "workshop" directory in the sample data
# included in the zip file for this assignment.
EXAMPLE_PATH = os.path.join('example-directory', 'workshop')


##############################################################################
# Helpers
##############################################################################
def is_valid_colour(colour: Tuple[int, int, int]) -> bool:
    """
    Return True iff <colour> is a valid colour. That is, if all of its
    values are between 0 and 255, inclusive.
    """
    for i in range(3):
        if not 0 <= colour[i] <= 255:
            return False
    return True


###########################################
# TMTree provided basic testing
###########################################
class TestTMTree:

    def test_init_doctest(self) -> None:
        t1 = TMTree('B', [], 5)
        assert t1.rect is None
        assert t1.data_size == 5
        t2 = TMTree('A', [t1], 1)
        assert t2.rect is None
        assert t2.data_size == 6

    def test_is_displayed_tree_leaf_doctest(self) -> None:
        t1 = TMTree('B', [], 5)
        assert t1.is_displayed_tree_leaf()
        t2 = TMTree('A', [t1], 1)
        assert t1.is_displayed_tree_leaf()
        assert not t2.is_displayed_tree_leaf()

    def test_update_rectangles_doctest(self) -> None:
        t1 = TMTree('B', [], 5)
        t2 = TMTree('A', [t1], 1)
        t2.update_rectangles((0, 0, 100, 200))
        assert t2.rect == (0, 0, 100, 200)
        assert t1.rect == (0, 0, 100, 200)
        s1 = TMTree('C1', [], 5)
        s2 = TMTree('C2', [], 15)
        t3 = TMTree('C', [s1, s2], 1)
        t3.update_rectangles((0, 0, 100, 200))
        assert s1.rect == (0, 0, 100, 50)
        assert s2.rect == (0, 50, 100, 150)
        assert t3.rect == (0, 0, 100, 200)

    def test_get_rectangles_doctest(self) -> None:
        t1 = TMTree('B', [], 5)
        t2 = TMTree('A', [t1], 1)
        t2.update_rectangles((0, 0, 100, 200))
        assert t2.get_rectangles()[0][0] == (0, 0, 100, 200)
        s1 = TMTree('C1', [], 5)
        s2 = TMTree('C2', [], 15)
        t3 = TMTree('C', [s1, s2], 1)
        t3.update_rectangles((0, 0, 100, 200))
        rectangles = t3.get_rectangles()
        assert rectangles[0][0] == (0, 0, 100, 50)
        assert rectangles[1][0] == (0, 50, 100, 150)

    def test_get_tree_at_position_doctest(self) -> None:
        t1 = TMTree('B', [], 5)
        t2 = TMTree('A', [t1], 1)
        t2.update_rectangles((0, 0, 100, 200))
        assert t1.get_tree_at_position((10, 10)) is t1
        assert t2.get_tree_at_position((10, 10)) is t1
        assert t2.get_tree_at_position((500, 500)) is None
        s1 = TMTree('C1', [], 5)
        s2 = TMTree('C2', [], 15)
        t3 = TMTree('C', [s1, s2], 1)
        t3.update_rectangles((0, 0, 100, 200))
        assert t3.get_tree_at_position((0, 0)) is s1
        assert t3.get_tree_at_position((100, 100)) is s2

    def test_expand_doctest(self) -> None:
        s1 = TMTree('C1', [], 5)
        s2 = TMTree('C2', [], 15)
        t3 = TMTree('C', [s1, s2], 1)
        t3._expanded = False
        assert s1.is_displayed_tree_leaf() is False
        assert t3.expand() is s1
        assert s1.is_displayed_tree_leaf() is True

    def test_expand_all_doctest(self) -> None:
        d1 = TMTree('C1', [], 5)
        d2 = TMTree('C2', [d1], 1)
        d3 = TMTree('C', [d2], 1)
        d3._expanded = False
        d2._expanded = False
        assert d1.is_displayed_tree_leaf() is False
        assert d2.is_displayed_tree_leaf() is False
        assert d3.expand_all() is d1
        assert d1.is_displayed_tree_leaf() is True
        assert d2.is_displayed_tree_leaf() is False

    def test_collapse_doctest(self) -> None:
        d1 = TMTree('C1', [], 5)
        d2 = TMTree('C2', [d1], 1)
        assert d1.is_displayed_tree_leaf() is True
        assert d2.is_displayed_tree_leaf() is False
        assert d1.collapse() is d2
        assert d1.is_displayed_tree_leaf() is False
        assert d2.is_displayed_tree_leaf() is True

    def test_collapse_all_doctest(self) -> None:
        d1 = TMTree('C1', [], 5)
        d2 = TMTree('C2', [d1], 1)
        d3 = TMTree('C', [d2], 1)
        assert d1.is_displayed_tree_leaf() is True
        assert d1.collapse_all() is d3
        assert d1.is_displayed_tree_leaf() is False
        assert d2.is_displayed_tree_leaf() is False
        assert d3.is_displayed_tree_leaf() is True

    def test_move_doctest(self) -> None:
        s1 = TMTree('C1', [], 5)
        s2 = TMTree('C2', [], 15)
        t3 = TMTree('C', [s1, s2], 1)
        t3.update_rectangles((0, 0, 100, 200))
        assert s1.is_displayed_tree_leaf() is True
        assert s2.is_displayed_tree_leaf() is True
        s2.move(s1)
        assert s2.rect == (0, 0, 100, 200)
        assert s1.data_size == 20
        assert t3.data_size == 21
        assert t3.get_tree_at_position((0, 0)) is s2
        assert s1.is_displayed_tree_leaf() is False
        assert s2.is_displayed_tree_leaf() is True

    def test_change_size_doctest(self) -> None:
        s1 = TMTree('C1', [], 5)
        s2 = TMTree('C2', [], 15)
        t3 = TMTree('C', [s1, s2], 1)
        t3.update_rectangles((0, 0, 100, 200))
        s2.change_size(-2 / 3)
        assert s2.data_size == 5
        assert t3.data_size == 11
        assert s2.rect == (0, 100, 100, 100)

    def test_get_path_string_doctest(self) -> None:
        d1 = TMTree('C1', [], 5)
        d2 = TMTree('C2', [d1], 1)
        d3 = TMTree('C', [d2], 1)
        assert d3.get_path_string() == 'C(7) None'
        assert d1.get_path_string() == 'C | C2 | C1(5) None'

    def test_worksheet_rectangles(self) -> None:
        worksheet_tree = get_worksheet_tree()
        assert worksheet_tree.data_size == 60
        assert worksheet_tree.rect == (0, 0, 55, 30)
        rectangles = worksheet_tree.get_rectangles()
        shape_only = [rectangle[0] for rectangle in rectangles]  # skip colours
        expected_rectangles = [(0, 0, 20, 24),
                               (20, 0, 10, 24),
                               (0, 24, 30, 6),
                               (30, 0, 15, 12),
                               (30, 12, 15, 12),
                               (30, 24, 15, 6),
                               (45, 0, 10, 30)]
        assert shape_only == expected_rectangles


###########################################
# _FileTree and DirectoryTree provided basic testing
###########################################
class TestFileSystem:

    def test_directorytree_class_doctest(self) -> None:
        my_dir = dir_tree_from_nested_tuple((
            (".", [
                ("documents", [("report.pdf", 13), ("data.xlsx", 10)]),
                ("images", [("vacation", [("beach.png", 5)])]),
                ("my_song.mp3", 14),
                ("empty_dir", [])
            ])
        ))
        assert my_dir.data_size == 47
        assert len(my_dir._subtrees) == 4
        documents = my_dir._subtrees[0]
        assert isinstance(documents, DirectoryTree)
        assert isinstance(documents, TMTree)
        images = my_dir._subtrees[1]
        empty_dir = my_dir._subtrees[3]
        report_file = documents._subtrees[0]
        data_file = documents._subtrees[1]
        assert isinstance(data_file, FileTree)
        assert isinstance(data_file, TMTree)
        assert documents.data_size == 24
        assert images.data_size == 7

        assert str(my_dir) == DIRECTORYTREE_EXAMPLE_RESULT

        expected_str = "./documents (directory)".replace("/", os.path.sep)
        assert str(documents.get_path_string()) == expected_str

        expected_str = "./documents/data.xlsx (file)".replace("/", os.path.sep)
        assert str(data_file.get_path_string()) == expected_str

        my_dir.update_rectangles((0, 0, 200, 400))

        with pytest.raises(OperationNotSupportedError):
            data_file.move(report_file)

        expected_str = "./documents/data.xlsx (file)".replace("/", os.path.sep)
        assert str(data_file.get_path_string()) == expected_str

        data_file.move(empty_dir)
        expected_str = "./empty_dir/data.xlsx (file)".replace("/", os.path.sep)
        assert str(data_file.get_path_string()) == expected_str

    def test_path_to_nested_tuple_doctest(self) -> None:
        path = os.path.join("example-directory", "workshop", "prep")
        rslt = path_to_nested_tuple(path)
        assert rslt[0] == 'prep'
        assert rslt[1] == [('images', [('Cats.pdf', 17)]), ('reading.md', 7)]

    def test_example_data(self) -> None:
        """
        Test that the root of the tree at the 'workshop' directory is correct.
        """
        nested_tuple = path_to_nested_tuple(EXAMPLE_PATH)
        tree = dir_tree_from_nested_tuple(nested_tuple)
        assert tree._name == 'workshop'
        assert tree._parent_tree is None
        assert tree.data_size == 162
        assert is_valid_colour(tree._colour)
        assert len(tree._subtrees) == 3
        for subtree in tree._subtrees:
            # Note the use of is rather than ==.
            # This checks ids rather than values.
            assert subtree._parent_tree is tree

    @given(integers(min_value=100, max_value=1000),
           integers(min_value=100, max_value=1000),
           integers(min_value=100, max_value=1000),
           integers(min_value=100, max_value=1000))
    def test_single_directory_rectangles(self, x, y, width, height) -> None:
        """
        Test that the correct rectangle is produced for a single directory, for
        a range of possible rect parameter values passed to update_rectangles.
        """
        tree = dir_tree_from_nested_tuple(('empty_dir', []))
        tree.update_rectangles((x, y, width, height))
        rects = tree.get_rectangles()

        # This should be just a single rectangle and colour returned.
        assert len(rects) == 1
        rect, colour = rects[0]
        assert rect == (x, y, width, height)
        assert is_valid_colour(colour)

    def test_example_data_rectangles(self) -> None:
        """
        Test that the correct rectangles are produced for
        the EXAMPLE_PATH directory.
        """
        nested_tuple = path_to_nested_tuple(EXAMPLE_PATH)
        tree = dir_tree_from_nested_tuple(nested_tuple)

        tree.update_rectangles((0, 0, 200, 100))
        rects = tree.get_rectangles()

        # IMPORTANT: This test should pass when you have completed Task 2.
        assert len(rects) == 6

        # Here, we illustrate the correct order of the returned rectangles.
        actual_rects = [r[0] for r in rects]
        expected_rects = [(0, 0, 94, 4), (0, 4, 94, 28), (0, 32, 94, 68),
                          (94, 0, 73, 100), (167, 0, 33, 72), (167, 72, 33, 28)]

        assert len(actual_rects) == len(expected_rects)
        for i in range(len(actual_rects)):
            assert actual_rects[i] == expected_rects[i]


###########################################
# ChessTree provided basic testing
###########################################

class TestChessTree:

    def test_moves_to_nested_dict_doctest(self) -> None:
        assert moves_to_nested_dict([[]]) == {}
        assert moves_to_nested_dict([]) == {}
        assert moves_to_nested_dict([['a'], []]) == {('a', 1): {}}
        d = moves_to_nested_dict([["a", "b", "c"],
                                  ["a", "b"], ["d", "e"], ["d", "e"]])
        assert d == {('a', 0): {('b', 1): {('c', 1): {}}},
                     ('d', 0): {('e', 2): {}}}
        d = moves_to_nested_dict([["a", "b", "c"], ["a", "b"],
                                  ["d", "e", "a"], ["d", "e"]])
        assert d == {('a', 0): {('b', 1): {('c', 1): {}}},
                     ('d', 0): {('e', 1): {('a', 1): {}}}}

    def test_init_doctest(self) -> None:
        ct = ChessTree({('e2e4', 0): {('e7e5', 1): {}}})
        assert ct.is_displayed_tree_leaf() is False
        assert ct.data_size == 1
        assert ct.rect is None
        assert str(ct) == "- | (1) None\n" \
                          "    e2e4 | (1) None\n" \
                          "        e7e5(1) None"


if __name__ == '__main__':
    pytest.main(['a2_sample_test.py'])