diff options
Diffstat (limited to 'src/test/java/org')
6 files changed, 244 insertions, 31 deletions
diff --git a/src/test/java/org/zwobble/couscous/tests/BackendTests.java b/src/test/java/org/zwobble/couscous/tests/BackendTests.java new file mode 100644 index 0000000..86818af --- /dev/null +++ b/src/test/java/org/zwobble/couscous/tests/BackendTests.java @@ -0,0 +1,49 @@ +package org.zwobble.couscous.tests; + +import org.junit.Test; +import org.zwobble.couscous.ast.ClassNode; +import org.zwobble.couscous.ast.MethodNode; +import org.zwobble.couscous.ast.ReturnNode; +import org.zwobble.couscous.values.InterpreterValue; +import org.zwobble.couscous.values.StringValue; + +import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; +import static org.zwobble.couscous.ast.LiteralNode.literal; +import static org.zwobble.couscous.ast.MethodNode.staticMethod; +import static org.zwobble.couscous.values.UnitValue.UNIT; + +import lombok.val; + +public abstract class BackendTests { + @Test + public void methodWithNoStatementsReturnsUnit() { + val method = staticMethod("hello"); + val result = runMethod(method); + + assertEquals(UNIT, result); + } + + @Test + public void canReturnLiteralValue() { + val method = staticMethod("hello") + .statement(new ReturnNode(literal("hello, world!"))); + val result = runMethod(method); + + assertEquals(new StringValue("hello, world!"), result); + } + + protected InterpreterValue runMethod(MethodNode.MethodNodeBuilder methodBuilder, InterpreterValue... arguments) { + val method = methodBuilder.build(); + val className = "com.example.Program"; + val classNode = ClassNode.builder(className) + .method(method) + .build(); + + val runner = buildMethodRunner(); + return runner.runMethod(classNode, method.getName(), asList(arguments)); + } + + protected abstract MethodRunner buildMethodRunner(); + +} diff --git a/src/test/java/org/zwobble/couscous/tests/MethodRunner.java b/src/test/java/org/zwobble/couscous/tests/MethodRunner.java new file mode 100644 index 0000000..29c15fe --- /dev/null +++ b/src/test/java/org/zwobble/couscous/tests/MethodRunner.java @@ -0,0 +1,10 @@ +package org.zwobble.couscous.tests; + +import java.util.List; + +import org.zwobble.couscous.ast.ClassNode; +import org.zwobble.couscous.values.InterpreterValue; + +public interface MethodRunner { + InterpreterValue runMethod(ClassNode classNode, String methodName, List<InterpreterValue> arguments); +} diff --git a/src/test/java/org/zwobble/couscous/tests/backends/python/PythonCompilerTests.java b/src/test/java/org/zwobble/couscous/tests/backends/python/PythonCompilerTests.java new file mode 100644 index 0000000..7419aca --- /dev/null +++ b/src/test/java/org/zwobble/couscous/tests/backends/python/PythonCompilerTests.java @@ -0,0 +1,70 @@ +package org.zwobble.couscous.tests.backends.python; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.util.List; + +import org.zwobble.couscous.ast.ClassNode; +import org.zwobble.couscous.backends.python.PythonCompiler; +import org.zwobble.couscous.tests.BackendTests; +import org.zwobble.couscous.tests.MethodRunner; +import org.zwobble.couscous.values.InterpreterValue; + +import com.google.common.base.Charsets; +import com.google.common.io.CharStreams; + +import static java.lang.Integer.parseInt; +import static java.util.Arrays.asList; +import static org.zwobble.couscous.tests.util.ExtraFiles.deleteRecursively; +import static org.zwobble.couscous.values.InterpreterValues.UNIT; +import static org.zwobble.couscous.values.InterpreterValues.value; + +import lombok.SneakyThrows; +import lombok.val; + +public class PythonCompilerTests extends BackendTests { + @Override + protected MethodRunner buildMethodRunner() { + return new MethodRunner() { + @Override + @SneakyThrows + public InterpreterValue runMethod( + ClassNode classNode, + String methodName, + List<InterpreterValue> arguments) { + val compiler = new PythonCompiler(); + val directoryPath = Files.createTempDirectory(null); + try { + compiler.compile(asList(classNode), directoryPath); + + val program = "from " + classNode.getName() + " import " + classNode.getLocalName() + + ";print(repr(" + classNode.getLocalName() + "." + methodName + "()))"; + + val process = new ProcessBuilder("python3.4", "-c", program) + .directory(directoryPath.toFile()) + .start(); + val exitCode = process.waitFor(); + val output = readString(process.getInputStream()).trim(); + val stderrOutput = readString(process.getErrorStream()); + if (exitCode != 0) { + throw new RuntimeException("stderr was: " + stderrOutput); + } else if (output.equals("None")) { + return UNIT; + } else if (output.startsWith("'")) { + return value(output.substring(1, output.length() - 1)); + } else { + return value(parseInt(output)); + } + } finally { + deleteRecursively(directoryPath.toFile()); + } + } + + private String readString(final InputStream stream) throws IOException { + return CharStreams.toString(new InputStreamReader(stream, Charsets.UTF_8)); + } + }; + } +} diff --git a/src/test/java/org/zwobble/couscous/tests/backends/python/PythonSerializerTests.java b/src/test/java/org/zwobble/couscous/tests/backends/python/PythonSerializerTests.java new file mode 100644 index 0000000..4f9b63b --- /dev/null +++ b/src/test/java/org/zwobble/couscous/tests/backends/python/PythonSerializerTests.java @@ -0,0 +1,80 @@ +package org.zwobble.couscous.tests.backends.python; + +import org.junit.Test; +import org.zwobble.couscous.backends.python.ast.PythonClassNode; + +import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; +import static org.zwobble.couscous.backends.python.PythonSerializer.serialize; +import static org.zwobble.couscous.backends.python.ast.PythonFunctionDefinitionNode.pythonFunctionDefinition; +import static org.zwobble.couscous.backends.python.ast.PythonIntegerLiteralNode.pythonIntegerLiteral; +import static org.zwobble.couscous.backends.python.ast.PythonModuleNode.pythonModule; +import static org.zwobble.couscous.backends.python.ast.PythonPassNode.PASS; +import static org.zwobble.couscous.backends.python.ast.PythonReturnNode.pythonReturn; +import static org.zwobble.couscous.backends.python.ast.PythonStringLiteralNode.pythonStringLiteral; + +import lombok.val; + +public class PythonSerializerTests { + @Test + public void integersAreNotBoxed() { + val output = serialize(pythonIntegerLiteral(42)); + assertEquals("42", output); + } + + @Test + public void stringsAreNotBoxed() { + // TODO: escaping + val output = serialize(pythonStringLiteral("blah")); + assertEquals("\"blah\"", output); + } + + @Test + public void returnKeywordIsUsedForReturns() { + val output = serialize(pythonReturn(pythonIntegerLiteral(42))); + assertEquals("return 42\n", output); + } + + @Test + public void passStatementIsSerializedAsPassKeyword() { + val output = serialize(PASS); + assertEquals("pass\n", output); + } + + @Test + public void emptyFunctionHasPassStatement() { + val output = serialize(pythonFunctionDefinition("empty", asList())); + assertEquals("def empty():\n pass\n", output); + } + + @Test + public void emptyClassHasPassStatement() { + val classNode = PythonClassNode.builder("Empty") + .build(); + val output = serialize(classNode); + assertEquals("class Empty(object):\n pass\n", output); + } + + @Test + public void moduleIsSerializedStatements() { + val classNode = pythonModule(asList(PASS)); + val output = serialize(classNode); + assertEquals("pass\n", output); + } + + @Test + public void bodiesOfBlocksAreIndented() { + val classNode = PythonClassNode.builder("Foo") + .statement(pythonFunctionDefinition("one", asList())) + .statement(pythonFunctionDefinition("two", asList())) + .build(); + val output = serialize(classNode); + val expectedOutput = + "class Foo(object):\n" + + " def one():\n" + + " pass\n" + + " def two():\n" + + " pass\n"; + assertEquals(expectedOutput, output); + } +} diff --git a/src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterTests.java b/src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterTests.java index d0c8976..98271f7 100644 --- a/src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterTests.java +++ b/src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterTests.java @@ -1,18 +1,21 @@ package org.zwobble.couscous.tests.interpreter; +import java.util.List; + import org.junit.Test; import org.zwobble.couscous.MapBackedProject; import org.zwobble.couscous.ast.Assignment; import org.zwobble.couscous.ast.ClassNode; import org.zwobble.couscous.ast.ExpressionStatementNode; 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.UnboundVariable; import org.zwobble.couscous.interpreter.UnexpectedValueType; import org.zwobble.couscous.interpreter.VariableNotInScope; import org.zwobble.couscous.interpreter.WrongNumberOfArguments; +import org.zwobble.couscous.tests.BackendTests; +import org.zwobble.couscous.tests.MethodRunner; import org.zwobble.couscous.values.ConcreteType; import org.zwobble.couscous.values.IntegerValue; import org.zwobble.couscous.values.InterpreterValue; @@ -20,7 +23,6 @@ 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.FormalArgumentNode.formalArg; import static org.zwobble.couscous.ast.LiteralNode.literal; @@ -29,28 +31,10 @@ import static org.zwobble.couscous.ast.MethodNode.staticMethod; import static org.zwobble.couscous.ast.VariableDeclaration.var; 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 methodWithNoStatementsReturnsUnit() { - val method = staticMethod("hello"); - val result = runMethod(method); - - assertEquals(UNIT, result); - } - - @Test - public void canReturnLiteralValue() { - val method = staticMethod("hello") - .statement(new ReturnNode(LiteralNode.literal("hello, world!"))); - val result = runMethod(method); - - assertEquals(new StringValue("hello, world!"), result); - } - +public class InterpreterTests extends BackendTests { @Test public void canPassValueToMethod() { val arg = formalArg(var(42, "x", StringValue.REF)); @@ -162,15 +146,19 @@ public class InterpreterTests { assertEquals(new UnboundVariable(42), exception); } - private InterpreterValue runMethod(MethodNode.MethodNodeBuilder methodBuilder, InterpreterValue... arguments) { - val method = methodBuilder.build(); - val className = "com.example.Program"; - val classNode = ClassNode.builder(className) - .method(method) - .build(); - val interpreter = new Interpreter(new MapBackedProject(ImmutableMap.of( - className, ConcreteType.fromNode(classNode)))); - - return interpreter.run(className, method.getName(), asList(arguments)); + @Override + protected MethodRunner buildMethodRunner() { + return new MethodRunner() { + @Override + public InterpreterValue runMethod( + ClassNode classNode, + String methodName, + List<InterpreterValue> arguments) { + val interpreter = new Interpreter(new MapBackedProject(ImmutableMap.of( + classNode.getName(), ConcreteType.fromNode(classNode)))); + + return interpreter.run(classNode.getName(), methodName, arguments); + } + }; } } diff --git a/src/test/java/org/zwobble/couscous/tests/util/ExtraFiles.java b/src/test/java/org/zwobble/couscous/tests/util/ExtraFiles.java new file mode 100644 index 0000000..032e22c --- /dev/null +++ b/src/test/java/org/zwobble/couscous/tests/util/ExtraFiles.java @@ -0,0 +1,16 @@ +package org.zwobble.couscous.tests.util; + +import java.io.File; + +import lombok.val; + +public class ExtraFiles { + public static void deleteRecursively(File file) { + if (file.isDirectory()) { + for (val child : file.listFiles()) { + deleteRecursively(child); + } + } + file.delete(); + } +} |
