import unittest
from unittest import TestCase

from algorithms.dataStructures.LinkedList import Node
from algorithms.dataStructures.LinkedList import LinkedList


class linked_list_Test(TestCase):

    def test_create_node(self):
        n = Node(1)
        self.assertIsInstance(n, Node)

    def test_create_node_assigns_correct_data(self):
        n = Node(1)
        self.assertEqual(1, n.data)

    def test_create_node_assigns_None_to_next(self):
        n = Node(1)
        self.assertIsNone(n.next)

    def test_create_linked_list(self):
        ll = LinkedList()
        self.assertIsInstance(ll, LinkedList)

    def test_create_linked_list_with_node(self):
        n = Node(1)
        ll = LinkedList(n)
        self.assertEqual(n, ll.head)
        self.assertEqual(1, ll.head.data)

    def test_create_linked_list_with_multiple_nodes(self):
        n = Node(1)
        n2 = Node(2, n)
        n3 = Node('a', n2)

        ll = LinkedList(n3)
        self.assertEqual(n3, ll.head)
        self.assertEqual('a', ll.head.data)

        self.assertEqual(n2, ll.head.next)
        self.assertEqual(2, ll.head.next.data)

        self.assertEqual(n, ll.head.next.next)
        self.assertEqual(1, ll.head.next.next.data)


class get_element_Test(TestCase):

    def setUp(self):
        n = Node(1)
        n2 = Node(2, n)
        n3 = Node('a', n2)

        self.ll = LinkedList(n3)

    def test_access_first_element(self):
        n = self.ll.get_element(0)
        self.assertEqual('a', n.data)

    def test_access_second_element(self):
        n = self.ll.get_element(1)
        self.assertEqual(2, n.data)

    def test_access_third_element(self):
        n = self.ll.get_element(2)
        self.assertEqual(1, n.data)

    def test_get_head_single_element_list(self):
        n = Node(1)
        ll2 = LinkedList(n)

        data = ll2.get_head()

        self.assertEqual(n, data)

    def test_get_head_multiple_element_list(self):
        n = self.ll.get_head()

        self.assertEqual('a', n.data)


class empty_linked_list_Test(TestCase):

    def setUp(self):
        self.ll = LinkedList()

    def test_insert_end(self):
        self.ll.insert_end(1)
        self.assertEqual(1, self.ll.head.data)
        self.assertIsNone(self.ll.head.next)

    def test_insert_start(self):
        self.ll.insert_start(1)
        self.assertEqual(1, self.ll.head.data)
        self.assertIsNone(self.ll.head.next)

    def test_insert_position_0(self):
        self.ll.insert(1, 0)
        self.assertEqual(1, self.ll.head.data)
        self.assertIsNone(self.ll.head.next)


class linked_list_insert_Test(TestCase):

    def setUp(self):
        n = Node(1)
        n2 = Node(2, n)
        n3 = Node('a', n2)

        self.ll = LinkedList(n3)

    def test_insert_node_at_end(self):
        self.ll.insert_end(5)

        self.assertEqual(5, self.ll.head.next.next.next.data)
        self.assertIsNone(self.ll.head.next.next.next.next)

    def test_insert_node_at_end_doesnt_change_other_nodes(self):
        self.ll.insert_end(5)

        self.assertEqual(1, self.ll.head.next.next.data)
        self.assertEqual(2, self.ll.head.next.data)
        self.assertEqual('a', self.ll.head.data)

    def test_insert_node_at_start(self):
        self.ll.insert_start(5)

        self.assertEqual(5, self.ll.head.data)
        self.assertIsNotNone(self.ll.head.next)

    def test_insert_node_at_start_doesnt_change_other_nodes(self):
        self.ll.insert_start(5)

        self.assertEqual(1, self.ll.head.next.next.next.data)
        self.assertEqual(2, self.ll.head.next.next.data)
        self.assertEqual('a', self.ll.head.next.data)

    def test_insert_position_1(self):
        self.ll.insert(5, 1)

        self.assertEqual(5, self.ll.head.next.data)
        self.assertIsNotNone(self.ll.head.next.next)

    def test_insert_position_1_doesnt_change_other_nodes(self):
        self.ll.insert(5, 1)

        self.assertEqual(1, self.ll.head.next.next.next.data)
        self.assertEqual(2, self.ll.head.next.next.data)
        self.assertEqual('a', self.ll.head.data)

    def test_insert_position_2(self):
        self.ll.insert(5, 2)

        self.assertEqual(5, self.ll.head.next.next.data)
        self.assertIsNotNone(self.ll.head.next.next.next)

    def test_insert_position_2_doesnt_change_other_nodes(self):
        self.ll.insert(5, 2)

        self.assertEqual(1, self.ll.head.next.next.next.data)
        self.assertEqual(2, self.ll.head.next.data)
        self.assertEqual('a', self.ll.head.data)

    def test_insert_position_3(self):
        self.ll.insert(5, 3)

        self.assertEqual(5, self.ll.head.next.next.next.data)
        self.assertIsNone(self.ll.head.next.next.next.next)

    def test_insert_position_3_doesnt_change_other_nodes(self):
        self.ll.insert(5, 3)

        self.assertEqual(1, self.ll.head.next.next.data)
        self.assertEqual(2, self.ll.head.next.data)
        self.assertEqual('a', self.ll.head.data)


