#!/usr/local/cpython-3.7/bin/python3

"""Automated tests for Linked_list class."""


import sys

import linked_list_mod


def make_used(variable):
    """Convince pyflakes that variable is used."""
    assert True or variable


def test_empty():
    """Test an empty list."""
    empty_list = linked_list_mod.Linked_list()
    if empty_list:
        sys.stderr.write('{}: test_empty: empty_list is not empty\n'.format(sys.argv[0]))
        return False

    return True


def test_nonempty():
    """Test a nonempty list."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    if linked_list:
        return True

    sys.stderr.write('{}: test_nonempty: linked_list is empty\n'.format(sys.argv[0]))
    return False


def test_append1():
    """Test appending a single value."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    if list(linked_list) == [1]:
        return True

    sys.stderr.write('{}: test_append1: linked_list is not [1] when converted to list\n'.format(sys.argv[0]))
    return False


def test_append2():
    """Test appending two values."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    linked_list.append(2)
    if list(linked_list) == [1, 2]:
        return True

    sys.stderr.write('{}: test_append2: linked_list is not [1, 2] when converted to list\n'.format(sys.argv[0]))
    return False


def test_append3():
    """Test appending three values."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    linked_list.append(2)
    linked_list.append(3)

    if list(linked_list) == [1, 2, 3]:
        return True

    sys.stderr.write('{}: test_append3: linked_list is not [1, 2, 3] when converted to list\n'.format(sys.argv[0]))
    return False


def test_prepend1():
    """Test prepending one value."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.prepend(1)

    if list(linked_list) == [1]:
        return True

    sys.stderr.write('{}: test_prepend1: linked_list is not [1] when converted to list\n'.format(sys.argv[0]))
    return False


def test_prepend2():
    """Test prepending two values."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.prepend(1)
    linked_list.prepend(2)
    if list(linked_list) == [2, 1]:
        return True

    sys.stderr.write('{}: test_prepend2: linked_list is not [2, 1] when converted to list\n'.format(sys.argv[0]))
    return False


def test_prepend3():
    """Test prepending three values."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.prepend(1)
    linked_list.prepend(2)
    linked_list.prepend(3)

    if list(linked_list) == [3, 2, 1]:
        return True

    sys.stderr.write('{}: test_prepend3: linked_list is not [3, 2, 1] when converted to list\n'.format(sys.argv[0]))
    return False


def test_length0():
    """Test the length of an empty list."""
    linked_list = linked_list_mod.Linked_list()

    if not linked_list:
        return True

    sys.stderr.write('{}: test_length0: linked_list is not empty\n'.format(sys.argv[0]))
    return False


def test_length1():
    """Test the length of a singleton."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.prepend(1)

    if len(linked_list) == 1:
        return True

    sys.stderr.write('{}: test_length1: len(linked_list) is not 1\n'.format(sys.argv[0]))
    return False


def test_length2():
    """Test the length of a list of length 2."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.prepend(1)
    linked_list.prepend(2)

    if len(linked_list) == 2:
        return True

    sys.stderr.write('{}: test_length2: len(linked_list) is not 2\n'.format(sys.argv[0]))
    return False


def test_length3():
    """Test the length of a list of length 3."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.prepend(1)
    linked_list.prepend(2)
    linked_list.prepend(3)

    if len(linked_list) == 3:
        return True

    sys.stderr.write('{}: test_length3: linked_list is not 3\n'.format(sys.argv[0]))
    return False


def test_reverse0():
    """Test reversing an empty list."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.reverse()
    if list(linked_list) == []:
        return True

    sys.stderr.write('{}: test_reverse0: reversed linked_list is not [] when converted to list\n'.format(sys.argv[0]))
    sys.stderr.write('{}\n'.format(str(linked_list)))
    return False


def test_reverse1():
    """Test reversing a singleton."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    linked_list.reverse()

    if list(linked_list) == [1]:
        return True

    sys.stderr.write('{}: test_reverse1: reversed linked_list is not [1] when converted to list\n'.format(sys.argv[0]))
    sys.stderr.write('{}\n'.format(str(linked_list)))
    return False


