diff --git a/pom.xml b/pom.xml
index 81c10f2..34b9a8a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -63,6 +63,12 @@
lanterna
3.0.0-rc1
+
+ com.github.stefanbirkner
+ system-rules
+ 1.16.1
+ test
+
UTF-8
diff --git a/src/main/LispMain.java b/src/main/LispMain.java
index 71d530d..8063b33 100644
--- a/src/main/LispMain.java
+++ b/src/main/LispMain.java
@@ -9,17 +9,18 @@ import java.util.function.Function;
import com.googlecode.lanterna.terminal.*;
import interpreter.*;
-import terminal.LispTerminal;
+import stream.UncheckedIOException;
+import terminal.*;
public class LispMain {
private static final String GREETING = "Transcendental Lisp - Version 1.0.1";
- private static final String ANSI_RESET = "\u001B[0m";
- private static final String ANSI_RED = "\u001B[31m";
- private static final String ANSI_GREEN = "\u001B[32m";
- private static final String ANSI_YELLOW = "\u001B[33m";
- private static final String ANSI_PURPLE = "\u001B[35m";
+ public static final String ANSI_RESET = "\u001B[0m";
+ public static final String ANSI_RED = "\u001B[31m";
+ public static final String ANSI_GREEN = "\u001B[32m";
+ public static final String ANSI_YELLOW = "\u001B[33m";
+ public static final String ANSI_PURPLE = "\u001B[35m";
public static void main(String[] arguments) {
LispMain lispMain = new LispMain();
@@ -143,35 +144,4 @@ public class LispMain {
return builder.build();
}
- public class TerminalConfiguration {
-
- private PipedOutputStream inputWriter;
- private PipedInputStream outputReader;
- private IOSafeTerminal terminal;
-
- public PipedOutputStream getInputWriter() {
- return inputWriter;
- }
-
- public void setInputWriter(PipedOutputStream inputWriter) {
- this.inputWriter = inputWriter;
- }
-
- public PipedInputStream getOutputReader() {
- return outputReader;
- }
-
- public void setOutputReader(PipedInputStream outputReader) {
- this.outputReader = outputReader;
- }
-
- public IOSafeTerminal getTerminal() {
- return terminal;
- }
-
- public void setTerminal(IOSafeTerminal terminal) {
- this.terminal = terminal;
- }
- }
-
}
diff --git a/src/terminal/TerminalConfiguration.java b/src/terminal/TerminalConfiguration.java
new file mode 100644
index 0000000..944d074
--- /dev/null
+++ b/src/terminal/TerminalConfiguration.java
@@ -0,0 +1,55 @@
+package terminal;
+
+import java.io.*;
+
+import com.googlecode.lanterna.terminal.IOSafeTerminal;
+
+public class TerminalConfiguration {
+
+ private PipedOutputStream inputWriter;
+ private PipedInputStream inputReader;
+ private PipedOutputStream outputWriter;
+ private PipedInputStream outputReader;
+ private IOSafeTerminal terminal;
+
+ public void setInputWriter(PipedOutputStream inputWriter) {
+ this.inputWriter = inputWriter;
+ }
+
+ public void setInputReader(PipedInputStream inputReader) {
+ this.inputReader = inputReader;
+ }
+
+ public void setOutputWriter(PipedOutputStream outputWriter) {
+ this.outputWriter = outputWriter;
+ }
+
+ public void setOutputReader(PipedInputStream outputReader) {
+ this.outputReader = outputReader;
+ }
+
+ public void setTerminal(IOSafeTerminal terminal) {
+ this.terminal = terminal;
+ }
+
+ public PipedOutputStream getInputWriter() {
+ return inputWriter;
+ }
+
+ public PipedInputStream getInputReader() {
+ return inputReader;
+ }
+
+ public PipedOutputStream getOutputWriter() {
+ return outputWriter;
+ }
+
+ public PipedInputStream getOutputReader() {
+ return outputReader;
+ }
+
+ public IOSafeTerminal getTerminal() {
+ return terminal;
+ }
+
+}
\ No newline at end of file
diff --git a/test/interpreter/LispInterpreterTest.java b/test/interpreter/LispInterpreterTest.java
index 60eb3bd..ced41f2 100644
--- a/test/interpreter/LispInterpreterTest.java
+++ b/test/interpreter/LispInterpreterTest.java
@@ -157,7 +157,7 @@ public class LispInterpreterTest {
}
@Test
- public void fileBasedInterpreterWorks() {
+ public void fileBasedInterpreterWorks_PrintsLastValueOnly() {
setCommonFeatures();
builder.useFile("test/interpreter/test-files/file.lisp");
builder.build().interpret();
diff --git a/test/interpreter/test-files/file.lisp b/test/interpreter/test-files/file.lisp
index 9ef47de..9734d52 100644
--- a/test/interpreter/test-files/file.lisp
+++ b/test/interpreter/test-files/file.lisp
@@ -1 +1,2 @@
+'onion
'pickle
\ No newline at end of file
diff --git a/test/main/MainTest.java b/test/main/MainTest.java
index 1647c34..1c38241 100644
--- a/test/main/MainTest.java
+++ b/test/main/MainTest.java
@@ -1,13 +1,92 @@
package main;
+import static java.text.MessageFormat.format;
+import static main.LispMain.*;
+import static org.junit.Assert.assertEquals;
+
+import java.io.*;
+
import org.junit.*;
+import org.junit.contrib.java.lang.system.*;
+
+import com.googlecode.lanterna.terminal.virtual.*;
+
+import environment.RuntimeEnvironment;
+import interpreter.LispInterpreterBuilderImpl;
+import terminal.*;
public class MainTest {
+ LispMain main;
+ PipedOutputStream inputWriter;
+ PipedInputStream outputReader;
+ VirtualTerminal terminal;
+ RuntimeEnvironment environment;
+
+ public MainTest() {
+ this.environment = RuntimeEnvironment.getInstance();
+ }
+
+ @Rule
+ public ExpectedSystemExit exit = ExpectedSystemExit.none();
+
+ @Rule
+ public SystemErrRule systemErrRule = new SystemErrRule().enableLog().mute();
+
+ @Rule
+ public SystemOutRule systemOutRule = new SystemOutRule().enableLog().mute();
+
@Before
- public void setUp() throws Exception {}
+ public void setUp() throws Exception {
+ environment.reset();
+ TerminalConfiguration configuration = new TerminalConfiguration();
+ inputWriter = new PipedOutputStream();
+ outputReader = new PipedInputStream();
+ terminal = new DefaultVirtualTerminal();
+ configuration.setInputWriter(inputWriter);
+ configuration.setOutputReader(outputReader);
+ configuration.setTerminal(terminal);
+ main = new LispMain(new LispInterpreterBuilderImpl() {}, configuration);
+ }
@After
- public void tearDown() throws Exception {}
+ public void tearDown() throws Exception {
+ terminal.close();
+ environment.reset();
+ }
+
+ @Test
+ public void runWithBadFile() {
+ String expectedMessage = "[critical] test/main/test-files/bad.lisp (No such file or directory)";
+
+ exit.expectSystemExitWithStatus(1);
+ exit.checkAssertionAfterwards(() -> {
+ assertEquals(format("{0}{1}{2}\n", ANSI_PURPLE, expectedMessage, ANSI_RESET),
+ systemErrRule.getLogWithNormalizedLineSeparator());
+ assertEquals("", systemOutRule.getLogWithNormalizedLineSeparator());
+
+ });
+
+ main.runWithFile("test/main/test-files/bad.lisp");
+
+ }
+
+ @Test
+ public void runWithFile_PrintsDecoratedLastValueOnly() {
+ main.runWithFile("test/main/test-files/file.lisp");
+ assertEquals("", systemErrRule.getLogWithNormalizedLineSeparator());
+ assertEquals(format("{0}{1}{2}\n\n", ANSI_GREEN, "RADISH", ANSI_RESET),
+ systemOutRule.getLogWithNormalizedLineSeparator());
+ }
+
+ @Test
+ public void runInteractive() {
+ VirtualTerminalInteractor t = new VirtualTerminalInteractor();
+ try {
+ t.start();
+ } finally {
+ t.stop();
+ }
+ }
}
diff --git a/test/main/test-files/file.lisp b/test/main/test-files/file.lisp
new file mode 100644
index 0000000..2c7d117
--- /dev/null
+++ b/test/main/test-files/file.lisp
@@ -0,0 +1,2 @@
+'pickle
+'radish
\ No newline at end of file
diff --git a/test/terminal/LispTerminalTest.java b/test/terminal/LispTerminalTest.java
index 938888d..29a5077 100644
--- a/test/terminal/LispTerminalTest.java
+++ b/test/terminal/LispTerminalTest.java
@@ -1,562 +1,437 @@
package terminal;
import static com.googlecode.lanterna.input.KeyType.*;
-import static org.junit.Assert.*;
import static terminal.LispTerminal.END_OF_SEGMENT;
-import java.io.*;
-
import org.junit.*;
-import com.googlecode.lanterna.*;
-import com.googlecode.lanterna.input.*;
-import com.googlecode.lanterna.terminal.virtual.*;
-
public class LispTerminalTest {
- private PipedInputStream inputReader;
- private PipedOutputStream inputWriter;
- private PipedInputStream outputReader;
- private PipedOutputStream outputWriter;
- private FlushListener flushListener;
- private VirtualTerminal virtualTerminal;
- private LispTerminal lispTerminal;
-
- private void pressKey(KeyType keyType) {
- virtualTerminal.addInput(new KeyStroke(keyType));
- waitForFlushes(1);
- }
-
- private void pressKeyTimes(KeyType keyType, int times) {
- for (int i = times; i > 0; i--)
- virtualTerminal.addInput(new KeyStroke(keyType));
-
- waitForFlushes(times);
- }
-
- private void pressControlKey(KeyType keyType) {
- virtualTerminal.addInput(new KeyStroke(keyType, true, false));
- waitForFlushes(1);
- }
-
- private void enterCharacter(char character) {
- virtualTerminal.addInput(new KeyStroke(character, false, false));
- waitForFlushes(1);
- }
-
- private void enterControlCharacter(char character) {
- virtualTerminal.addInput(new KeyStroke(character, true, false));
- waitForFlushes(1);
- }
-
- private void enterCharacters(String characters) {
- for (char c : characters.toCharArray())
- virtualTerminal.addInput(new KeyStroke(c, false, false));
-
- waitForFlushes(characters.length());
- }
-
- private void produceOutput(String output) {
- try {
- for (char c : output.toCharArray())
- outputWriter.write(c);
-
- outputWriter.write(END_OF_SEGMENT);
- outputWriter.flush();
- waitForFlushes(1);
- } catch (IOException ignored) {}
- }
-
- private void waitForFlushes(int flushCount) {
- try {
- synchronized (flushListener) {
- while (flushListener.getFlushCount() < flushCount)
- flushListener.wait();
-
- flushListener.resetFlushCount();
- }
- } catch (InterruptedException ignored) {}
- }
-
- private void setColumns(int columns) {
- int rows = virtualTerminal.getTerminalSize().getRows();
- virtualTerminal.setTerminalSize(new TerminalSize(columns, rows));
- }
-
- private void setRows(int rows) {
- int columns = virtualTerminal.getTerminalSize().getColumns();
- virtualTerminal.setTerminalSize(new TerminalSize(columns, rows));
- }
-
- private void assertCursorPosition(int column, int row) {
- assertEquals(column, virtualTerminal.getCursorPosition().getColumn());
- assertEquals(row, virtualTerminal.getCursorPosition().getRow());
- }
-
- private void assertCharacterAtPosition(char character, int column, int row) {
- TerminalPosition position = new TerminalPosition(column, row);
- String expected = String.valueOf(character);
- String actual = String.valueOf(virtualTerminal.getCharacter(position).getCharacter());
- assertEquals(expected, actual);
- }
-
- private void assertCharacterPositions(char[][] positions) {
- for (int row = 0; row < positions.length; row++)
- for (int column = 0; column < positions[row].length; column++)
- assertCharacterAtPosition(positions[row][column], column, row);
- }
-
- private void assertInputWritten(String expected) {
- String actual = "";
-
- try {
- inputWriter.close();
-
- for (int c = inputReader.read(); c != -1; c = inputReader.read())
- actual += (char) c;
- } catch (IOException ignored) {}
-
- assertEquals(expected, actual);
- }
-
- private void assertInputStreamClosed() {
- try {
- inputWriter.write(0);
- fail("input stream not closed");
- } catch (IOException ignored) {}
- }
+ private VirtualTerminalInteractor terminal;
@Before
- public void setUp() throws IOException {
- inputReader = new PipedInputStream();
- inputWriter = new PipedOutputStream(inputReader);
- outputReader = new PipedInputStream();
- outputWriter = new PipedOutputStream(outputReader);
- virtualTerminal = new DefaultVirtualTerminal();
- flushListener = new FlushListener();
- virtualTerminal.addVirtualTerminalListener(flushListener);
- lispTerminal = new LispTerminal(virtualTerminal, inputWriter, outputReader);
- lispTerminal.start();
+ public void setUp() {
+ terminal = new VirtualTerminalInteractor();
+ terminal.start();
}
@After
- public void tearDown() throws IOException {
- lispTerminal.stop();
- outputWriter.close();
+ public void tearDown() {
+ terminal.stop();
}
@Test
public void leftArrowDoesNotMovePastOrigin() {
- pressKey(ArrowLeft);
- assertCursorPosition(0, 0);
+ terminal.pressKey(ArrowLeft);
+ terminal.assertCursorPosition(0, 0);
}
@Test
public void leftArrowWorksAfterEnteringCharacters() {
- enterCharacters("abc");
- assertCursorPosition(3, 0);
- pressKey(ArrowLeft);
- assertCursorPosition(2, 0);
- pressKey(ArrowLeft);
- assertCursorPosition(1, 0);
- pressKey(ArrowLeft);
- assertCursorPosition(0, 0);
- pressKey(ArrowLeft);
- assertCursorPosition(0, 0);
+ terminal.enterCharacters("abc");
+ terminal.assertCursorPosition(3, 0);
+ terminal.pressKey(ArrowLeft);
+ terminal.assertCursorPosition(2, 0);
+ terminal.pressKey(ArrowLeft);
+ terminal.assertCursorPosition(1, 0);
+ terminal.pressKey(ArrowLeft);
+ terminal.assertCursorPosition(0, 0);
+ terminal.pressKey(ArrowLeft);
+ terminal.assertCursorPosition(0, 0);
}
@Test
public void leftArrowWorksAcrossRows() {
- setColumns(5);
- enterCharacters("123451");
- assertCursorPosition(1, 1);
- pressKeyTimes(ArrowLeft, 2);
- assertCursorPosition(4, 0);
+ terminal.setColumns(5);
+ terminal.enterCharacters("123451");
+ terminal.assertCursorPosition(1, 1);
+ terminal.pressKeyTimes(ArrowLeft, 2);
+ terminal.assertCursorPosition(4, 0);
}
@Test
public void rightArrowDoesNotMovePastEndOfInput() {
- pressKey(ArrowRight);
- assertCursorPosition(0, 0);
+ terminal.pressKey(ArrowRight);
+ terminal.assertCursorPosition(0, 0);
}
@Test
public void rightArrowWorksAfterMovingLeft() {
- enterCharacters("12");
- assertCursorPosition(2, 0);
- pressKey(ArrowLeft);
- assertCursorPosition(1, 0);
- pressKey(ArrowRight);
- assertCursorPosition(2, 0);
- pressKey(ArrowRight);
- assertCursorPosition(2, 0);
+ terminal.enterCharacters("12");
+ terminal.assertCursorPosition(2, 0);
+ terminal.pressKey(ArrowLeft);
+ terminal.assertCursorPosition(1, 0);
+ terminal.pressKey(ArrowRight);
+ terminal.assertCursorPosition(2, 0);
+ terminal.pressKey(ArrowRight);
+ terminal.assertCursorPosition(2, 0);
}
@Test
public void rightArrowWorksAcrossRow() {
- setColumns(5);
- enterCharacters("123451");
- assertCursorPosition(1, 1);
- pressKeyTimes(ArrowLeft, 3);
- assertCursorPosition(3, 0);
- pressKeyTimes(ArrowRight, 3);
- assertCursorPosition(1, 1);
+ terminal.setColumns(5);
+ terminal.enterCharacters("123451");
+ terminal.assertCursorPosition(1, 1);
+ terminal.pressKeyTimes(ArrowLeft, 3);
+ terminal.assertCursorPosition(3, 0);
+ terminal.pressKeyTimes(ArrowRight, 3);
+ terminal.assertCursorPosition(1, 1);
}
@Test
public void characterKeyIsEchoed() {
- enterCharacter('a');
- assertCursorPosition(1, 0);
- assertCharacterAtPosition('a', 0, 0);
+ terminal.enterCharacter('a');
+ terminal.assertCursorPosition(1, 0);
+ terminal.assertCharacterAtPosition('a', 0, 0);
}
@Test
public void characterIsInserted() {
- enterCharacters("abcd");
- pressKeyTimes(ArrowLeft, 2);
- enterCharacter('x');
- assertCharacterPositions(new char[][] { { 'a', 'b', 'x', 'c', 'd' } });
+ terminal.enterCharacters("abcd");
+ terminal.pressKeyTimes(ArrowLeft, 2);
+ terminal.enterCharacter('x');
+ terminal.assertCharacterPositions(new char[][] { { 'a', 'b', 'x', 'c', 'd' } });
}
@Test
public void characterIsInserted_PushesInputToNextRow() {
- setColumns(4);
- enterCharacters("abcd");
- pressKeyTimes(ArrowLeft, 2);
- enterCharacter('x');
- assertCharacterPositions(new char[][] { { 'a', 'b', 'x', 'c' }, { 'd', ' ', ' ', ' ' } });
+ terminal.setColumns(4);
+ terminal.enterCharacters("abcd");
+ terminal.pressKeyTimes(ArrowLeft, 2);
+ terminal.enterCharacter('x');
+ terminal.assertCharacterPositions(new char[][] { { 'a', 'b', 'x', 'c' }, { 'd', ' ', ' ', ' ' } });
}
@Test
public void backspaceDoesNothingAtOrigin() {
- pressKey(Backspace);
- assertCursorPosition(0, 0);
+ terminal.pressKey(Backspace);
+ terminal.assertCursorPosition(0, 0);
}
@Test
public void backspaceWorksAfterInput() {
- enterCharacters("12345");
- pressKeyTimes(Backspace, 2);
- assertCursorPosition(3, 0);
- assertCharacterPositions(new char[][] { { '1', '2', '3', ' ', ' ', ' ' } });
+ terminal.enterCharacters("12345");
+ terminal.pressKeyTimes(Backspace, 2);
+ terminal.assertCursorPosition(3, 0);
+ terminal.assertCharacterPositions(new char[][] { { '1', '2', '3', ' ', ' ', ' ' } });
}
@Test
public void backspaceWorksAcrossRow() {
- setColumns(4);
- enterCharacters("1234567");
- pressKeyTimes(Backspace, 5);
- assertCursorPosition(2, 0);
- assertCharacterPositions(new char[][] { { '1', '2', ' ', ' ' }, { ' ', ' ', ' ', ' ' } });
+ terminal.setColumns(4);
+ terminal.enterCharacters("1234567");
+ terminal.pressKeyTimes(Backspace, 5);
+ terminal.assertCursorPosition(2, 0);
+ terminal.assertCharacterPositions(new char[][] { { '1', '2', ' ', ' ' }, { ' ', ' ', ' ', ' ' } });
}
@Test
public void backspaceWorksInMiddleOfInput() {
- enterCharacters("12345");
- pressKeyTimes(ArrowLeft, 2);
- pressKey(Backspace);
- assertCursorPosition(2, 0);
- assertCharacterPositions(new char[][] { { '1', '2', '4', '5' } });
+ terminal.enterCharacters("12345");
+ terminal.pressKeyTimes(ArrowLeft, 2);
+ terminal.pressKey(Backspace);
+ terminal.assertCursorPosition(2, 0);
+ terminal.assertCharacterPositions(new char[][] { { '1', '2', '4', '5' } });
}
@Test
public void deleteDoesNothingAtOrigin() {
- pressKey(Delete);
- assertCursorPosition(0, 0);
+ terminal.pressKey(Delete);
+ terminal.assertCursorPosition(0, 0);
}
@Test
public void deleteDoesNothingAtEndOfInput() {
- enterCharacters("del");
- pressKey(Delete);
- assertCursorPosition(3, 0);
- assertCharacterPositions(new char[][] { { 'd', 'e', 'l' } });
+ terminal.enterCharacters("del");
+ terminal.pressKey(Delete);
+ terminal.assertCursorPosition(3, 0);
+ terminal.assertCharacterPositions(new char[][] { { 'd', 'e', 'l' } });
}
@Test
public void deleteWorksAtStartOfInput() {
- enterCharacters("del");
- pressKeyTimes(ArrowLeft, 3);
- pressKeyTimes(Delete, 3);
- assertCursorPosition(0, 0);
- assertCharacterPositions(new char[][] { { ' ', ' ', ' ' } });
+ terminal.enterCharacters("del");
+ terminal.pressKeyTimes(ArrowLeft, 3);
+ terminal.pressKeyTimes(Delete, 3);
+ terminal.assertCursorPosition(0, 0);
+ terminal.assertCharacterPositions(new char[][] { { ' ', ' ', ' ' } });
}
@Test
public void deleteWorksAcrossRow() {
- setColumns(4);
- enterCharacters("delete");
- pressKeyTimes(ArrowLeft, 5);
- pressKey(Delete);
- assertCursorPosition(1, 0);
- assertCharacterPositions(new char[][] { { 'd', 'l', 'e', 't' }, { 'e', ' ' } });
+ terminal.setColumns(4);
+ terminal.enterCharacters("delete");
+ terminal.pressKeyTimes(ArrowLeft, 5);
+ terminal.pressKey(Delete);
+ terminal.assertCursorPosition(1, 0);
+ terminal.assertCharacterPositions(new char[][] { { 'd', 'l', 'e', 't' }, { 'e', ' ' } });
}
@Test
public void enterMovesToNextLine() {
- pressKey(Enter);
- assertCursorPosition(0, 1);
+ terminal.pressKey(Enter);
+ terminal.assertCursorPosition(0, 1);
}
@Test
public void enterWritesLineToPipedStream() {
- enterCharacters("enter");
- pressKey(Enter);
- assertInputWritten("enter\n");
+ terminal.enterCharacters("enter");
+ terminal.pressKey(Enter);
+ terminal.assertInputWritten("enter\n");
}
@Test
public void enterPressedInMiddleOfInput_WritesEntireLineToPipedStream() {
- enterCharacters("enter");
- pressKeyTimes(ArrowLeft, 2);
- pressKey(Enter);
- assertInputWritten("enter\n");
+ terminal.enterCharacters("enter");
+ terminal.pressKeyTimes(ArrowLeft, 2);
+ terminal.pressKey(Enter);
+ terminal.assertInputWritten("enter\n");
}
@Test
public void enterAfterInsertedText_WritesLineToPipedStream() {
- enterCharacters("enter");
- pressKeyTimes(ArrowLeft, 2);
- enterCharacters("||");
- pressKey(Enter);
- assertInputWritten("ent||er\n");
+ terminal.enterCharacters("enter");
+ terminal.pressKeyTimes(ArrowLeft, 2);
+ terminal.enterCharacters("||");
+ terminal.pressKey(Enter);
+ terminal.assertInputWritten("ent||er\n");
}
@Test
public void enterAfterBackspace_WritesLineToPipedStream() {
- enterCharacters("enter");
- pressKeyTimes(Backspace, 2);
- pressKey(Enter);
- assertInputWritten("ent\n");
+ terminal.enterCharacters("enter");
+ terminal.pressKeyTimes(Backspace, 2);
+ terminal.pressKey(Enter);
+ terminal.assertInputWritten("ent\n");
}
@Test
public void enterAfterDelete_WritesLineToPipedStream() {
- enterCharacters("enter");
- pressKeyTimes(ArrowLeft, 2);
- pressKeyTimes(Delete, 2);
- pressKey(Enter);
- assertInputWritten("ent\n");
+ terminal.enterCharacters("enter");
+ terminal.pressKeyTimes(ArrowLeft, 2);
+ terminal.pressKeyTimes(Delete, 2);
+ terminal.pressKey(Enter);
+ terminal.assertInputWritten("ent\n");
}
@Test
public void controlDWorks() {
- enterCharacters("control-d");
- enterControlCharacter('d');
- assertInputStreamClosed();
- assertInputWritten("control-d\n");
+ terminal.enterCharacters("control-d");
+ terminal.enterControlCharacter('d');
+ terminal.assertInputStreamClosed();
+ terminal.assertInputWritten("control-d\n");
}
@Test
public void controlCWorks() {
- enterCharacters("ctrl-c");
- enterControlCharacter('c');
- produceOutput("");
- assertInputStreamClosed();
- assertInputWritten("");
- assertCharacterPositions(new char[][] { { 'c', 't', 'r', 'l', '-', 'c', ' ', ' ', ' ' },
- { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' } });
+ terminal.enterCharacters("ctrl-c");
+ terminal.enterControlCharacter('c');
+ terminal.produceOutput("");
+ terminal.assertInputStreamClosed();
+ terminal.assertInputWritten("");
+ terminal.assertCharacterPositions(new char[][] { { 'c', 't', 'r', 'l', '-', 'c', ' ', ' ', ' ' },
+ { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' } });
}
@Test
public void controlDWorksInMiddleOfInput() {
- enterCharacters("control-d");
- pressKeyTimes(ArrowLeft, 2);
- enterControlCharacter('d');
- assertInputStreamClosed();
- assertInputWritten("control-d\n");
+ terminal.enterCharacters("control-d");
+ terminal.pressKeyTimes(ArrowLeft, 2);
+ terminal.enterControlCharacter('d');
+ terminal.assertInputStreamClosed();
+ terminal.assertInputWritten("control-d\n");
}
@Test
public void escapeDoesNothing() {
- pressKey(Escape);
- assertCursorPosition(0, 0);
- assertInputWritten("");
+ terminal.pressKey(Escape);
+ terminal.assertCursorPosition(0, 0);
+ terminal.assertInputWritten("");
}
@Test
public void controlQDoesNothing() {
- enterControlCharacter('q');
- assertCursorPosition(0, 0);
- assertInputWritten("");
+ terminal.enterControlCharacter('q');
+ terminal.assertCursorPosition(0, 0);
+ terminal.assertInputWritten("");
}
@Test
public void controlEnterDoesNothing() {
- pressControlKey(Enter);
- assertCursorPosition(0, 0);
- assertInputWritten("");
+ terminal.pressControlKey(Enter);
+ terminal.assertCursorPosition(0, 0);
+ terminal.assertInputWritten("");
}
@Test
public void outputIsWritten() {
- produceOutput("output");
- assertCursorPosition(6, 0);
- assertCharacterPositions(new char[][] { { 'o', 'u', 't', 'p', 'u', 't' } });
+ terminal.produceOutput("output");
+ terminal.assertCursorPosition(6, 0);
+ terminal.assertCharacterPositions(new char[][] { { 'o', 'u', 't', 'p', 'u', 't' } });
}
@Test
public void endOfSegmentCharacterIsNotPrinted() {
- produceOutput("> " + END_OF_SEGMENT);
- assertCursorPosition(2, 0);
- assertCharacterPositions(new char[][] { { '>', ' ', ' ' } });
+ terminal.produceOutput("> " + END_OF_SEGMENT);
+ terminal.assertCursorPosition(2, 0);
+ terminal.assertCharacterPositions(new char[][] { { '>', ' ', ' ' } });
}
@Test
public void enterTextPastLastLineOfBuffer() {
- setColumns(3);
- setRows(2);
- enterCharacters("01201201");
- assertCursorPosition(2, 1);
- assertCharacterPositions(new char[][] { { '0', '1', '2' }, { '0', '1', ' ' } });
+ terminal.setColumns(3);
+ terminal.setRows(2);
+ terminal.enterCharacters("01201201");
+ terminal.assertCursorPosition(2, 1);
+ terminal.assertCharacterPositions(new char[][] { { '0', '1', '2' }, { '0', '1', ' ' } });
}
@Test
public void insertingTextPushesInputPastEndOfBuffer() {
- setColumns(3);
- setRows(4);
- pressKey(Enter);
- enterCharacters("00011122");
- pressKeyTimes(ArrowLeft, 4);
- assertCursorPosition(1, 2);
- enterCharacters("zz");
- assertCursorPosition(0, 2);
- assertCharacterPositions(new char[][] { { '0', '0', '0' }, { '1', 'z', 'z' }, { '1', '1', '2' },
- { '2', ' ', ' ' } });
+ terminal.setColumns(3);
+ terminal.setRows(4);
+ terminal.pressKey(Enter);
+ terminal.enterCharacters("00011122");
+ terminal.pressKeyTimes(ArrowLeft, 4);
+ terminal.assertCursorPosition(1, 2);
+ terminal.enterCharacters("zz");
+ terminal.assertCursorPosition(0, 2);
+ terminal.assertCharacterPositions(new char[][] { { '0', '0', '0' }, { '1', 'z', 'z' }, { '1', '1', '2' },
+ { '2', ' ', ' ' } });
}
@Test
public void insertingTextDoesNothingWhenBufferFilled() {
- setColumns(3);
- setRows(3);
- enterCharacters("00011122");
- pressKeyTimes(ArrowLeft, 4);
- assertCursorPosition(1, 1);
- enterCharacters("zz");
- assertCursorPosition(1, 1);
- assertCharacterPositions(new char[][] { { '0', '0', '0' }, { '1', '1', '1' }, { '2', '2', ' ' } });
+ terminal.setColumns(3);
+ terminal.setRows(3);
+ terminal.enterCharacters("00011122");
+ terminal.pressKeyTimes(ArrowLeft, 4);
+ terminal.assertCursorPosition(1, 1);
+ terminal.enterCharacters("zz");
+ terminal.assertCursorPosition(1, 1);
+ terminal.assertCharacterPositions(new char[][] { { '0', '0', '0' }, { '1', '1', '1' }, { '2', '2', ' ' } });
}
@Test
public void appendingTextDoesNothingWhenBufferFilled() {
- setColumns(3);
- setRows(3);
- enterCharacters("000111222333444");
- assertCursorPosition(2, 2);
- assertCharacterPositions(new char[][] { { '0', '0', '0' }, { '1', '1', '1' }, { '2', '2', ' ' } });
+ terminal.setColumns(3);
+ terminal.setRows(3);
+ terminal.enterCharacters("000111222333444");
+ terminal.assertCursorPosition(2, 2);
+ terminal.assertCharacterPositions(new char[][] { { '0', '0', '0' }, { '1', '1', '1' }, { '2', '2', ' ' } });
}
@Test
public void printedOutputToEndOfRow_MovesCursorToNextRow() {
- setColumns(3);
- produceOutput("out");
- assertCursorPosition(0, 1);
+ terminal.setColumns(3);
+ terminal.produceOutput("out");
+ terminal.assertCursorPosition(0, 1);
}
@Test
public void printedOutputToEndOfBuffer_MovesCursorToNewRow() {
- setColumns(3);
- setRows(2);
- produceOutput("output");
- assertCursorPosition(0, 1);
- assertCharacterPositions(new char[][] { { 'p', 'u', 't' }, { ' ', ' ', ' ' } });
+ terminal.setColumns(3);
+ terminal.setRows(2);
+ terminal.produceOutput("output");
+ terminal.assertCursorPosition(0, 1);
+ terminal.assertCharacterPositions(new char[][] { { 'p', 'u', 't' }, { ' ', ' ', ' ' } });
}
@Test
public void outputDoesNotOverwriteInput_AndRedisplaysInput() {
- setColumns(3);
- enterCharacters("0123");
- pressKeyTimes(ArrowLeft, 3);
- produceOutput("out");
- assertCursorPosition(2, 3);
- assertCharacterPositions(new char[][] { { '0', '1', '2' }, { '3', 'o', 'u' }, { 't', '0', '1' },
- { '2', '3', ' ' }, { ' ', ' ', ' ' } });
+ terminal.setColumns(3);
+ terminal.enterCharacters("0123");
+ terminal.pressKeyTimes(ArrowLeft, 3);
+ terminal.produceOutput("out");
+ terminal.assertCursorPosition(2, 3);
+ terminal.assertCharacterPositions(new char[][] { { '0', '1', '2' }, { '3', 'o', 'u' }, { 't', '0', '1' },
+ { '2', '3', ' ' }, { ' ', ' ', ' ' } });
}
@Test
public void outputEndsOnSecondToLastColumn_MovesToNewRow() {
- setColumns(3);
- enterCharacters("01234");
- pressKeyTimes(ArrowLeft, 3);
- produceOutput("out");
- assertCursorPosition(2, 4);
- assertCharacterPositions(new char[][] { { '0', '1', '2' }, { '3', '4', 'o' }, { 'u', 't', ' ' },
- { '0', '1', '2' }, { '3', '4', ' ' } });
+ terminal.setColumns(3);
+ terminal.enterCharacters("01234");
+ terminal.pressKeyTimes(ArrowLeft, 3);
+ terminal.produceOutput("out");
+ terminal.assertCursorPosition(2, 4);
+ terminal.assertCharacterPositions(new char[][] { { '0', '1', '2' }, { '3', '4', 'o' }, { 'u', 't', ' ' },
+ { '0', '1', '2' }, { '3', '4', ' ' } });
}
@Test
public void outputEndsOnLastColumn_MovesToNewRow() {
- setColumns(3);
- enterCharacters("012345");
- pressKeyTimes(ArrowLeft, 3);
- produceOutput("out");
- assertCursorPosition(0, 5);
- assertCharacterPositions(new char[][] { { '0', '1', '2' }, { '3', '4', '5' }, { 'o', 'u', 't' },
- { '0', '1', '2' }, { '3', '4', '5' }, { ' ', ' ', ' ' } });
+ terminal.setColumns(3);
+ terminal.enterCharacters("012345");
+ terminal.pressKeyTimes(ArrowLeft, 3);
+ terminal.produceOutput("out");
+ terminal.assertCursorPosition(0, 5);
+ terminal.assertCharacterPositions(new char[][] { { '0', '1', '2' }, { '3', '4', '5' }, { 'o', 'u', 't' },
+ { '0', '1', '2' }, { '3', '4', '5' }, { ' ', ' ', ' ' } });
}
@Test
public void outputRedisplaysInputAtEndOfBuffer() {
- setColumns(3);
- setRows(4);
- enterCharacters("01234");
- pressKeyTimes(ArrowLeft, 3);
- produceOutput("out");
- assertCursorPosition(2, 3);
- assertCharacterPositions(new char[][] { { '3', '4', 'o' }, { 'u', 't', ' ' }, { '0', '1', '2' },
- { '3', '4', ' ' } });
+ terminal.setColumns(3);
+ terminal.setRows(4);
+ terminal.enterCharacters("01234");
+ terminal.pressKeyTimes(ArrowLeft, 3);
+ terminal.produceOutput("out");
+ terminal.assertCursorPosition(2, 3);
+ terminal.assertCharacterPositions(new char[][] { { '3', '4', 'o' }, { 'u', 't', ' ' }, { '0', '1', '2' },
+ { '3', '4', ' ' } });
}
@Test
public void outputDoesNotOverwriteInput_AfterEnter() {
- setColumns(3);
- enterCharacters("01201201");
- pressKeyTimes(ArrowLeft, 5);
- pressKey(Enter);
- produceOutput("out");
- assertCursorPosition(0, 4);
- assertCharacterPositions(new char[][] { { '0', '1', '2' }, { '0', '1', '2' }, { '0', '1', ' ' },
- { 'o', 'u', 't' }, { ' ', ' ', ' ' } });
+ terminal.setColumns(3);
+ terminal.enterCharacters("01201201");
+ terminal.pressKeyTimes(ArrowLeft, 5);
+ terminal.pressKey(Enter);
+ terminal.produceOutput("out");
+ terminal.assertCursorPosition(0, 4);
+ terminal.assertCharacterPositions(new char[][] { { '0', '1', '2' }, { '0', '1', '2' }, { '0', '1', ' ' },
+ { 'o', 'u', 't' }, { ' ', ' ', ' ' } });
}
@Test
public void resizeIsHandledGracefully() {
- enterCharacters("resize");
- pressKey(Enter);
- enterCharacters("test");
- setColumns(3);
- assertCursorPosition(1, 1);
- assertCharacterPositions(new char[][] { { 't', 'e', 's' }, { 't', ' ', ' ' } });
+ terminal.enterCharacters("resize");
+ terminal.pressKey(Enter);
+ terminal.enterCharacters("test");
+ terminal.setColumns(3);
+ terminal.assertCursorPosition(1, 1);
+ terminal.assertCharacterPositions(new char[][] { { 't', 'e', 's' }, { 't', ' ', ' ' } });
}
@Test
public void backspaceWorksAfterResize() {
- enterCharacters("resize");
- pressKey(Enter);
- enterCharacters("test");
- setColumns(3);
- pressKeyTimes(Backspace, 20);
- assertCursorPosition(0, 0);
- assertCharacterPositions(new char[][] { { ' ', ' ', ' ' }, { ' ', ' ', ' ' } });
+ terminal.enterCharacters("resize");
+ terminal.pressKey(Enter);
+ terminal.enterCharacters("test");
+ terminal.setColumns(3);
+ terminal.pressKeyTimes(Backspace, 20);
+ terminal.assertCursorPosition(0, 0);
+ terminal.assertCharacterPositions(new char[][] { { ' ', ' ', ' ' }, { ' ', ' ', ' ' } });
}
@Test
public void deleteWorksAfterResize() {
- enterCharacters("resize");
- pressKey(Enter);
- enterCharacters("test");
- setColumns(3);
- pressKeyTimes(ArrowLeft, 20);
- pressKeyTimes(Delete, 20);
- pressKeyTimes(ArrowRight, 20);
- assertCursorPosition(0, 0);
- assertCharacterPositions(new char[][] { { ' ', ' ', ' ' }, { ' ', ' ', ' ' } });
+ terminal.enterCharacters("resize");
+ terminal.pressKey(Enter);
+ terminal.enterCharacters("test");
+ terminal.setColumns(3);
+ terminal.pressKeyTimes(ArrowLeft, 20);
+ terminal.pressKeyTimes(Delete, 20);
+ terminal.pressKeyTimes(ArrowRight, 20);
+ terminal.assertCursorPosition(0, 0);
+ terminal.assertCharacterPositions(new char[][] { { ' ', ' ', ' ' }, { ' ', ' ', ' ' } });
}
@Test
public void controlSequencesAreNotPrinted() {
- produceOutput("\u001B[32mcontrol\u001B[0mseq");
- assertCharacterPositions(new char[][] { { 'c', 'o', 'n', 't', 'r', 'o', 'l', 's', 'e', 'q' } });
+ terminal.produceOutput("\u001B[32mcontrol\u001B[0mseq");
+ terminal.assertCharacterPositions(new char[][] { { 'c', 'o', 'n', 't', 'r', 'o', 'l', 's', 'e', 'q' } });
}
}
diff --git a/test/terminal/VirtualTerminalInteractor.java b/test/terminal/VirtualTerminalInteractor.java
new file mode 100644
index 0000000..d20071f
--- /dev/null
+++ b/test/terminal/VirtualTerminalInteractor.java
@@ -0,0 +1,156 @@
+package terminal;
+
+import static org.junit.Assert.*;
+import static terminal.LispTerminal.END_OF_SEGMENT;
+
+import java.io.*;
+
+import com.googlecode.lanterna.*;
+import com.googlecode.lanterna.input.*;
+import com.googlecode.lanterna.terminal.virtual.*;
+
+import stream.UncheckedIOException;
+
+public class VirtualTerminalInteractor {
+
+ private VirtualTerminal virtualTerminal;
+ private FlushListener flushListener;
+ private PipedInputStream inputReader;
+ private PipedOutputStream inputWriter;
+ private PipedInputStream outputReader;
+ private PipedOutputStream outputWriter;
+ private LispTerminal lispTerminal;
+
+ public VirtualTerminalInteractor() {
+ try {
+ this.inputReader = new PipedInputStream();
+ this.inputWriter = new PipedOutputStream(inputReader);
+ this.outputReader = new PipedInputStream();
+ this.outputWriter = new PipedOutputStream(outputReader);
+ this.virtualTerminal = new DefaultVirtualTerminal();
+ this.flushListener = new FlushListener();
+ this.virtualTerminal.addVirtualTerminalListener(flushListener);
+ this.lispTerminal = new LispTerminal(virtualTerminal, inputWriter, outputReader);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ public void start() {
+ lispTerminal.start();
+ }
+
+ public void stop() {
+ try {
+ lispTerminal.stop();
+ outputWriter.close();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ public void pressKey(KeyType keyType) {
+ virtualTerminal.addInput(new KeyStroke(keyType));
+ waitForFlushes(1);
+ }
+
+ public void pressKeyTimes(KeyType keyType, int times) {
+ for (int i = times; i > 0; i--)
+ virtualTerminal.addInput(new KeyStroke(keyType));
+
+ waitForFlushes(times);
+ }
+
+ public void pressControlKey(KeyType keyType) {
+ virtualTerminal.addInput(new KeyStroke(keyType, true, false));
+ waitForFlushes(1);
+ }
+
+ public void enterCharacter(char character) {
+ virtualTerminal.addInput(new KeyStroke(character, false, false));
+ waitForFlushes(1);
+ }
+
+ public void enterControlCharacter(char character) {
+ virtualTerminal.addInput(new KeyStroke(character, true, false));
+ waitForFlushes(1);
+ }
+
+ public void enterCharacters(String characters) {
+ for (char c : characters.toCharArray())
+ virtualTerminal.addInput(new KeyStroke(c, false, false));
+
+ waitForFlushes(characters.length());
+ }
+
+ public void produceOutput(String output) {
+ try {
+ for (char c : output.toCharArray())
+ outputWriter.write(c);
+
+ outputWriter.write(END_OF_SEGMENT);
+ outputWriter.flush();
+ waitForFlushes(1);
+ } catch (IOException ignored) {}
+ }
+
+ public void waitForFlushes(int flushCount) {
+ try {
+ synchronized (flushListener) {
+ while (flushListener.getFlushCount() < flushCount)
+ flushListener.wait();
+
+ flushListener.resetFlushCount();
+ }
+ } catch (InterruptedException ignored) {}
+ }
+
+ public void setColumns(int columns) {
+ int rows = virtualTerminal.getTerminalSize().getRows();
+ virtualTerminal.setTerminalSize(new TerminalSize(columns, rows));
+ }
+
+ public void setRows(int rows) {
+ int columns = virtualTerminal.getTerminalSize().getColumns();
+ virtualTerminal.setTerminalSize(new TerminalSize(columns, rows));
+ }
+
+ public void assertCursorPosition(int column, int row) {
+ assertEquals(column, virtualTerminal.getCursorPosition().getColumn());
+ assertEquals(row, virtualTerminal.getCursorPosition().getRow());
+ }
+
+ public void assertCharacterAtPosition(char character, int column, int row) {
+ TerminalPosition position = new TerminalPosition(column, row);
+ String expected = String.valueOf(character);
+ String actual = String.valueOf(virtualTerminal.getCharacter(position).getCharacter());
+ assertEquals(expected, actual);
+ }
+
+ public void assertCharacterPositions(char[][] positions) {
+ for (int row = 0; row < positions.length; row++)
+ for (int column = 0; column < positions[row].length; column++)
+ assertCharacterAtPosition(positions[row][column], column, row);
+ }
+
+ public void assertInputWritten(String expected) {
+ String actual = "";
+
+ try {
+ inputWriter.close();
+
+ for (int c = inputReader.read(); c != -1; c = inputReader.read())
+ actual += (char) c;
+ } catch (IOException ignored) {}
+
+ assertEquals(expected, actual);
+ }
+
+ public void assertInputStreamClosed() {
+ try {
+ inputWriter.write(0);
+ fail("input stream not closed");
+ } catch (IOException ignored) {}
+ }
+
+}