"""Unit tests for review exercises."""

from inspect import getsource
from pytest import approx
from redirect import run_module
from sys import modules


def test_ch1():
    prompts = "First name: Last name: "
    # run the program with different inputs
    output = run_module("ch1", "Serena\nWilliams\n")
    assert output == prompts + "Serena Williams / Williams, Serena\n"
    output = run_module("ch1", "Michael\nJordan\n")
    assert output == prompts + "Michael Jordan / Jordan, Michael\n"
    # no f-strings, no use of +, two prints
    code = getsource(modules.get("ch1"))
    assert "{" not in code
    assert "+" not in code
    assert code.count("\nprint(") == 2


def test_ch2():
    # run the program with different inputs
    output = run_module("ch2", "1\n4\n-21\n")
    assert output == "3.000\n-7.000\n"
    output = run_module("ch2", "-2.3\n4.5\n6.7\n")
    assert output == "-0.989\n2.946\n"
    # no if statements, no brackets
    code = getsource(modules.get("ch2"))
    assert "if" not in code
    assert "[" not in code


def test_ch3():
    # test with every multiple of 5
    for n in range(5, 100, 10):
        output = run_module("ch3", f"{n}\n")
        assert output == "north-south border-to-border\n"
    for n in range(10, 100, 10):
        output = run_module("ch3", f"{n}\n")
        assert output == "east-west coast-to-coast\n"
    # run the program 20 more times
    for n in range(1, 100, 10):
        output = run_module("ch3", f"{n}\n")
        assert output == "north-south\n"
    for n in range(2, 100, 10):
        output = run_module("ch3", f"{n}\n")
        assert output == "east-west\n"
    # one print, no brackets, no and/elif
    code = getsource(modules.get("ch3"))
    assert code.count("\nprint(") == 1
    assert "[" not in code
    assert "and" not in code
    assert "elif" not in code


def test_ch4():
    from ch4 import quadratic
    # use approx to compare floating-point
    x1, x2 = quadratic(1, 4, -21)
    assert x1 == approx(3)
    assert x2 == approx(-7)
    x1, x2 = quadratic(-2.3, 4.5, 6.7)
    assert x1 == approx(-0.98898)
    assert x2 == approx(+2.94550)
    # make sure nothing is input or printed
    code = getsource(modules.get("ch4"))
    assert "input" not in code
    assert "print" not in code


def test_ch5():
    from ch5 import is_valid, is_unique
    # test the is_valid function
    assert is_valid((0, 0, 0))
    assert is_valid((69, 0, 132))
    assert is_valid((255, 255, 255))
    assert not is_valid((-1, 0, 0))
    assert not is_valid((0, -1, 0))
    assert not is_valid((0, 0, -1))
    assert not is_valid((256, 0, 0))
    assert not is_valid((0, 256, 0))
    assert not is_valid((0, 0, 256))
    # test the is_unique function
    assert is_unique([1, 2, 3, 4, 5])
    assert is_unique([100, -35, 17, 5678, -999])
    assert is_unique([0])
    assert is_unique([])
    assert not is_unique([1, 1])
    assert not is_unique([2, 1, 2])
    assert not is_unique([0, 1, -1, 1])
    assert not is_unique([4, 5, 6, 5, 4])
    # no if statements or loops
    code = getsource(modules.get("ch5"))
    assert "if" not in code
    assert "for" not in code
    assert "while" not in code


def test_ch6():
    from ch6 import count_words
    # make a copy in case the function modifies the words set
    magic = {"abracadabra", "alakazam", "hocus-pocus", "voila"}
    assert count_words("go dukes", magic.copy()) == 0
    assert count_words("and then she said voila", magic.copy()) == 1
    assert count_words("voila voila voila voila", magic.copy()) == 4
    assert count_words("abracadabra ha ha alakazam", magic.copy()) == 2
    assert count_words("", set()) == 0


def test_ch7():
    # make sure the code won't loop forever
    output = run_module("ch7", "STOP\n")
    assert output == ""
    # run the program with different inputs
    output = run_module("ch7", "super\nSTOP\n")
    assert output == "duper!\n" * 1
    output = run_module("ch7", "super fall\nis super\nSTOP\n")
    assert output == "duper!\n" * 2
    output = run_module("ch7", "super\nsuper\nsuper\nhero\nSTOP\n")
    assert output == "duper!\n" * 3