def test_reverse2():
    """Test reversing a two-element list."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    linked_list.append(2)
    linked_list.reverse()

    if list(linked_list) == [2, 1]:
        return True

    sys.stderr.write('{}: test_reverse2: reversed linked_list is not [2, 1] when converted to list\n'.format(sys.argv[0]))
    sys.stderr.write('{}\n'.format(str(linked_list)))
    return False


def test_reverse3():
    """Test reversing a three-element list."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    linked_list.append(2)
    linked_list.append(3)
    linked_list.reverse()
    if list(linked_list) == [3, 2, 1]:
        return True

    sys.stderr.write('{}: test_reverse3: reversed linked_list is not [3, 2, 1] when converted to list\n'.format(sys.argv[0]))
    sys.stderr.write('{}\n'.format(str(linked_list)))
    return False


def test_reverse3_and_append():
    """Test reversing a 3 element list, followed by an append."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    linked_list.append(2)
    linked_list.append(3)
    linked_list.reverse()
    linked_list.append(4)
    if list(linked_list) == [3, 2, 1, 4]:
        return True

    sys.stderr.write(
        '{}: test_reverse3_and_append, reversed, appended linked_list is not '
        '[3, 2, 1, 4] when converted to list\n'.format(sys.argv[0]))
    sys.stderr.write('{}\n'.format(str(linked_list)))
    return False


def test_reverse3_and_prepend():
    """Test reversing a 3 element list, followed by a prepend."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    linked_list.append(2)
    linked_list.append(3)
    linked_list.reverse()
    linked_list.prepend(4)
    if list(linked_list) == [4, 3, 2, 1]:
        return True

    sys.stderr.write(
        '{}: test_reverse3_and_prepend, reversed, prepended linked_list is not '
        '[4, 3, 2, 1] when converted to list\n'.format(sys.argv[0]))
    sys.stderr.write('{}\n'.format(str(linked_list)))
    return False


def test_str0():
    """Test converting an empty list to a string."""
    linked_list = linked_list_mod.Linked_list()

    string = str(linked_list)

    if string == 'Linked_list([])':
        return True

    sys.stderr.write('{}: test_str0: str(linked_list) is not Linked_list([])\n'.format(sys.argv[0]))
    sys.stderr.write('{}\n'.format(str(linked_list)))
    return False


