package function.builtin.special;

import static testutil.TestUtilities.*;

import org.junit.Test;

import function.ArgumentValidator.*;

public class CONDTester {

    @Test
    public void testCondWithNoArguments() {
        String input = "(cond)";

        assertSExpressionsMatch(parseString("nil"), evaluateString(input));
    }

    @Test
    public void testCondWithTrue() {
        String input = "(cond (T))";

        assertSExpressionsMatch(parseString("T"), evaluateString(input));
    }

    @Test
    public void testCondWithNumber() {
        String input = "(cond ((+ 1 2)))";

        assertSExpressionsMatch(parseString("3"), evaluateString(input));
    }

    @Test
    public void testCondWithSingleExpression() {
        String input = "(cond (T \"true\"))";

        assertSExpressionsMatch(parseString("\"true\""), evaluateString(input));
    }

    @Test
    public void testCondWithMultipleExpressions() {
        String input = "(cond ((= 1 2) 2) ((= 1 2) 2) ((= 1 1) 3))";

        assertSExpressionsMatch(parseString("3"), evaluateString(input));
    }

    @Test
    public void testCondWithMultipleConditionsMatching_ReturnFirstOne() {
        String input = "(cond ((= 1 1) 2) ((= 1 1) 3))";

        assertSExpressionsMatch(parseString("2"), evaluateString(input));
    }

    @Test
    public void testCondWithMultipleConditionsMatching_OnlyEvaluatesFirstOne() {
        String input = "(cond ((= 1 1) 2) ((= 1 1) x))";

        assertSExpressionsMatch(parseString("2"), evaluateString(input));
    }

    @Test
    public void testCondWithMultipleResultValues_OnlyReturnsLast() {
        String input = "(cond ((= 1 1) 2 3 4))";

        assertSExpressionsMatch(parseString("4"), evaluateString(input));
    }

    @Test
    public void testCondWithNoConditionMatching_ReturnsNil() {
        String input = "(cond ((= 1 2) T) ((= 1 3) T))";

        assertSExpressionsMatch(parseString("nil"), evaluateString(input));
    }

    @Test(expected = BadArgumentTypeException.class)
    public void testCondWithNilArgument_ThrowsException() {
        evaluateString("(cond ())");
    }

    @Test(expected = BadArgumentTypeException.class)
    public void testCondWithNonListArgument_ThrowsException() {
        evaluateString("(cond o)");
    }

    @Test(expected = DottedArgumentListException.class)
    public void testCondWithDottedArgumentList_ThrowsException() {
        evaluateString("(apply 'cond (cons '(nil T) 'b))");
    }

}
