transcendental-lisp/src/test/kotlin/terminal/VirtualTerminalInteractor.kt

151 lines
4.3 KiB
Kotlin

package terminal
import com.googlecode.lanterna.TerminalPosition
import com.googlecode.lanterna.TerminalSize
import com.googlecode.lanterna.input.KeyStroke
import com.googlecode.lanterna.input.KeyType
import com.googlecode.lanterna.terminal.virtual.DefaultVirtualTerminal
import com.googlecode.lanterna.terminal.virtual.VirtualTerminal
import org.assertj.core.api.Assertions.assertThat
import org.junit.Assert.fail
import terminal.LispTerminal.END_OF_SEGMENT
import java.io.IOException
import java.io.PipedInputStream
import java.io.PipedOutputStream
class VirtualTerminalInteractor {
private val inputWriter = PipedOutputStream()
private val inputReader = PipedInputStream()
private val outputWriter = PipedOutputStream()
private val outputReader = PipedInputStream()
private val flushListener = FlushListener()
private val virtualTerminal: VirtualTerminal = DefaultVirtualTerminal().apply {
addVirtualTerminalListener(flushListener)
}
val configuration = TerminalConfiguration().also {
it.setInputPair(inputWriter, inputReader)
it.setOutputPair(outputWriter, outputReader)
it.terminal = virtualTerminal
}
private val lispTerminal = LispTerminal(configuration)
fun start() {
lispTerminal.start()
}
fun stop() {
lispTerminal.stop()
outputWriter.close()
}
fun pressKey(keyType: KeyType) {
virtualTerminal.addInput(KeyStroke(keyType))
waitForFlushes(1)
}
fun pressKeyTimes(keyType: KeyType, times: Int) {
for (i in times downTo 1)
virtualTerminal.addInput(KeyStroke(keyType))
waitForFlushes(times)
}
fun pressControlKey(keyType: KeyType) {
virtualTerminal.addInput(KeyStroke(keyType, true, false))
waitForFlushes(1)
}
fun enterCharacter(character: Char) {
virtualTerminal.addInput(KeyStroke(character, false, false))
waitForFlushes(1)
}
fun enterControlCharacter(character: Char) {
virtualTerminal.addInput(KeyStroke(character, true, false))
waitForFlushes(1)
}
fun enterCharacters(characters: String) {
for (c in characters.toCharArray())
virtualTerminal.addInput(KeyStroke(c, false, false))
waitForFlushes(characters.length)
}
fun produceOutput(output: String) {
for (c in output.toCharArray())
outputWriter.write(c.toInt())
outputWriter.write(END_OF_SEGMENT.toInt())
outputWriter.flush()
waitForFlushes(1)
}
private fun waitForFlushes(flushCount: Int) {
synchronized(flushListener.lock) {
while (flushListener.flushCount < flushCount)
flushListener.lock.wait()
flushListener.reduceFlushCount(flushCount)
}
}
fun waitForPrompt() {
waitForFlushes(1)
}
fun setColumns(columns: Int) {
val rows = virtualTerminal.terminalSize.rows
virtualTerminal.terminalSize = TerminalSize(columns, rows)
}
fun setRows(rows: Int) {
val columns = virtualTerminal.terminalSize.columns
virtualTerminal.terminalSize = TerminalSize(columns, rows)
}
fun assertCursorPosition(column: Int, row: Int) {
assertThat(virtualTerminal.cursorPosition.column).isEqualTo(column)
assertThat(virtualTerminal.cursorPosition.row).isEqualTo(row)
}
fun assertScreenText(vararg rows: String) {
for (row in rows.indices)
for (column in 0 until rows[row].length)
assertCharacterAtPosition(rows[row][column], column, row)
}
private fun assertCharacterAtPosition(character: Char, column: Int, row: Int) {
val position = TerminalPosition(column, row)
val expected = character.toString()
val actual = virtualTerminal.getCharacter(position).character.toString()
assertThat(actual).isEqualTo(expected)
}
fun assertInputWritten(expected: String) {
var actual = ""
inputWriter.close()
var c = inputReader.read()
while (c != -1) {
actual += c.toChar()
c = inputReader.read()
}
assertThat(actual).isEqualTo(expected)
}
fun assertInputStreamClosed() {
try {
inputWriter.write(0)
fail("input stream not closed")
} catch (ignored: IOException) {
}
}
}