162 lines
5.8 KiB
Kotlin
162 lines
5.8 KiB
Kotlin
package interpreter
|
|
|
|
import environment.RuntimeEnvironment
|
|
import interpreter.InteractiveLispInterpreter.Companion.PROMPT
|
|
import org.assertj.core.api.Assertions.assertThat
|
|
import org.junit.jupiter.api.AfterEach
|
|
import org.junit.jupiter.api.BeforeEach
|
|
import org.junit.jupiter.api.Test
|
|
import testutil.TestUtilities.createInputStreamFromString
|
|
import java.io.ByteArrayOutputStream
|
|
import java.io.PrintStream
|
|
|
|
class LispInterpreterTest {
|
|
|
|
companion object {
|
|
private const val TERMINATED = "terminated"
|
|
private val FILE = LispInterpreterTest::class.java.getResource("file.lisp").file
|
|
}
|
|
|
|
private val indicatorSet = mutableSetOf<String>()
|
|
private val outputStream = ByteArrayOutputStream()
|
|
private val errorOutputStream = ByteArrayOutputStream()
|
|
|
|
private fun setCommonFeatures() {
|
|
LispInterpreterBuilder.setOutput(PrintStream(outputStream))
|
|
LispInterpreterBuilder.setErrorOutput(PrintStream(errorOutputStream))
|
|
LispInterpreterBuilder.setTerminationFunction { }
|
|
LispInterpreterBuilder.setErrorTerminationFunction { indicatorSet.add(TERMINATED) }
|
|
}
|
|
|
|
private fun assertTerminated() {
|
|
assertThat(indicatorSet).contains(TERMINATED)
|
|
}
|
|
|
|
private fun assertErrorMessageWritten() {
|
|
assertThat(errorOutputStream.toByteArray()).isNotEmpty()
|
|
}
|
|
|
|
@BeforeEach()
|
|
fun setUp() {
|
|
indicatorSet.clear()
|
|
outputStream.reset()
|
|
errorOutputStream.reset()
|
|
RuntimeEnvironment.reset()
|
|
LispInterpreterBuilder.reset()
|
|
}
|
|
|
|
@AfterEach()
|
|
fun tearDown() {
|
|
RuntimeEnvironment.reset()
|
|
LispInterpreterBuilder.reset()
|
|
}
|
|
|
|
@Test
|
|
fun `build an interactive interpreter`() {
|
|
setCommonFeatures()
|
|
LispInterpreterBuilder.setInput(System.`in`, "stdin")
|
|
val interpreter = LispInterpreterBuilder.build()
|
|
|
|
assertThat(interpreter is InteractiveLispInterpreter).isTrue()
|
|
}
|
|
|
|
@Test
|
|
fun `build a non interactive interpreter`() {
|
|
setCommonFeatures()
|
|
LispInterpreterBuilder.setInput(System.`in`, "stdin")
|
|
LispInterpreterBuilder.setNotInteractive()
|
|
val interpreter = LispInterpreterBuilder.build()
|
|
|
|
assertThat(interpreter is InteractiveLispInterpreter).isFalse()
|
|
assertThat(interpreter is FileLispInterpreter).isFalse()
|
|
}
|
|
|
|
@Test
|
|
fun `build a file based interpreter`() {
|
|
setCommonFeatures()
|
|
LispInterpreterBuilder.useFile(FILE)
|
|
val interpreter = LispInterpreterBuilder.build()
|
|
|
|
assertThat(interpreter is FileLispInterpreter).isTrue()
|
|
}
|
|
|
|
@Test
|
|
fun `building an interpreter on a bad file terminates correctly`() {
|
|
setCommonFeatures()
|
|
LispInterpreterBuilder.useFile("does-not-exist.lisp")
|
|
LispInterpreterBuilder.build()
|
|
|
|
assertErrorMessageWritten()
|
|
assertTerminated()
|
|
}
|
|
|
|
@Test
|
|
fun `the decorators are initialized with the correct defaults`() {
|
|
LispInterpreterBuilder.build()
|
|
|
|
assertThat(RuntimeEnvironment.decoratePrompt("")).isEmpty()
|
|
assertThat(RuntimeEnvironment.decorateValueOutput("")).isEmpty()
|
|
assertThat(RuntimeEnvironment.decorateWarningOutput("")).isEmpty()
|
|
assertThat(RuntimeEnvironment.decorateErrorOutput("")).isEmpty()
|
|
assertThat(RuntimeEnvironment.decorateCriticalOutput("")).isEmpty()
|
|
}
|
|
|
|
@Test
|
|
fun `the decorators are set correctly`() {
|
|
LispInterpreterBuilder.setPromptDecorator { s -> "#$s#" }
|
|
LispInterpreterBuilder.setValueOutputDecorator { s -> "@$s@" }
|
|
LispInterpreterBuilder.setWarningOutputDecorator { s -> "%$s%" }
|
|
LispInterpreterBuilder.setErrorOutputDecorator { s -> "*$s*" }
|
|
LispInterpreterBuilder.setCriticalOutputDecorator { s -> "$$s$" }
|
|
LispInterpreterBuilder.build()
|
|
|
|
assertThat(RuntimeEnvironment.decoratePrompt("x")).isEqualTo("#x#")
|
|
assertThat(RuntimeEnvironment.decorateValueOutput("x")).isEqualTo("@x@")
|
|
assertThat(RuntimeEnvironment.decorateWarningOutput("x")).isEqualTo("%x%")
|
|
assertThat(RuntimeEnvironment.decorateErrorOutput("x")).isEqualTo("*x*")
|
|
assertThat(RuntimeEnvironment.decorateCriticalOutput("x")).isEqualTo("\$x$")
|
|
}
|
|
|
|
@Test
|
|
fun `the file based interpreter works and prints the last value only`() {
|
|
setCommonFeatures()
|
|
LispInterpreterBuilder.useFile(FILE)
|
|
LispInterpreterBuilder.build().interpret()
|
|
|
|
assertThat(outputStream.toString()).isEqualTo("PICKLE\n\n")
|
|
assertThat(errorOutputStream.toString()).isEmpty()
|
|
}
|
|
|
|
@Test
|
|
fun `the interactive interpreter works`() {
|
|
setCommonFeatures()
|
|
LispInterpreterBuilder.setInput(createInputStreamFromString("'pickle"), "input")
|
|
LispInterpreterBuilder.build().interpret()
|
|
|
|
assertThat(outputStream.toString()).isEqualTo("$PROMPT\nPICKLE\n$PROMPT\n")
|
|
assertThat(errorOutputStream.toString()).isEmpty()
|
|
}
|
|
|
|
@Test
|
|
fun `the interpreter handles bad input`() {
|
|
setCommonFeatures()
|
|
LispInterpreterBuilder.setNotInteractive()
|
|
LispInterpreterBuilder.setInput(createInputStreamFromString("['pickle"), "input")
|
|
LispInterpreterBuilder.build().interpret()
|
|
|
|
assertThat(outputStream.toString()).isEqualTo("PICKLE\n\n")
|
|
assertThat(errorOutputStream.toString()).isEqualTo("[error] illegal character >>[<< - line 1, column 1\n")
|
|
}
|
|
|
|
@Test
|
|
fun `the interpreter handles an evaluation error`() {
|
|
setCommonFeatures()
|
|
LispInterpreterBuilder.setNotInteractive()
|
|
LispInterpreterBuilder.setInput(createInputStreamFromString("pickle"), "input")
|
|
LispInterpreterBuilder.build().interpret()
|
|
|
|
assertThat(outputStream.toString()).isEqualTo("\n")
|
|
assertThat(errorOutputStream.toString()).isEqualTo("[error] symbol PICKLE has no value\n")
|
|
}
|
|
}
|