transcendental-lisp/test/function/builtin/special/RECURTest.java

140 lines
5.3 KiB
Java

package function.builtin.special;
import static org.junit.Assert.fail;
import static testutil.TestUtilities.assertSExpressionsMatch;
import static testutil.TestUtilities.evaluateString;
import static testutil.TestUtilities.parseString;
import org.junit.Test;
import function.ArgumentValidator.BadArgumentTypeException;
import function.ArgumentValidator.TooManyArgumentsException;
import function.builtin.EVAL.RecurNotInTailPositionException;
import function.builtin.special.RECUR.NestedRecurException;
import function.builtin.special.RECUR.RecurOutsideOfFunctionException;
import testutil.SymbolAndFunctionCleaner;
public class RECURTest extends SymbolAndFunctionCleaner {
@Test(expected = RecurOutsideOfFunctionException.class)
public void recurOutsideOfFunction_ThrowsException() {
evaluateString("(recur)");
}
@Test(expected = RecurOutsideOfFunctionException.class)
public void recurOutsideOfFunction_AfterFunctionCall_ThrowsException() {
evaluateString("(defun f (n) (if (= n 0) 'ZERO n))");
evaluateString("(f 2)");
evaluateString("(recur)");
}
@Test(expected = BadArgumentTypeException.class)
public void recurInSpecialFunction_DoesNotEvaluateArguments() {
evaluateString("(define-special tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))");
evaluateString("(tail-recursive 900)");
}
@Test(expected = BadArgumentTypeException.class)
public void recurInMacro_DoesNotEvaluateArguments() {
evaluateString("(defmacro tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))");
evaluateString("(tail-recursive 900)");
}
@Test(expected = NestedRecurException.class)
public void nestedRecur_ThrowsException() {
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (recur (- n 1)))))");
evaluateString("(tail-recursive 900)");
}
@Test(expected = TooManyArgumentsException.class)
public void functionCallValidatesRecurArguments() {
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1) 23)))");
evaluateString("(tail-recursive 900)");
}
@Test(expected = RecurNotInTailPositionException.class)
public void recurInNonTailPosition_ThrowsException() {
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (list (recur (- n 1)))))");
evaluateString("(tail-recursive 900)");
}
@Test(expected = RecurNotInTailPositionException.class)
public void recurInNonTailPosition2_ThrowsException() {
evaluateString("(defun tail-recursive (n) (begin (recur) 2))");
evaluateString("(tail-recursive 900)");
}
@Test
public void recurCallsCurrentFunction() {
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))");
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"));
}
@Test
public void recurCallsCurrentFunction_InBegin() {
evaluateString("(defun tail-recursive (n) (if (> n 1) (begin 1 2 (recur (- n 1))) 'PASS))");
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"));
}
@Test
public void recurCallsCurrentFunction_WithApply() {
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))");
assertSExpressionsMatch(parseString("PASS"), evaluateString("(apply 'tail-recursive '(900))"));
}
@Test
public void recurCallsCurrentFunction_WithFuncall() {
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))");
assertSExpressionsMatch(parseString("PASS"), evaluateString("(call 'tail-recursive '900)"));
}
@Test
public void recurWorksAfterFailure() {
evaluateString("(defun bad-tail-recursive (n) (if (= n 0) 'PASS (list (recur (- n 1)))))");
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))");
try {
evaluateString("(bad-tail-recursive 900)");
fail("expectedException");
} catch (RecurNotInTailPositionException e) {}
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"));
}
@Test
public void recurWorksAfterFailure2() {
evaluateString("(defun bad-tail-recursive (n) (begin (recur) 2))");
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))");
try {
evaluateString("(bad-tail-recursive 900)");
fail("expectedException");
} catch (RecurNotInTailPositionException e) {}
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"));
}
@Test
public void recurWorksAfterNestedFailure() {
evaluateString("(defun bad-tail-recursive (n) (if (= n 0) 'PASS (recur (recur (- n 1)))))");
evaluateString("(defun tail-recursive (n) (if (= n 0) 'PASS (recur (- n 1))))");
try {
evaluateString("(bad-tail-recursive 900)");
fail("expectedException");
} catch (NestedRecurException e) {}
assertSExpressionsMatch(parseString("PASS"), evaluateString("(tail-recursive 900)"));
}
// recur non-tail in apply call
// recur with no args, alters global variable
// recur with anonymous function
// recur with nested anonymous function
// test scope after failure in function, is it global again?
}