Clean up terminal code and unit tests
The terminal unit tests were updated so that they don't rely on an arbitrary delay.
This commit is contained in:
parent
072a432026
commit
38ab1144fb
|
@ -1,8 +1,12 @@
|
|||
package main;
|
||||
|
||||
import static terminal.LispTerminal.END_OF_SEGMENT;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.googlecode.lanterna.terminal.*;
|
||||
|
||||
import interpreter.*;
|
||||
import terminal.LispTerminal;
|
||||
|
||||
|
@ -17,25 +21,27 @@ public class LispMain {
|
|||
private PipedOutputStream inputWriter;
|
||||
private PipedInputStream outputReader;
|
||||
private PipedOutputStream outputWriter;
|
||||
private LispTerminal terminal;
|
||||
private LispTerminal lispTerminal;
|
||||
|
||||
private LispMain() throws IOException {
|
||||
inputReader = new PipedInputStream();
|
||||
inputWriter = new PipedOutputStream(inputReader);
|
||||
outputReader = new PipedInputStream();
|
||||
outputWriter = new PipedOutputStream(outputReader);
|
||||
terminal = new LispTerminal(inputWriter, outputReader);
|
||||
lispTerminal = new LispTerminal(createIOSafeTerminal(), inputWriter, outputReader);
|
||||
}
|
||||
|
||||
private IOSafeTerminal createIOSafeTerminal() throws IOException {
|
||||
return IOSafeTerminalAdapter.createRuntimeExceptionConvertingAdapter(new DefaultTerminalFactory().createTerminal());
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
LispMain main = new LispMain();
|
||||
|
||||
main.run(args);
|
||||
new LispMain().run(args);
|
||||
}
|
||||
|
||||
private void run(String[] args) {
|
||||
if (args.length == 0)
|
||||
terminal.run();
|
||||
lispTerminal.run();
|
||||
|
||||
LispInterpreter interpreter = buildInterpreter(args);
|
||||
interpreter.interpret();
|
||||
|
@ -79,7 +85,7 @@ public class LispMain {
|
|||
|
||||
private void shutdown() {
|
||||
try {
|
||||
terminal.finish();
|
||||
lispTerminal.finish();
|
||||
outputWriter.close();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
|
@ -120,7 +126,7 @@ public class LispMain {
|
|||
|
||||
@Override
|
||||
public String apply(String s) {
|
||||
return s + 'x';
|
||||
return s + END_OF_SEGMENT;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package terminal;
|
||||
|
||||
import static terminal.ControlSequenceHandler.Command.SGR;
|
||||
|
||||
class ControlSequenceHandler {
|
||||
|
||||
public static final boolean isEscape(char c) {
|
||||
return c == '\u001B';
|
||||
}
|
||||
|
||||
private boolean inControlSequence;
|
||||
private int code;
|
||||
private Command command;
|
||||
|
||||
public ControlSequenceHandler() {
|
||||
// TODO Auto-generated constructor stub
|
||||
this.inControlSequence = false;
|
||||
this.code = 0;
|
||||
this.command = SGR;
|
||||
}
|
||||
|
||||
public static enum Command {
|
||||
SGR
|
||||
}
|
||||
|
||||
}
|
|
@ -1,95 +1,55 @@
|
|||
package terminal;
|
||||
|
||||
import static terminal.ControlSequenceHandler.isEscape;
|
||||
import static util.Characters.EOF;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import com.googlecode.lanterna.TerminalPosition;
|
||||
import com.googlecode.lanterna.input.*;
|
||||
import com.googlecode.lanterna.terminal.*;
|
||||
import com.googlecode.lanterna.terminal.IOSafeTerminal;
|
||||
|
||||
public class LispTerminal {
|
||||
|
||||
// private static final String ANSI_RESET = "001B[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 char END_OF_SEGMENT = 'x';
|
||||
|
||||
private IOSafeTerminal terminal;
|
||||
private boolean isFinished;
|
||||
private String currentLine;
|
||||
private TerminalPosition origin;
|
||||
private PipedOutputStream inputWriter;
|
||||
private PipedInputStream outputReader;
|
||||
private ExecutorService executor;
|
||||
private ExecutorService executorService;
|
||||
private ControlSequenceHandler controlSequenceHandler;
|
||||
private TerminalPosition origin;
|
||||
private String inputLine;
|
||||
private String outputSegment;
|
||||
private boolean isFinished;
|
||||
|
||||
LispTerminal(IOSafeTerminal terminal, PipedOutputStream inputWriter, PipedInputStream outputReader) {
|
||||
public LispTerminal(IOSafeTerminal terminal, PipedOutputStream inputWriter, PipedInputStream outputReader) {
|
||||
this.terminal = terminal;
|
||||
initialize(inputWriter, outputReader);
|
||||
}
|
||||
|
||||
public LispTerminal(PipedOutputStream inputWriter, PipedInputStream outputReader) {
|
||||
try {
|
||||
Terminal unsafe = new DefaultTerminalFactory().createTerminal();
|
||||
this.terminal = IOSafeTerminalAdapter.createRuntimeExceptionConvertingAdapter(unsafe);
|
||||
} catch (IOException e) {
|
||||
// TODO
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
initialize(inputWriter, outputReader);
|
||||
}
|
||||
|
||||
private void initialize(PipedOutputStream inputWriter, PipedInputStream outputReader) {
|
||||
this.isFinished = false;
|
||||
this.currentLine = "";
|
||||
this.origin = terminal.getCursorPosition();
|
||||
this.inputWriter = inputWriter;
|
||||
this.outputReader = outputReader;
|
||||
this.executor = Executors.newFixedThreadPool(2);
|
||||
this.executorService = Executors.newFixedThreadPool(2);
|
||||
this.controlSequenceHandler = new ControlSequenceHandler();
|
||||
this.origin = terminal.getCursorPosition();
|
||||
this.inputLine = "";
|
||||
this.outputSegment = "";
|
||||
this.isFinished = false;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
executor.execute(this::readInput);
|
||||
executor.execute(this::writeOutput);
|
||||
executor.shutdown();
|
||||
executorService.execute(this::readInput);
|
||||
executorService.execute(this::writeOutput);
|
||||
executorService.shutdown();
|
||||
}
|
||||
|
||||
public void readInput() {
|
||||
while (!isFinished) {
|
||||
handleKeyStroke(getKeyStroke());
|
||||
terminal.flush();
|
||||
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
isFinished = true;
|
||||
}
|
||||
}
|
||||
while (!isFinished)
|
||||
processNextKey();
|
||||
}
|
||||
|
||||
public void writeOutput() {
|
||||
try {
|
||||
for (int c = outputReader.read(); c != -1; c = outputReader.read()) {
|
||||
synchronized (this) {
|
||||
terminal.setCursorVisible(false);
|
||||
|
||||
if (c == 'x') {
|
||||
terminal.flush();
|
||||
origin = terminal.getCursorPosition();
|
||||
terminal.setCursorVisible(true);
|
||||
} else {
|
||||
terminal.putCharacter((char) c);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
terminal.setCursorVisible(true);
|
||||
terminal.close();
|
||||
private void processNextKey() {
|
||||
handleKey(getKeyStroke());
|
||||
takeNap();
|
||||
}
|
||||
|
||||
private KeyStroke getKeyStroke() {
|
||||
|
@ -98,6 +58,7 @@ public class LispTerminal {
|
|||
try {
|
||||
keyStroke = terminal.pollInput();
|
||||
} catch (IllegalStateException e) {
|
||||
// TODO - Issue #299
|
||||
terminal.putCharacter('\n');
|
||||
System.exit(0);
|
||||
}
|
||||
|
@ -105,14 +66,19 @@ public class LispTerminal {
|
|||
return keyStroke;
|
||||
}
|
||||
|
||||
private synchronized void handleKeyStroke(KeyStroke keyStroke) {
|
||||
private synchronized void handleKey(KeyStroke keyStroke) {
|
||||
if (keyStroke == null)
|
||||
return;
|
||||
|
||||
doKey(keyStroke);
|
||||
terminal.flush();
|
||||
}
|
||||
|
||||
private synchronized void doKey(KeyStroke keyStroke) {
|
||||
KeyType keyType = keyStroke.getKeyType();
|
||||
|
||||
if (keyStroke.isCtrlDown())
|
||||
doControlKeyCharacter(keyStroke);
|
||||
doControlKey(keyStroke);
|
||||
else if (keyType == KeyType.ArrowLeft)
|
||||
moveCursorLeft();
|
||||
else if (keyType == KeyType.ArrowRight)
|
||||
|
@ -127,40 +93,28 @@ public class LispTerminal {
|
|||
doCharacter(keyStroke);
|
||||
}
|
||||
|
||||
private synchronized void doControlKeyCharacter(KeyStroke keyStroke) {
|
||||
private synchronized void doControlKey(KeyStroke keyStroke) {
|
||||
KeyType keyType = keyStroke.getKeyType();
|
||||
|
||||
if (keyType == KeyType.Character)
|
||||
if (keyStroke.getCharacter() == 'd') {
|
||||
doControlCharacter(keyStroke);
|
||||
}
|
||||
|
||||
private synchronized void doControlCharacter(KeyStroke keyStroke) {
|
||||
if (keyStroke.getCharacter() == 'd')
|
||||
doControlD();
|
||||
}
|
||||
|
||||
private synchronized void doControlD() {
|
||||
doEnter();
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void doEnter() {
|
||||
terminal.putCharacter('\n');
|
||||
currentLine += "\n";
|
||||
|
||||
try {
|
||||
inputWriter.write(currentLine.getBytes());
|
||||
inputWriter.flush();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
currentLine = "";
|
||||
origin = terminal.getCursorPosition();
|
||||
}
|
||||
|
||||
private synchronized void moveCursorLeft() {
|
||||
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
||||
|
||||
if (isPossibleToMoveLeft(cursorPosition))
|
||||
if (cursorPosition.getColumn() == 0)
|
||||
terminal.setCursorPosition(terminal.getTerminalSize().getColumns(), cursorPosition.getRow() - 1);
|
||||
else
|
||||
terminal.setCursorPosition(cursorPosition.withRelativeColumn(-1));
|
||||
retractCursor(cursorPosition);
|
||||
}
|
||||
|
||||
private synchronized boolean isPossibleToMoveLeft(TerminalPosition cursorPosition) {
|
||||
|
@ -168,13 +122,22 @@ public class LispTerminal {
|
|||
}
|
||||
|
||||
private synchronized int distanceFromOrigin(TerminalPosition cursorPosition) {
|
||||
int cursorColumn = cursorPosition.getColumn();
|
||||
int cursorRow = cursorPosition.getRow();
|
||||
int originColumn = origin.getColumn();
|
||||
int originRow = origin.getRow();
|
||||
int columnDifference = cursorPosition.getColumn() - origin.getColumn();
|
||||
int rowDifference = cursorPosition.getRow() - origin.getRow();
|
||||
int totalColumns = terminal.getTerminalSize().getColumns();
|
||||
|
||||
return cursorColumn - originColumn + (totalColumns * (cursorRow - originRow));
|
||||
return columnDifference + (totalColumns * rowDifference);
|
||||
}
|
||||
|
||||
private synchronized void retractCursor(TerminalPosition cursorPosition) {
|
||||
if (isAtStartOfRow(cursorPosition))
|
||||
terminal.setCursorPosition(terminal.getTerminalSize().getColumns(), cursorPosition.getRow() - 1);
|
||||
else
|
||||
terminal.setCursorPosition(cursorPosition.withRelativeColumn(-1));
|
||||
}
|
||||
|
||||
private boolean isAtStartOfRow(TerminalPosition cursorPosition) {
|
||||
return cursorPosition.getColumn() == 0;
|
||||
}
|
||||
|
||||
private synchronized void moveCursorRight() {
|
||||
|
@ -184,42 +147,52 @@ public class LispTerminal {
|
|||
advanceCursor(cursorPosition);
|
||||
}
|
||||
|
||||
private synchronized boolean isPossibleToMoveRight(TerminalPosition cursorPosition) {
|
||||
return distanceFromOrigin(cursorPosition) < inputLine.length();
|
||||
}
|
||||
|
||||
private synchronized void advanceCursor(TerminalPosition cursorPosition) {
|
||||
if (isCursorAtEndOfRow(cursorPosition))
|
||||
if (isAtEndOfRow(cursorPosition))
|
||||
terminal.setCursorPosition(0, cursorPosition.getRow() + 1);
|
||||
else
|
||||
terminal.setCursorPosition(cursorPosition.withRelativeColumn(1));
|
||||
}
|
||||
|
||||
private synchronized boolean isCursorAtEndOfRow(TerminalPosition cursorPosition) {
|
||||
private synchronized boolean isAtEndOfRow(TerminalPosition cursorPosition) {
|
||||
return cursorPosition.getColumn() == terminal.getTerminalSize().getColumns() - 1;
|
||||
}
|
||||
|
||||
private synchronized boolean isPossibleToMoveRight(TerminalPosition cursorPosition) {
|
||||
return distanceFromOrigin(cursorPosition) < currentLine.length();
|
||||
private synchronized void doEnter() {
|
||||
terminal.putCharacter('\n');
|
||||
inputLine += "\n";
|
||||
|
||||
try {
|
||||
inputWriter.write(inputLine.getBytes());
|
||||
inputWriter.flush();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
inputLine = "";
|
||||
origin = terminal.getCursorPosition();
|
||||
}
|
||||
|
||||
private synchronized void doBackspace() {
|
||||
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
||||
|
||||
if (isPossibleToMoveLeft(cursorPosition)) {
|
||||
String remaining = currentLine.substring(distanceFromOrigin(cursorPosition), currentLine.length());
|
||||
currentLine = currentLine.substring(0, distanceFromOrigin(cursorPosition) - 1) + remaining;
|
||||
String remaining = inputLine.substring(distanceFromOrigin(cursorPosition), inputLine.length());
|
||||
inputLine = inputLine.substring(0, distanceFromOrigin(cursorPosition) - 1) + remaining;
|
||||
|
||||
if (cursorPosition.getColumn() == 0)
|
||||
terminal.setCursorPosition(terminal.getTerminalSize().getColumns(), cursorPosition.getRow() - 1);
|
||||
else
|
||||
terminal.setCursorPosition(cursorPosition.withRelativeColumn(-1));
|
||||
retractCursor(cursorPosition);
|
||||
|
||||
for (char c : remaining.toCharArray())
|
||||
terminal.putCharacter(c);
|
||||
|
||||
terminal.putCharacter(' ');
|
||||
|
||||
if (cursorPosition.getColumn() == 0)
|
||||
terminal.setCursorPosition(terminal.getTerminalSize().getColumns(), cursorPosition.getRow() - 1);
|
||||
else
|
||||
terminal.setCursorPosition(cursorPosition.withRelativeColumn(-1));
|
||||
retractCursor(cursorPosition);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -227,8 +200,8 @@ public class LispTerminal {
|
|||
TerminalPosition cursorPosition = terminal.getCursorPosition();
|
||||
|
||||
if (isPossibleToMoveRight(cursorPosition)) {
|
||||
String remaining = currentLine.substring(distanceFromOrigin(cursorPosition) + 1, currentLine.length());
|
||||
currentLine = currentLine.substring(0, distanceFromOrigin(cursorPosition)) + remaining;
|
||||
String remaining = inputLine.substring(distanceFromOrigin(cursorPosition) + 1, inputLine.length());
|
||||
inputLine = inputLine.substring(0, distanceFromOrigin(cursorPosition)) + remaining;
|
||||
|
||||
for (char c : remaining.toCharArray())
|
||||
terminal.putCharacter(c);
|
||||
|
@ -243,8 +216,8 @@ public class LispTerminal {
|
|||
|
||||
if (isPossibleToMoveRight(cursorPosition)) {
|
||||
String remaining = keyStroke.getCharacter()
|
||||
+ currentLine.substring(distanceFromOrigin(cursorPosition), currentLine.length());
|
||||
currentLine = currentLine.substring(0, distanceFromOrigin(cursorPosition)) + remaining;
|
||||
+ inputLine.substring(distanceFromOrigin(cursorPosition), inputLine.length());
|
||||
inputLine = inputLine.substring(0, distanceFromOrigin(cursorPosition)) + remaining;
|
||||
|
||||
for (char c : remaining.toCharArray())
|
||||
terminal.putCharacter(c);
|
||||
|
@ -252,12 +225,61 @@ public class LispTerminal {
|
|||
advanceCursor(cursorPosition);
|
||||
} else {
|
||||
terminal.putCharacter(keyStroke.getCharacter());
|
||||
currentLine += keyStroke.getCharacter();
|
||||
inputLine += keyStroke.getCharacter();
|
||||
|
||||
advanceCursor(cursorPosition);
|
||||
}
|
||||
}
|
||||
|
||||
private void takeNap() {
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
isFinished = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void writeOutput() {
|
||||
try {
|
||||
for (int c = outputReader.read(); c != EOF; c = outputReader.read())
|
||||
processOutput((char) c);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
terminal.setCursorVisible(true);
|
||||
terminal.flush();
|
||||
terminal.close();
|
||||
}
|
||||
|
||||
private synchronized void processOutput(char c) {
|
||||
if (isEscape(c))
|
||||
parseControlSequence();
|
||||
else if (isEndOfSegment(c))
|
||||
printSegment();
|
||||
else
|
||||
outputSegment += c;
|
||||
}
|
||||
|
||||
private synchronized void parseControlSequence() {}
|
||||
|
||||
private synchronized boolean isEndOfSegment(char c) {
|
||||
return c == END_OF_SEGMENT;
|
||||
}
|
||||
|
||||
private synchronized void printSegment() {
|
||||
terminal.setCursorVisible(false);
|
||||
|
||||
for (char c : outputSegment.toCharArray())
|
||||
terminal.putCharacter(c);
|
||||
|
||||
terminal.flush();
|
||||
terminal.setCursorVisible(true);
|
||||
outputSegment = "";
|
||||
origin = terminal.getCursorPosition();
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
isFinished = true;
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package terminal;
|
||||
|
||||
import com.googlecode.lanterna.TerminalSize;
|
||||
import com.googlecode.lanterna.terminal.Terminal;
|
||||
import com.googlecode.lanterna.terminal.virtual.VirtualTerminalListener;
|
||||
|
||||
public class FlushListener implements VirtualTerminalListener {
|
||||
|
||||
private int flushCount;
|
||||
|
||||
public FlushListener() {
|
||||
this.flushCount = 0;
|
||||
}
|
||||
|
||||
public int getFlushCount() {
|
||||
return flushCount;
|
||||
}
|
||||
|
||||
public void resetFlushCount() {
|
||||
flushCount = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResized(Terminal terminal, TerminalSize newSize) {}
|
||||
|
||||
@Override
|
||||
public synchronized void onFlush() {
|
||||
flushCount++;
|
||||
notify();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBell() {}
|
||||
|
||||
@Override
|
||||
public void onClose() {}
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package terminal;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.*;
|
||||
import static terminal.LispTerminal.END_OF_SEGMENT;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
|
@ -10,52 +11,100 @@ import com.googlecode.lanterna.*;
|
|||
import com.googlecode.lanterna.input.*;
|
||||
import com.googlecode.lanterna.terminal.virtual.*;
|
||||
|
||||
public class LispTerminalAttempt {
|
||||
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));
|
||||
sleep();
|
||||
waitForFlushes(1);
|
||||
}
|
||||
|
||||
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));
|
||||
sleep();
|
||||
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));
|
||||
|
||||
sleep();
|
||||
waitForFlushes(characters.length());
|
||||
}
|
||||
|
||||
private void sleep() {
|
||||
private void produceOutput(String output) {
|
||||
try {
|
||||
Thread.sleep(15);
|
||||
for (char c : output.toCharArray())
|
||||
outputWriter.write(c);
|
||||
|
||||
outputWriter.write(END_OF_SEGMENT);
|
||||
outputWriter.flush();
|
||||
waitForFlushes(1);
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
|
||||
private void waitForFlushes(int flushCount) {
|
||||
try {
|
||||
synchronized (flushListener) {
|
||||
while (flushListener.getFlushCount() < flushCount)
|
||||
flushListener.wait();
|
||||
|
||||
flushListener.resetFlushCount();
|
||||
}
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
|
||||
private void assertCursorPosition(int column, int row) {
|
||||
assertTrue(virtualTerminal.getCursorBufferPosition().getColumn() == column);
|
||||
assertTrue(virtualTerminal.getCursorBufferPosition().getRow() == row);
|
||||
}
|
||||
|
||||
private void assertCharacterAtPosition(char character, int column, int row) {
|
||||
TerminalPosition position = new TerminalPosition(column, row);
|
||||
assertTrue(virtualTerminal.getBufferCharacter(position).getCharacter() == character);
|
||||
}
|
||||
|
||||
private void setColumns(int columns) {
|
||||
virtualTerminal.setTerminalSize(new TerminalSize(columns, virtualTerminal.getTerminalSize().getRows()));
|
||||
}
|
||||
|
||||
private void assertCursorPosition(int column, int row) {
|
||||
assertEquals(column, virtualTerminal.getCursorBufferPosition().getColumn());
|
||||
assertEquals(row, virtualTerminal.getCursorBufferPosition().getRow());
|
||||
}
|
||||
|
||||
private void assertCharacterAtPosition(char character, int column, int row) {
|
||||
TerminalPosition position = new TerminalPosition(column, row);
|
||||
assertEquals(character, virtualTerminal.getBufferCharacter(position).getCharacter());
|
||||
}
|
||||
|
||||
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 e) {}
|
||||
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
private void assertInputStreamClosed() {
|
||||
try {
|
||||
inputWriter.write(0);
|
||||
fail("input stream not closed");
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
inputReader = new PipedInputStream();
|
||||
|
@ -63,6 +112,8 @@ public class LispTerminalAttempt {
|
|||
outputReader = new PipedInputStream();
|
||||
outputWriter = new PipedOutputStream(outputReader);
|
||||
virtualTerminal = new DefaultVirtualTerminal();
|
||||
flushListener = new FlushListener();
|
||||
virtualTerminal.addVirtualTerminalListener(flushListener);
|
||||
lispTerminal = new LispTerminal(virtualTerminal, inputWriter, outputReader);
|
||||
lispTerminal.run();
|
||||
}
|
||||
|
@ -222,4 +273,135 @@ public class LispTerminalAttempt {
|
|||
assertCharacterAtPosition('5', 3, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteDoesNothingAtOrigin() {
|
||||
pressKey(KeyType.Delete);
|
||||
assertCursorPosition(0, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteDoesNothingAtEndOfInput() {
|
||||
enterCharacters("del");
|
||||
pressKey(KeyType.Delete);
|
||||
assertCursorPosition(3, 0);
|
||||
assertCharacterAtPosition('d', 0, 0);
|
||||
assertCharacterAtPosition('e', 1, 0);
|
||||
assertCharacterAtPosition('l', 2, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteWorksAtStartOfInput() {
|
||||
enterCharacters("del");
|
||||
pressKey(KeyType.ArrowLeft);
|
||||
pressKey(KeyType.ArrowLeft);
|
||||
pressKey(KeyType.ArrowLeft);
|
||||
pressKey(KeyType.Delete);
|
||||
pressKey(KeyType.Delete);
|
||||
pressKey(KeyType.Delete);
|
||||
assertCursorPosition(0, 0);
|
||||
assertCharacterAtPosition(' ', 0, 0);
|
||||
assertCharacterAtPosition(' ', 1, 0);
|
||||
assertCharacterAtPosition(' ', 2, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteWorksAcrossRow() {
|
||||
setColumns(4);
|
||||
enterCharacters("delete");
|
||||
pressKey(KeyType.ArrowLeft);
|
||||
pressKey(KeyType.ArrowLeft);
|
||||
pressKey(KeyType.ArrowLeft);
|
||||
pressKey(KeyType.ArrowLeft);
|
||||
pressKey(KeyType.ArrowLeft);
|
||||
pressKey(KeyType.Delete);
|
||||
assertCursorPosition(1, 0);
|
||||
assertCharacterAtPosition('d', 0, 0);
|
||||
assertCharacterAtPosition('l', 1, 0);
|
||||
assertCharacterAtPosition('e', 2, 0);
|
||||
assertCharacterAtPosition('t', 3, 0);
|
||||
assertCharacterAtPosition('e', 0, 1);
|
||||
assertCharacterAtPosition(' ', 1, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enterMovesToNextLine() {
|
||||
pressKey(KeyType.Enter);
|
||||
assertCursorPosition(0, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enterWritesLineToPipedStream() {
|
||||
enterCharacters("enter");
|
||||
pressKey(KeyType.Enter);
|
||||
assertInputWritten("enter\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enterPressedInMiddleOfInput_WritesEntireLineToPipedStream() {
|
||||
enterCharacters("enter");
|
||||
pressKey(KeyType.ArrowLeft);
|
||||
pressKey(KeyType.ArrowLeft);
|
||||
pressKey(KeyType.Enter);
|
||||
assertInputWritten("enter\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void controlDWorks() {
|
||||
enterCharacters("control-d");
|
||||
enterControlCharacter('d');
|
||||
assertInputStreamClosed();
|
||||
assertInputWritten("control-d\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void controlDWorksInMiddleOfInput() {
|
||||
enterCharacters("control-d");
|
||||
pressKey(KeyType.ArrowLeft);
|
||||
pressKey(KeyType.ArrowLeft);
|
||||
enterControlCharacter('d');
|
||||
assertInputStreamClosed();
|
||||
assertInputWritten("control-d\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void escapeDoesNothing() {
|
||||
pressKey(KeyType.Escape);
|
||||
assertCursorPosition(0, 0);
|
||||
assertInputWritten("");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void controlQDoesNothing() {
|
||||
enterControlCharacter('q');
|
||||
assertCursorPosition(0, 0);
|
||||
assertInputWritten("");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void controlEnterDoesNothing() {
|
||||
pressControlKey(KeyType.Enter);
|
||||
assertCursorPosition(0, 0);
|
||||
assertInputWritten("");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputIsWritten() {
|
||||
produceOutput("output");
|
||||
assertCharacterAtPosition('o', 0, 0);
|
||||
assertCharacterAtPosition('u', 1, 0);
|
||||
assertCharacterAtPosition('t', 2, 0);
|
||||
assertCharacterAtPosition('p', 3, 0);
|
||||
assertCharacterAtPosition('u', 4, 0);
|
||||
assertCharacterAtPosition('t', 5, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void endOfSegmentCharacterIsNotPrinted() {
|
||||
produceOutput("> " + END_OF_SEGMENT);
|
||||
assertCursorPosition(2, 0);
|
||||
assertCharacterAtPosition('>', 0, 0);
|
||||
assertCharacterAtPosition(' ', 1, 0);
|
||||
assertCharacterAtPosition(' ', 2, 0);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue