Mike Cifelli
64e18fe076
Added unit tests and cleaned up some code The setup for acceptance tests now cleans up the environment
171 lines
5.3 KiB
Java
171 lines
5.3 KiB
Java
package function.builtin.special;
|
|
|
|
import static org.junit.Assert.assertTrue;
|
|
import static testutil.TestUtilities.*;
|
|
|
|
import java.io.*;
|
|
|
|
import org.junit.*;
|
|
|
|
import environment.RuntimeEnvironment;
|
|
import error.ErrorManager;
|
|
import function.ArgumentValidator.*;
|
|
import function.UserDefinedFunction.IllegalKeywordRestPositionException;
|
|
import table.FunctionTable;
|
|
|
|
public class DEFUNTester {
|
|
|
|
private ByteArrayOutputStream outputStream;
|
|
private RuntimeEnvironment environment;
|
|
|
|
public DEFUNTester() {
|
|
this.environment = RuntimeEnvironment.getInstance();
|
|
}
|
|
|
|
private void assertSomethingPrinted() {
|
|
assertTrue(outputStream.toByteArray().length > 0);
|
|
}
|
|
|
|
@Before
|
|
public void setUp() {
|
|
outputStream = new ByteArrayOutputStream();
|
|
|
|
environment.setOutput(new PrintStream(outputStream));
|
|
environment.setErrorManager(new ErrorManager());
|
|
environment.setWarningOutputDecorator(s -> s);
|
|
|
|
FunctionTable.reset();
|
|
}
|
|
|
|
@After
|
|
public void tearDown() {
|
|
FunctionTable.reset();
|
|
}
|
|
|
|
@Test
|
|
public void defun() {
|
|
String input = "(defun f () t)";
|
|
|
|
assertSExpressionsMatch(parseString("f"), evaluateString(input));
|
|
assertSExpressionsMatch(parseString("t"), evaluateString("(f)"));
|
|
}
|
|
|
|
@Test
|
|
public void defunWithEmptyBody() {
|
|
String input = "(defun f ())";
|
|
|
|
assertSExpressionsMatch(parseString("f"), evaluateString(input));
|
|
assertSExpressionsMatch(parseString("()"), evaluateString("(f)"));
|
|
}
|
|
|
|
@Test
|
|
public void defunEvaluatesArguments() {
|
|
evaluateString("(defun f (x) (car x))");
|
|
assertSExpressionsMatch(parseString("1"), evaluateString("(f '(1 2 3))"));
|
|
}
|
|
|
|
@Test
|
|
public void defunRecursiveFunction() {
|
|
evaluateString("(defun fact (x) (if (< x 2) 1 (* x (fact (- x 1)))))");
|
|
assertSExpressionsMatch(parseString("120"), evaluateString("(fact 5)"));
|
|
}
|
|
|
|
@Test
|
|
public void defunTailRecursiveFunction() {
|
|
evaluateString("(defun fact-tail (x acc) (if (< x 2) acc (fact-tail (- x 1) (* x acc))))");
|
|
assertSExpressionsMatch(parseString("120"), evaluateString("(fact-tail 5 1)"));
|
|
}
|
|
|
|
@Test
|
|
public void defunSimpleClass() {
|
|
evaluateString("(defun counter-class () (let ((counter 0)) (lambda () (setf counter (+ 1 counter)))))");
|
|
evaluateString("(setf my-counter (counter-class))");
|
|
|
|
assertSExpressionsMatch(parseString("1"), evaluateString("(funcall my-counter)"));
|
|
assertSExpressionsMatch(parseString("2"), evaluateString("(funcall my-counter)"));
|
|
assertSExpressionsMatch(parseString("3"), evaluateString("(funcall my-counter)"));
|
|
assertSExpressionsMatch(parseString("4"), evaluateString("(funcall my-counter)"));
|
|
}
|
|
|
|
@Test
|
|
public void redefineFunction_DisplaysWarning() {
|
|
String input = "(defun myFunction () nil)";
|
|
evaluateString(input);
|
|
evaluateString(input);
|
|
|
|
assertSomethingPrinted();
|
|
}
|
|
|
|
@Test
|
|
public void redefineFunction_ActuallyRedefinesFunction() {
|
|
evaluateString("(defun myFunction2 () nil)");
|
|
evaluateString("(defun myFunction2 () T)");
|
|
|
|
assertSomethingPrinted();
|
|
assertSExpressionsMatch(parseString("t"), evaluateString("(myFunction2)"));
|
|
}
|
|
|
|
@Test(expected = DottedArgumentListException.class)
|
|
public void defunWithDottedLambdaList() {
|
|
evaluateString("(funcall 'defun 'x (cons 'a 'b) ())");
|
|
}
|
|
|
|
@Test(expected = BadArgumentTypeException.class)
|
|
public void defunWithNonSymbolName() {
|
|
evaluateString("(defun 1 () ())");
|
|
}
|
|
|
|
@Test(expected = BadArgumentTypeException.class)
|
|
public void defunWithBadLambdaList() {
|
|
evaluateString("(defun x a ())");
|
|
}
|
|
|
|
@Test(expected = TooFewArgumentsException.class)
|
|
public void defunWithTooFewArguments() {
|
|
evaluateString("(defun x)");
|
|
}
|
|
|
|
@Test(expected = TooFewArgumentsException.class)
|
|
public void defunFunctionAndCallWithTooFewArguments() {
|
|
evaluateString("(defun x (a b))");
|
|
evaluateString("(x 'a)");
|
|
}
|
|
|
|
@Test(expected = TooManyArgumentsException.class)
|
|
public void defunFunctionAndCallWithTooManyArguments() {
|
|
evaluateString("(defun x (a b))");
|
|
evaluateString("(x 'a 'b 'c)");
|
|
}
|
|
|
|
@Test
|
|
public void defunWithKeywordRestParameter() {
|
|
evaluateString("(defun f (&rest x) (car x))");
|
|
assertSExpressionsMatch(parseString("1"), evaluateString("(f 1 2 3 4 5)"));
|
|
}
|
|
|
|
@Test
|
|
public void defunWithNormalAndKeywordRestParameter() {
|
|
evaluateString("(defun 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 defunWithParametersFollowingKeywordRest() {
|
|
evaluateString("(defun f (a &rest b c) (cons a b))");
|
|
evaluateString("(f 1 2 3)");
|
|
}
|
|
|
|
@Test
|
|
public void defunWithKeywordRest_CallWithNoArguments() {
|
|
evaluateString("(defun f (&rest a) (car a))");
|
|
assertSExpressionsMatch(parseString("nil"), evaluateString("(f)"));
|
|
}
|
|
|
|
@Test(expected = TooFewArgumentsException.class)
|
|
public void defunWithNormalAndKeywordRest_CallWithNoArguments() {
|
|
evaluateString("(defun f (a &rest b) a)");
|
|
evaluateString("(f)");
|
|
}
|
|
|
|
}
|