package interpreter; import static error.ErrorManager.Severity.CRITICAL; import static interpreter.InteractiveLispInterpreter.PROMPT; import static java.text.MessageFormat.format; import static org.junit.Assert.*; import static testutil.TestUtilities.createInputStreamFromString; import java.io.*; import java.util.*; import org.junit.*; import environment.RuntimeEnvironment; import interpreter.LispInterpreterBuilderImpl.InterpreterAlreadyBuiltException; public class LispInterpreterTest { private static final String TERMINATED = "terminated"; private Set indicatorSet; private ByteArrayOutputStream outputStream; private ByteArrayOutputStream errorOutputStream; private RuntimeEnvironment environment; private LispInterpreterBuilder builder; public LispInterpreterTest() { this.environment = RuntimeEnvironment.getInstance(); this.builder = new LispInterpreterBuilderImpl() { @Override public void reset() { this.isBuilt = false; } }; } private void setCommonFeatures() { builder.setOutput(new PrintStream(outputStream)); builder.setErrorOutput(new PrintStream(errorOutputStream)); builder.setTerminationFunction(() -> {}); builder.setErrorTerminationFunction(() -> indicatorSet.add(TERMINATED)); } private void assertTerminated() { assertTrue(indicatorSet.contains(TERMINATED)); } private void assertErrorMessageWritten() { assertTrue(errorOutputStream.toByteArray().length > 0); } @Before public void setUp() { indicatorSet = new HashSet<>(); outputStream = new ByteArrayOutputStream(); errorOutputStream = new ByteArrayOutputStream(); builder.reset(); environment.reset(); } @After public void tearDown() { builder.reset(); environment.reset(); } @Test public void buildInteractiveInterpreter() { setCommonFeatures(); builder.setInput(System.in, "stdin"); LispInterpreter interpreter = builder.build(); assertTrue(interpreter instanceof InteractiveLispInterpreter); } @Test public void buildNonInteractiveInterpreter() { setCommonFeatures(); builder.setInput(System.in, "stdin"); builder.setNotInteractive(); LispInterpreter interpreter = builder.build(); assertFalse(interpreter instanceof InteractiveLispInterpreter); assertFalse(interpreter instanceof FileLispInterpreter); } @Test public void buildFileBasedInterpreter() { setCommonFeatures(); builder.useFile("test/interpreter/test-files/file.lisp"); LispInterpreter interpreter = builder.build(); assertTrue(interpreter instanceof FileLispInterpreter); } @Test(expected = InterpreterAlreadyBuiltException.class) public void cannotBuildMoreThanOneInterpreter() { builder.build(); builder.build(); } @Test public void interpreterAlreadyBuiltException_HasCorrectAttributes() { InterpreterAlreadyBuiltException e = new InterpreterAlreadyBuiltException(); assertEquals(CRITICAL, e.getSeverity()); assertNotNull(e.getMessage()); assertTrue(e.getMessage().length() > 0); } @Test(expected = InterpreterAlreadyBuiltException.class) public void resetNormallyDoesNothing() { builder = new LispInterpreterBuilderImpl(); builder.build(); builder.reset(); builder.build(); } @Test public void attemptToBuildInterpreterOnBadFile() { setCommonFeatures(); builder.useFile("test/interpreter/test-files/does-not-exist.lisp"); builder.build(); assertErrorMessageWritten(); assertTerminated(); } @Test public void makeSureDecoratorsAreInitializedWithDefaults() { builder.build(); assertEquals("", environment.decoratePrompt("")); assertEquals("", environment.decorateValueOutput("")); assertEquals("", environment.decorateWarningOutput("")); assertEquals("", environment.decorateErrorOutput("")); assertEquals("", environment.decorateCriticalOutput("")); } @Test public void makeSureDecoratorsAreSetCorrectly() { builder.setPromptDecorator(s -> "#" + s + "#"); builder.setValueOutputDecorator(s -> "@" + s + "@"); builder.setWarningOutputDecorator(s -> "%" + s + "%"); builder.setErrorOutputDecorator(s -> "*" + s + "*"); builder.setCriticalOutputDecorator(s -> "$" + s + "$"); builder.build(); assertEquals("#x#", environment.decoratePrompt("x")); assertEquals("@x@", environment.decorateValueOutput("x")); assertEquals("%x%", environment.decorateWarningOutput("x")); assertEquals("*x*", environment.decorateErrorOutput("x")); assertEquals("$x$", environment.decorateCriticalOutput("x")); } @Test public void fileBasedInterpreterWorks_PrintsLastValueOnly() { setCommonFeatures(); builder.useFile("test/interpreter/test-files/file.lisp"); builder.build().interpret(); assertEquals("PICKLE\n\n", outputStream.toString()); assertEquals("", errorOutputStream.toString()); } @Test public void interactiveInterpreterWorks() { setCommonFeatures(); builder.setInput(createInputStreamFromString("'pickle"), "input"); builder.build().interpret(); assertEquals(format("{0}\n{1}\n{0}\n", PROMPT, "PICKLE"), outputStream.toString()); assertEquals("", errorOutputStream.toString()); } @Test public void interpreterHandlesError() { setCommonFeatures(); builder.setNotInteractive(); builder.setInput(createInputStreamFromString("pickle"), "input"); builder.build().interpret(); assertEquals("\n", outputStream.toString()); assertEquals("[error] symbol PICKLE has no value\n", errorOutputStream.toString()); } }