def test_str3():
    """Test converting a three-element list to a string."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    linked_list.append(2)
    linked_list.append(3)

    string = str(linked_list)

    if string == 'Linked_list([1, 2, 3])':
        return True

    sys.stderr.write('{}: test_str3: str(linked_list) is not Linked_list([1, 2, 3])\n'.format(sys.argv[0]))
    sys.stderr.write('{}\n'.format(str(linked_list)))
    return False


def test_popleft():
    """Test removing a single value from the left end of a 3-element list."""
    all_good = True

    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    linked_list.append(2)
    linked_list.append(3)

    value = linked_list.popleft()

    if value == 1:
        pass
    else:
        sys.stderr.write('{}: test_popleft: linked_list.popleft() is not 1\n'.format(sys.argv[0]))
        sys.stderr.write('{}\n'.format(str(linked_list)))
        all_good = False

    if len(linked_list) == 2:
        pass
    else:
        sys.stderr.write('{}: test_popleft: len(linked_list) is not 2\n'.format(sys.argv[0]))
        sys.stderr.write('{}\n'.format(str(linked_list)))
        all_good = False

    return all_good


def test_popleft_empty():
    """Test removing a single value from the left end of a 0-element list."""
    all_good = True

    linked_list = linked_list_mod.Linked_list()

    try:
        make_used(linked_list.popleft())
    except IndexError:
        pass
    else:
        sys.stderr.write('{}: test_popleft: linked_list.popleft() is not IndexError\n'.format(sys.argv[0]))
        sys.stderr.write('{}\n'.format(str(linked_list)))
        all_good = False

    if linked_list:
        sys.stderr.write('{}: test_popleft_empty: linked_list is not empty\n'.format(sys.argv[0]))
        sys.stderr.write('{}\n'.format(str(linked_list)))
        all_good = False

    return all_good


def test_popleft1():
    """Test removing a single value from the left end of a 1-element list."""
    all_good = True

    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)

    value = linked_list.popleft()

    if value != 1:
        sys.stderr.write('{}: test_popleft1: linked_list.popleft() is not 1\n'.format(sys.argv[0]))
        sys.stderr.write('{}\n'.format(str(linked_list)))
        all_good = False

    if linked_list:
        sys.stderr.write('{}: test_popleft1: linked_list is not empty\n'.format(sys.argv[0]))
        sys.stderr.write('{}\n'.format(str(linked_list)))
        all_good = False

    return all_good


def test_remove0():
    """Test removing a single value from an empty list."""
    all_good = True

    linked_list = linked_list_mod.Linked_list()
    try:
        linked_list.remove(0)
    except ValueError:
        pass
    else:
        sys.stderr.write('{}: test_remove0: removed empty linked_list did not raise ValueError \n'.format(sys.argv[0]))
        sys.stderr.write('{}\n'.format(str(linked_list)))
        all_good = False

    if linked_list:
        sys.stderr.write('{}: test_remove0: len(linked_list) is not 0\n'.format(sys.argv[0]))
        sys.stderr.write('{}\n'.format(str(linked_list)))
        all_good = False

    return all_good


def test_remove1():
    """Test removing a single value from single-element list."""
    all_good = True

    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    linked_list.remove(1)
    if list(linked_list) != []:
        sys.stderr.write('{}: test_remove1: removed linked_list is not [] when converted to list\n'.format(sys.argv[0]))
        sys.stderr.write('{}\n'.format(str(linked_list)))
        all_good = False

    if linked_list:
        sys.stderr.write('{}: test_remove1: len(linked_list) is not 0\n'.format(sys.argv[0]))
        sys.stderr.write('{}\n'.format(str(linked_list)))
        all_good = False

    return all_good


def test_remove2():
    """Test removing a single value from two-element list."""
    all_good = True

    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    linked_list.append(2)
    linked_list.remove(2)
    if list(linked_list) == [1]:
        pass
    else:
        sys.stderr.write('{}: test_remove2: removed linked_list is not [1] when converted to list\n'.format(sys.argv[0]))
        sys.stderr.write('{}\n'.format(str(linked_list)))
        all_good = False

    if len(linked_list) == 1:
        pass
    else:
        sys.stderr.write('{}: test_remove2: len(linked_list) is not 1\n'.format(sys.argv[0]))
        sys.stderr.write('{}\n'.format(str(linked_list)))
        all_good = False

    return all_good


def test_remove3_first():
    """Test removing the first value from three-element list."""
    all_good = True

    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    linked_list.append(2)
    linked_list.append(3)
    linked_list.remove(1)
    if list(linked_list) == [2, 3]:
        return True

    sys.stderr.write('{}: test_remove3_first: removed linked_list is not [2, 1] when converted to list\n'.format(sys.argv[0]))
    sys.stderr.write('{}\n'.format(str(linked_list)))
    all_good = False

    if len(linked_list) != 2:
        sys.stderr.write('{}: test_remove3_first: len(linked_list) is not 2\n'.format(sys.argv[0]))
        sys.stderr.write('{}\n'.format(str(linked_list)))
        all_good = False

    return all_good


def test_remove3_middle():
    """Test removing the middle value from three-element list."""
    all_good = True

    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    linked_list.append(2)
    linked_list.append(3)
    linked_list.remove(2)
    if list(linked_list) == [1, 3]:
        pass
    else:
        sys.stderr.write('{}: test_remove3_middle: removed linked_list is not [1, 3] when converted to list\n'.format(sys.argv[0]))
        sys.stderr.write('{}\n'.format(str(linked_list)))
        all_good = False

    if len(linked_list) == 2:
        pass
    else:
        sys.stderr.write('{}: test_remove3_middle: len(linked_list) is not 2\n'.format(sys.argv[0]))
        sys.stderr.write('{}\n'.format(str(linked_list)))
        all_good = False

    return all_good


def test_remove3_last():
    """Test removing the last value from three-element list."""
    all_good = True

    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    linked_list.append(2)
    linked_list.append(3)
    linked_list.remove(3)
    if list(linked_list) == [1, 2]:
        pass
    else:
        sys.stderr.write('{}: test_remove3_last: removed linked_list is not [1, 2] when converted to list\n'.format(sys.argv[0]))
        sys.stderr.write('{}\n'.format(str(linked_list)))
        all_good = False

    if len(linked_list) == 2:
        pass
    else:
        sys.stderr.write('{}: test_remove3_last: len(linked_list) is not 2\n'.format(sys.argv[0]))
        sys.stderr.write('{}\n'.format(str(linked_list)))
        all_good = False

    return all_good


def test_in_positive():
    """Test "in" operator for a value that exists."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    linked_list.append(2)
    linked_list.append(3)
    if 2 in linked_list:
        return True

    sys.stderr.write('{}: test_in_positive: 2 not in list\n'.format(sys.argv[0]))
    sys.stderr.write('{}\n'.format(str(linked_list)))
    return False


