package function.builtin.special; import static org.junit.Assert.assertTrue; import static table.FunctionTable.resetFunctionTable; import static testutil.TestUtilities.*; import java.io.*; import org.junit.*; import environment.RuntimeEnvironment; import error.ErrorManager; import function.ArgumentValidator.*; import function.UserDefinedFunction.IllegalKeywordRestPositionException; public class DEFINE_SPECIALTester { private ByteArrayOutputStream outputStream; private RuntimeEnvironment environment; public DEFINE_SPECIALTester() { this.environment = RuntimeEnvironment.getInstance(); } private void assertSomethingPrinted() { assertTrue(outputStream.toByteArray().length > 0); } @Before public void setUp() { outputStream = new ByteArrayOutputStream(); environment.reset(); environment.setOutput(new PrintStream(outputStream)); environment.setErrorManager(new ErrorManager()); environment.setWarningOutputDecorator(s -> s); resetFunctionTable(); } @After public void tearDown() { environment.reset(); resetFunctionTable(); } @Test public void defineSpecial() { String input = "(define-special f () t)"; assertSExpressionsMatch(parseString("f"), evaluateString(input)); assertSExpressionsMatch(parseString("t"), evaluateString("(f)")); } @Test public void defineSpecialWithEmptyBody() { String input = "(define-special f ())"; assertSExpressionsMatch(parseString("f"), evaluateString(input)); assertSExpressionsMatch(parseString("()"), evaluateString("(f)")); } @Test public void defineSpecialDoesNotEvaluateArguments() { evaluateString("(define-special f (x) (car x))"); assertSExpressionsMatch(parseString("quote"), evaluateString("(f '(1 2 3))")); } @Test public void defineSpecialAdd() { evaluateString("(define-special f (x) (+ (eval x) 23))"); assertSExpressionsMatch(parseString("27"), evaluateString("(f (+ 2 2))")); } @Test public void defineSpecialSetVariable() { evaluateString("(define-special f (x) (set x 23))"); evaluateString("(f y)"); assertSExpressionsMatch(parseString("23"), evaluateString("y")); } @Test public void defineSpecialVariableCapture() { evaluateString("(setq x 0)"); evaluateString("(define-special f (x) (set x 23))"); evaluateString("(f x)"); assertSExpressionsMatch(parseString("0"), evaluateString("x")); } @Test public void defineSpecialAvoidVariableCaptureConvention() { evaluateString("(setq x 0)"); evaluateString("(define-special f (-x-) (set -x- 23))"); evaluateString("(f x)"); assertSExpressionsMatch(parseString("23"), evaluateString("x")); } @Test public void redefineSpecial_DisplaysWarning() { String input = "(define-special myFunction () nil)"; evaluateString(input); evaluateString(input); assertSomethingPrinted(); } @Test public void redefineSpecial_ActuallyRedefinesSpecialFunction() { evaluateString("(define-special mySpecialFunction () nil)"); evaluateString("(define-special mySpecialFunction () T)"); assertSomethingPrinted(); assertSExpressionsMatch(parseString("t"), evaluateString("(mySpecialFunction)")); } @Test(expected = DottedArgumentListException.class) public void defineSpecialWithDottedLambdaList() { evaluateString("(funcall 'define-special 'x (cons 'a 'b) ())"); } @Test(expected = BadArgumentTypeException.class) public void defineSpecialWithNonSymbolName() { evaluateString("(define-special 1 () ())"); } @Test(expected = BadArgumentTypeException.class) public void defineSpecialWithBadLambdaList() { evaluateString("(define-special x a ())"); } @Test(expected = TooFewArgumentsException.class) public void defineSpecialWithTooFewArguments() { evaluateString("(define-special x)"); } @Test(expected = TooFewArgumentsException.class) public void defineSpecialAndCallWithTooFewArguments() { evaluateString("(define-special x (a b))"); evaluateString("(x a)"); } @Test(expected = TooManyArgumentsException.class) public void defineSpecialAndCallWithTooManyArguments() { evaluateString("(define-special x (a b))"); evaluateString("(x a b c)"); } @Test public void defineSpecialWithKeywordRestParameter() { evaluateString("(define-special f (&rest x) (car x))"); assertSExpressionsMatch(parseString("1"), evaluateString("(f 1 2 3 4 5)")); } @Test public void defineSpecialWithNormalAndKeywordRestParameter() { evaluateString("(define-special f (a &rest b) (cons a b))"); assertSExpressionsMatch(parseString("(1 2 3 4 5)"), evaluateString("(f 1 2 3 4 5)")); } @Test(expected = IllegalKeywordRestPositionException.class) public void defineSpecialWithParametersFollowingKeywordRest() { evaluateString("(define-special f (a &rest b c) (cons a b))"); evaluateString("(f 1 2 3)"); } @Test public void defineSpecialWithKeywordRest_CallWithNoArguments() { evaluateString("(define-special f (&rest a) (car a))"); assertSExpressionsMatch(parseString("nil"), evaluateString("(f)")); } @Test(expected = TooFewArgumentsException.class) public void defineSpecialWithNormalAndKeywordRest_CallWithNoArguments() { evaluateString("(define-special f (a &rest b) a)"); evaluateString("(f)"); } @Test public void resultOfSpecialFunctionIsNotEvaluated() { evaluateString("(setq x 'grains)"); evaluateString("(define-special f (x) x)"); evaluateString("(f (setq x 'sprouts))"); assertSExpressionsMatch(parseString("grains"), evaluateString("x")); } }