aboutsummaryrefslogtreecommitdiff
path: root/src/test/java/org/zwobble
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2015-09-29 20:14:34 +0100
committerMichael Williamson <mike@zwobble.org>2015-09-29 20:14:34 +0100
commitccac9247fe4663874be8026ab9cc968ecec7157b (patch)
tree6e15ebf9eb130034c45f84372ce4d3776f401328 /src/test/java/org/zwobble
Initial commit
Diffstat (limited to 'src/test/java/org/zwobble')
-rw-r--r--src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterTests.java117
-rw-r--r--src/test/java/org/zwobble/couscous/tests/util/ExtraAsserts.java21
2 files changed, 138 insertions, 0 deletions
diff --git a/src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterTests.java b/src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterTests.java
new file mode 100644
index 0000000..d3bc21c
--- /dev/null
+++ b/src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterTests.java
@@ -0,0 +1,117 @@
+package org.zwobble.couscous.tests.interpreter;
+
+import java.util.Map;
+
+import org.junit.Test;
+import org.zwobble.couscous.Project;
+import org.zwobble.couscous.ast.Assignment;
+import org.zwobble.couscous.ast.ClassNode;
+import org.zwobble.couscous.ast.ExpressionStatementNode;
+import org.zwobble.couscous.ast.FormalArgumentNode;
+import org.zwobble.couscous.ast.LiteralNode;
+import org.zwobble.couscous.ast.MethodNode;
+import org.zwobble.couscous.ast.ReturnNode;
+import org.zwobble.couscous.interpreter.Interpreter;
+import org.zwobble.couscous.interpreter.WrongNumberOfArguments;
+import org.zwobble.couscous.values.InterpreterValue;
+import org.zwobble.couscous.values.StringValue;
+
+import com.google.common.collect.ImmutableMap;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertEquals;
+import static org.zwobble.couscous.ast.MethodNode.staticMethod;
+import static org.zwobble.couscous.ast.VariableReferenceNode.reference;
+import static org.zwobble.couscous.tests.util.ExtraAsserts.assertThrows;
+import static org.zwobble.couscous.values.UnitValue.UNIT;
+
+import lombok.val;
+
+public class InterpreterTests {
+ @Test
+ public void canReturnLiteralValue() {
+ val method = staticMethod("hello")
+ .statement(new ReturnNode(LiteralNode.of("hello, world!")));
+ val result = runMethod(method);
+
+ assertEquals(new StringValue("hello, world!"), result);
+ }
+
+ @Test
+ public void canPassValueToMethod() {
+ val arg = new FormalArgumentNode(42, "x");
+ val method = staticMethod("hello")
+ .argument(arg)
+ .statement(new ReturnNode(reference(arg)));
+ val result = runMethod(method, new StringValue("hello, world!"));
+
+ assertEquals(new StringValue("hello, world!"), result);
+ }
+
+ @Test
+ public void errorIfWrongNumberOfArgumentsArePassed() {
+ val method = staticMethod("hello");
+
+ val exception = assertThrows(WrongNumberOfArguments.class,
+ () -> runMethod(method, new StringValue("hello, world!")));
+
+ assertEquals(new WrongNumberOfArguments(0, 1), exception);
+ }
+
+ @Test
+ public void canReassignValueToArgument() {
+ val arg = new FormalArgumentNode(42, "x");
+ val method = staticMethod("hello")
+ .argument(arg)
+ .statement(new ExpressionStatementNode(new Assignment(reference(arg), LiteralNode.of("[updated value]"))))
+ .statement(new ReturnNode(reference(arg)));
+ val result = runMethod(method, new StringValue("[initial value]"));
+
+ assertEquals(new StringValue("[updated value]"), result);
+ }
+
+ @Test
+ public void valueOfAssignmentExpressionIsNewValue() {
+ val arg = new FormalArgumentNode(42, "x");
+ val method = staticMethod("hello")
+ .argument(arg)
+ .statement(new ReturnNode(new Assignment(reference(arg), LiteralNode.of("[updated value]"))));
+ val result = runMethod(method, new StringValue("[initial value]"));
+
+ assertEquals(new StringValue("[updated value]"), result);
+ }
+
+ @Test
+ public void methodWithNoStatementsReturnsUnit() {
+ val method = staticMethod("hello");
+ val result = runMethod(method);
+
+ assertEquals(UNIT, result);
+ }
+
+ private InterpreterValue runMethod(MethodNode.MethodNodeBuilder methodBuilder, InterpreterValue... arguments) {
+ val method = methodBuilder.build();
+ val classNode = ClassNode.builder()
+ .method(method)
+ .build();
+ val className = "com.example.Program";
+ val interpreter = new Interpreter(new MapBackedProject(ImmutableMap.of(
+ className, classNode)));
+
+ return interpreter.run(className, method.getName(), asList(arguments));
+ }
+
+ private static class MapBackedProject implements Project {
+ private Map<String, ClassNode> classes;
+
+ public MapBackedProject(Map<String, ClassNode> classes) {
+ this.classes = classes;
+ }
+
+ @Override
+ public ClassNode findClass(String name) {
+ return classes.get(name);
+ }
+
+ }
+}
diff --git a/src/test/java/org/zwobble/couscous/tests/util/ExtraAsserts.java b/src/test/java/org/zwobble/couscous/tests/util/ExtraAsserts.java
new file mode 100644
index 0000000..8b1c16c
--- /dev/null
+++ b/src/test/java/org/zwobble/couscous/tests/util/ExtraAsserts.java
@@ -0,0 +1,21 @@
+package org.zwobble.couscous.tests.util;
+
+public class ExtraAsserts {
+ public interface Action {
+ void run();
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T extends Throwable> T assertThrows(Class<T> exceptionType, Action action) {
+ try {
+ action.run();
+ throw new AssertionError("Expected exception");
+ } catch (Exception exception) {
+ if (exceptionType.isInstance(exception)) {
+ return (T)exception;
+ } else {
+ throw exception;
+ }
+ }
+ }
+}