def test_in_negative():
    """Test "in" operator for a value that does not exist."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    linked_list.append(2)
    linked_list.append(3)
    if 0 in linked_list:
        sys.stderr.write('{}: test_in_negative: 0 in list\n'.format(sys.argv[0]))
        sys.stderr.write('{}\n'.format(str(linked_list)))
        return False

    return True


def test_index_positive():
    """Test finding the index of an element that exists."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    linked_list.append(2)
    linked_list.append(3)

    try:
        result = linked_list.index(2)
    except ValueError:
        sys.stderr.write('{}: test_index_positive: 2 not found'.format(sys.argv[0]))
        return False

    if result == 1:
        return True

    sys.stderr.write('{}: test_index: incorrect value\n'.format(sys.argv[0]))
    sys.stderr.write('{}\n'.format(str(linked_list)))
    return False


def test_index_negative():
    """Test finding the index of an element that does not exist."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    linked_list.append(2)
    linked_list.append(3)

    try:
        linked_list.index(4)
    except ValueError:
        return True
    else:
        sys.stderr.write('{}: test_index_positive: 4 found\n'.format(sys.argv[0]))
        return False


def test_extend():
    """Test extending a list with values from an iterator."""
    linked_list = linked_list_mod.Linked_list()
    linked_list.append(1)
    linked_list.append(2)
    linked_list.append(3)
    linked_list.extend([4, 5, 6])
    if list(linked_list) == [1, 2, 3, 4, 5, 6]:
        return True

    sys.stderr.write('{}: test_extend: linked_list is not [1, 2, 3, 4, 5, 6] when converted to list\n'.format(sys.argv[0]))
    return False


def test_list_constructor():
    """Test creating a list with values from a list."""
    linked_list = linked_list_mod.Linked_list([1, 2, 3])
    if list(linked_list) == [1, 2, 3]:
        return True

    sys.stderr.write(
        '{}: test_list_constructor: linked_list is not [1, 2, 3] when converted to list\n'.format(sys.argv[0]))
    return False


def test_iterator_constructor():
    """Test extending a list with values from an iterator."""
    def iterator():
        """Yield 1, then 2, then 3."""
        yield 1
        yield 2
        yield 3

    linked_list = linked_list_mod.Linked_list(iterator())
    if list(linked_list) == [1, 2, 3]:
        return True

    sys.stderr.write(
        '{}: test_iterator_constructor: linked_list is not [1, 2, 3] when converted to list\n'.format(sys.argv[0]))
    return False


def main():
    """Run all tests and report."""
    all_good = True
    all_good &= test_empty()
    all_good &= test_nonempty()
    all_good &= test_append1()
    all_good &= test_append2()
    all_good &= test_append3()
    all_good &= test_prepend1()
    all_good &= test_prepend2()
    all_good &= test_prepend3()
    all_good &= test_length0()
    all_good &= test_length1()
    all_good &= test_length2()
    all_good &= test_length3()
    all_good &= test_reverse0()
    all_good &= test_reverse1()
    all_good &= test_reverse2()
    all_good &= test_reverse3()
    all_good &= test_reverse3_and_append()
    all_good &= test_reverse3_and_prepend()
    all_good &= test_str0()
    all_good &= test_str3()
    all_good &= test_popleft()
    all_good &= test_popleft_empty()
    all_good &= test_popleft1()
    all_good &= test_remove0()
    all_good &= test_remove1()
    all_good &= test_remove2()
    all_good &= test_remove3_first()
    all_good &= test_remove3_middle()
    all_good &= test_remove3_last()
    all_good &= test_in_positive()
    all_good &= test_in_negative()
    all_good &= test_index_positive()
    all_good &= test_index_negative()
    all_good &= test_extend()
    all_good &= test_list_constructor()
    all_good &= test_iterator_constructor()

    if all_good:
        sys.stderr.write('{}: All tests passed\n'.format(sys.argv[0]))
        sys.exit(0)
    else:
        sys.stderr.write('{}: One or more tests failed\n'.format(sys.argv[0]))
        sys.exit(1)


main()