class linked_list_delete_Test(TestCase):

    def setUp(self):
        n = Node(1)
        n2 = Node(2, n)
        n3 = Node('a', n2)

        self.ll = LinkedList(n3)

    def test_delete_only_element(self):
        ll = LinkedList()
        ll.insert_start(1)
        ll.delete(0)

        self.assertIsNone(ll.head)

    def test_delete_head(self):
        self.ll.delete(0)

        self.assertEqual(2, self.ll.head.data)
        self.assertEqual(1, self.ll.head.next.data)
        self.assertIsNone(self.ll.head.next.next)

    def test_delete_end(self):
        self.ll.delete(2)

        self.assertIsNone(self.ll.head.next.next)
        self.assertEqual(2, self.ll.head.next.data)
        self.assertEqual('a', self.ll.head.data)


class linked_list_print_Test(TestCase):

    def setUp(self):
        n = Node(1)
        n2 = Node(2, n)
        n3 = Node('a', n2)

        self.ll = LinkedList(n3)

    def test_display(self):
        import sys
        from io import StringIO

        saved_stdout = sys.stdout
        try:
            out = StringIO()
            sys.stdout = out
            self.ll.display()
            output = out.getvalue().strip()
            self.assertEqual(output, "a\n2\n1")
        finally:
            sys.stdout = saved_stdout


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

import unittest
from unittest import TestCase

from algorithms.dataStructures.LinkedList import Node
from algorithms.dataStructures.LinkedList import LinkedList


class linked_list_Test(TestCase):

    def test_create_node(self):
        n = Node(1)
        self.assertIsInstance(n, Node)

    def test_create_node_assigns_correct_data(self):
        n = Node(1)
        self.assertEqual(1, n.data)

    def test_create_node_assigns_None_to_next(self):
        n = Node(1)
        self.assertIsNone(n.next)

    def test_create_linked_list(self):
        ll = LinkedList()
        self.assertIsInstance(ll, LinkedList)

    def test_create_linked_list_with_node(self):
        n = Node(1)
        ll = LinkedList(n)
        self.assertEqual(n, ll.head)
        self.assertEqual(1, ll.head.data)

    def test_create_linked_list_with_multiple_nodes(self):
        n = Node(1)
        n2 = Node(2, n)
        n3 = Node('a', n2)

        ll = LinkedList(n3)
        self.assertEqual(n3, ll.head)
        self.assertEqual('a', ll.head.data)

        self.assertEqual(n2, ll.head.next)
        self.assertEqual(2, ll.head.next.data)

        self.assertEqual(n, ll.head.next.next)
        self.assertEqual(1, ll.head.next.next.data)


class get_element_Test(TestCase):

    def setUp(self):
        n = Node(1)
        n2 = Node(2, n)
        n3 = Node('a', n2)

        self.ll = LinkedList(n3)

    def test_access_first_element(self):
        n = self.ll.get_element(0)
        self.assertEqual('a', n.data)

    def test_access_second_element(self):
        n = self.ll.get_element(1)
        self.assertEqual(2, n.data)

    def test_access_third_element(self):
        n = self.ll.get_element(2)
        self.assertEqual(1, n.data)

    def test_get_head_single_element_list(self):
        n = Node(1)
        ll2 = LinkedList(n)

        data = ll2.get_head()

        self.assertEqual(n, data)

    def test_get_head_multiple_element_list(self):
        n = self.ll.get_head()

        self.assertEqual('a', n.data)


