<filename>tests/test_closed_universe.py
from copy import copy
from unittest import TestCase
from life import ClosedUniverse


class ClosedUniverseTestCase(TestCase):
    def test_init(self):
        with self.assertRaises(ValueError):
            ClosedUniverse(0, 1)

        with self.assertRaises(ValueError):
            ClosedUniverse(1, 0)

        with self.assertRaises(ValueError):
            ClosedUniverse(-1, 1)

        with self.assertRaises(ValueError):
            ClosedUniverse(1, -1)

        self.assertIsInstance(ClosedUniverse(1, 1), ClosedUniverse)

    def test_width(self):
        universe = ClosedUniverse(2, 1)

        self.assertEqual(universe.width, 2)

    def test_height(self):
        universe = ClosedUniverse(1, 2)

        self.assertEqual(universe.height, 2)

    def test_through(self):
        universe = ClosedUniverse(2, 2)

        self.assertEqual(list(universe.through()), [(0, 0), (1, 0), (0, 1), (1, 1)])

    def test_neighbours_of(self):
        universe = ClosedUniverse.from_data([
            [1, 2, 3],
            [4, 5, 6],
            [7, 8, 9]
        ])

        neighbours = universe.neighbours_of(0, 0)
        self.assertEqual(list(neighbours), [2, 5, 4])

        neighbours = universe.neighbours_of(2, 2)
        self.assertEqual(list(neighbours), [5, 6, 8])

        neighbours = universe.neighbours_of(1, 1)
        self.assertEqual(list(neighbours), [1, 2, 3, 6, 9, 8, 7, 4])

    def test_adjust_position(self):
        universe = ClosedUniverse(2, 2)

        with self.assertRaises(IndexError):
            universe.adjust_position(-1, 0)

        with self.assertRaises(IndexError):
            universe.adjust_position(2, 0)

        with self.assertRaises(IndexError):
            universe.adjust_position(0, -1)

        with self.assertRaises(IndexError):
            universe.adjust_position(0, 2)

        self.assertEqual(universe.adjust_position(0, 0), (0, 0))

    def test_is_position_in_range(self):
        universe = ClosedUniverse(2, 2)

        self.assertFalse(universe.is_position_in_range(-1, 0))
        self.assertFalse(universe.is_position_in_range(2, 0))
        self.assertFalse(universe.is_position_in_range(0, -1))
        self.assertFalse(universe.is_position_in_range(0, 2))
        self.assertTrue(universe.is_position_in_range(0, 0))

    def test_copy(self):
        universe = ClosedUniverse.from_data([
            [1, 2],
            [3, list()]
        ])

        universe_copy = copy(universe)

        self.assertEqual(universe_copy.width, universe.width)
        self.assertEqual(universe_copy.height, universe.height)
        self.assertEqual(universe_copy, universe)
        self.assertIs(universe_copy[1, 1], universe[1, 1])

    def test_get_item(self):
        universe = ClosedUniverse.from_data([
            [1, 3],
            [2, None]
        ])

        self.assertEqual(universe[0, 0], 1)
        self.assertEqual(universe[1, 1], None)

        with self.assertRaises(IndexError):
            universe[-1, 0]

        with self.assertRaises(IndexError):
            universe[2, 0]

        with self.assertRaises(IndexError):
            universe[0, -1]

        with self.assertRaises(IndexError):
            universe[0, 2]

    def test_set_item(self):
        universe = ClosedUniverse(2, 2)

        universe[0, 0] = 1
        self.assertEqual(universe[0, 0], 1)

        universe[0, 0] = None
        self.assertEqual(universe[0, 0], None)

        with self.assertRaises(IndexError):
            universe[-1, 0] = 2

        with self.assertRaises(IndexError):
            universe[2, 0] = 3

        with self.assertRaises(IndexError):
            universe[0, -1] = 4

        with self.assertRaises(IndexError):
            universe[0, 2] = 5

    def test_str(self):
        universe = ClosedUniverse.from_data([
            [None, 3],
            [2, None]
        ])

        self.assertMultiLineEqual(str(universe), '  3\n2  ')

    def test_eq(self):
        left = ClosedUniverse.from_data([
            [1, 3],
            [2, None]
        ])

        right = ClosedUniverse.from_data([
            [1, 3],
            [2, None]
        ])

        self.assertEqual(left, right)

        left = ClosedUniverse.from_data([
            [1, 3],
            [2, None]
        ])

        right = ClosedUniverse.from_data([
            [1, 3],
            [2, 4]
        ])

        self.assertNotEqual(left, right)

    def test_from_data(self):
        universe = ClosedUniverse.from_data([
            [0, 1],
            [2, 3, 4]
        ])

        self.assertEqual(universe.width, 2)
        self.assertEqual(universe.height, 2)

        self.assertIsNone(universe[0, 0])
        self.assertEqual(universe[1, 0], 1)
        self.assertEqual(universe[0, 1], 2)
        self.assertEqual(universe[1, 1], 3)

        universe = ClosedUniverse.from_data([
            [0, '*'],
            [2, '*', '*']
        ], lambda cell: cell == '*')

        self.assertEqual(universe.width, 2)
        self.assertEqual(universe.height, 2)

        self.assertIsNone(universe[0, 0])
        self.assertEqual(universe[1, 0], '*')
        self.assertIsNone(universe[0, 1])
        self.assertEqual(universe[1, 1], '*')

        with self.assertRaises(ValueError):
            universe = ClosedUniverse.from_data([])

        with self.assertRaises(ValueError):
            universe = ClosedUniverse.from_data([[]])

    def test_random(self):
        universe = ClosedUniverse.random(2, 2, lambda: 1)

        self.assertEqual(universe.width, 2)
        self.assertEqual(universe.height, 2)
        self.assertEqual(universe[0, 0], 1)
        self.assertEqual(universe[1, 0], 1)
        self.assertEqual(universe[0, 1], 1)
        self.assertEqual(universe[1, 1], 1)


if __name__ == '__main__':
    unittest.main()