class empty_linked_list_Test(TestCase):

    def setUp(self):
        self.ll = LinkedList()

    def test_insert_end(self):
        self.ll.insert_end(1)
        self.assertEqual(1, self.ll.head.data)
        self.assertIsNone(self.ll.head.next)

    def test_insert_start(self):
        self.ll.insert_start(1)
        self.assertEqual(1, self.ll.head.data)
        self.assertIsNone(self.ll.head.next)

    def test_insert_position_0(self):
        self.ll.insert(1, 0)
        self.assertEqual(1, self.ll.head.data)
        self.assertIsNone(self.ll.head.next)


class linked_list_insert_Test(TestCase):

    def setUp(self):
        n = Node(1)
        n2 = Node(2, n)
        n3 = Node('a', n2)

        self.ll = LinkedList(n3)

    def test_insert_node_at_end(self):
        self.ll.insert_end(5)

        self.assertEqual(5, self.ll.head.next.next.next.data)
        self.assertIsNone(self.ll.head.next.next.next.next)

    def test_insert_node_at_end_doesnt_change_other_nodes(self):
        self.ll.insert_end(5)

        self.assertEqual(1, self.ll.head.next.next.data)
        self.assertEqual(2, self.ll.head.next.data)
        self.assertEqual('a', self.ll.head.data)

    def test_insert_node_at_start(self):
        self.ll.insert_start(5)

        self.assertEqual(5, self.ll.head.data)
        self.assertIsNotNone(self.ll.head.next)

    def test_insert_node_at_start_doesnt_change_other_nodes(self):
        self.ll.insert_start(5)

        self.assertEqual(1, self.ll.head.next.next.next.data)
        self.assertEqual(2, self.ll.head.next.next.data)
        self.assertEqual('a', self.ll.head.next.data)

    def test_insert_position_1(self):
        self.ll.insert(5, 1)

        self.assertEqual(5, self.ll.head.next.data)
        self.assertIsNotNone(self.ll.head.next.next)

    def test_insert_position_1_doesnt_change_other_nodes(self):
        self.ll.insert(5, 1)

        self.assertEqual(1, self.ll.head.next.next.next.data)
        self.assertEqual(2, self.ll.head.next.next.data)
        self.assertEqual('a', self.ll.head.data)

    def test_insert_position_2(self):
        self.ll.insert(5, 2)

        self.assertEqual(5, self.ll.head.next.next.data)
        self.assertIsNotNone(self.ll.head.next.next.next)

    def test_insert_position_2_doesnt_change_other_nodes(self):
        self.ll.insert(5, 2)

        self.assertEqual(1, self.ll.head.next.next.next.data)
        self.assertEqual(2, self.ll.head.next.data)
        self.assertEqual('a', self.ll.head.data)

    def test_insert_position_3(self):
        self.ll.insert(5, 3)

        self.assertEqual(5, self.ll.head.next.next.next.data)
        self.assertIsNone(self.ll.head.next.next.next.next)

    def test_insert_position_3_doesnt_change_other_nodes(self):
        self.ll.insert(5, 3)

        self.assertEqual(1, self.ll.head.next.next.data)
        self.assertEqual(2, self.ll.head.next.data)
        self.assertEqual('a', self.ll.head.data)


class linked_list_delete_Test(TestCase):

    def setUp(self):
        n = Node(1)
        n2 = Node(2, n)
        n3 = Node('a', n2)

        self.ll = LinkedList(n3)

    def test_delete_only_element(self):
        ll = LinkedList()
        ll.insert_start(1)
        ll.delete(0)

        self.assertIsNone(ll.head)

    def test_delete_head(self):
        self.ll.delete(0)

        self.assertEqual(2, self.ll.head.data)
        self.assertEqual(1, self.ll.head.next.data)
        self.assertIsNone(self.ll.head.next.next)

    def test_delete_end(self):
        self.ll.delete(2)

        self.assertIsNone(self.ll.head.next.next)
        self.assertEqual(2, self.ll.head.next.data)
        self.assertEqual('a', self.ll.head.data)


class linked_list_print_Test(TestCase):

    def setUp(self):
        n = Node(1)
        n2 = Node(2, n)
        n3 = Node('a', n2)

        self.ll = LinkedList(n3)

    def test_display(self):
        import sys
        from io import StringIO

        saved_stdout = sys.stdout
        try:
            out = StringIO()
            sys.stdout = out
            self.ll.display()
            output = out.getvalue().strip()
            self.assertEqual(output, "a\n2\n1")
        finally:
            sys.stdout = saved_stdout


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