Increase test coverage and refactor code

This commit is contained in:
Mike Cifelli 2017-03-23 12:14:44 -04:00
parent 79fb8b633b
commit 23dd1c0654
25 changed files with 350 additions and 101 deletions

View File

@ -1,9 +1,9 @@
package error; package error;
import static error.ErrorManager.Severity.*; import static error.ErrorManager.Severity.*;
import static java.text.MessageFormat.format;
import java.io.PrintStream; import java.io.PrintStream;
import java.text.MessageFormat;
import environment.RuntimeEnvironment; import environment.RuntimeEnvironment;
@ -40,7 +40,7 @@ public class ErrorManager {
private String formatMessage(LispException lispException) { private String formatMessage(LispException lispException) {
Severity severity = lispException.getSeverity(); Severity severity = lispException.getSeverity();
String prefix = severity.toDisplayString(); String prefix = severity.toDisplayString();
String message = MessageFormat.format("[{0}] {1}", prefix, lispException.getMessage()); String message = format("[{0}] {1}", prefix, lispException.getMessage());
return severity.decorate(message, environment); return severity.decorate(message, environment);
} }

View File

@ -1,6 +1,6 @@
package error; package error;
import java.text.MessageFormat; import static java.text.MessageFormat.format;
import file.FilePosition; import file.FilePosition;
@ -15,8 +15,8 @@ public abstract class LineColumnException extends LispException {
@Override @Override
public String getMessage() { public String getMessage() {
return MessageFormat.format("{0} - line {1}, column {2}", getMessagePrefix(), position.getLineNumber(), return format("{0} - line {1}, column {2}", getMessagePrefix(), position.getLineNumber(),
position.getColumnNumber()); position.getColumnNumber());
} }
public abstract String getMessagePrefix(); public abstract String getMessagePrefix();

View File

@ -1,9 +1,9 @@
package function; package function;
import static function.builtin.cons.LENGTH.getLength; import static function.builtin.cons.LENGTH.getLength;
import static java.text.MessageFormat.format;
import java.math.BigInteger; import java.math.BigInteger;
import java.text.MessageFormat;
import error.LispException; import error.LispException;
import sexpression.*; import sexpression.*;
@ -163,7 +163,7 @@ public class ArgumentValidator {
@Override @Override
public String getMessage() { public String getMessage() {
return MessageFormat.format("too few arguments given to {0}: {1}", functionName, argumentList); return format("too few arguments given to {0}: {1}", functionName, argumentList);
} }
} }
@ -180,7 +180,7 @@ public class ArgumentValidator {
@Override @Override
public String getMessage() { public String getMessage() {
return MessageFormat.format("too many arguments given to {0}: {1}", functionName, argumentList); return format("too many arguments given to {0}: {1}", functionName, argumentList);
} }
} }
@ -197,7 +197,7 @@ public class ArgumentValidator {
@Override @Override
public String getMessage() { public String getMessage() {
return MessageFormat.format("dotted argument list given to {0}: {1}", functionName, argumentList); return format("dotted argument list given to {0}: {1}", functionName, argumentList);
} }
} }
@ -217,8 +217,8 @@ public class ArgumentValidator {
@Override @Override
public String getMessage() { public String getMessage() {
return MessageFormat.format("{0}: {1} is not the expected type of ''{2}''", functionName, argument, return format("{0}: {1} is not the expected type of ''{2}''", functionName, argument,
getExpectedTypeName()); getExpectedTypeName());
} }
private String getExpectedTypeName() { private String getExpectedTypeName() {

View File

@ -1,9 +1,9 @@
package function; package function;
import static function.builtin.EVAL.eval; import static function.builtin.EVAL.eval;
import static java.text.MessageFormat.format;
import static sexpression.Nil.NIL; import static sexpression.Nil.NIL;
import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import error.LispException; import error.LispException;
@ -140,8 +140,8 @@ public class UserDefinedFunction extends LispFunction {
@Override @Override
public String getMessage() { public String getMessage() {
return MessageFormat.format("unexpected parameters following ''&rest'' in definition of {0}: {1}", return format("unexpected parameters following ''&rest'' in definition of {0}: {1}", functionName,
functionName, parameters); parameters);
} }
} }

View File

@ -2,12 +2,11 @@ package function.builtin;
import static function.builtin.cons.LIST.makeList; import static function.builtin.cons.LIST.makeList;
import static function.builtin.special.LAMBDA.*; import static function.builtin.special.LAMBDA.*;
import static java.text.MessageFormat.format;
import static sexpression.Nil.NIL; import static sexpression.Nil.NIL;
import static sexpression.Symbol.T; import static sexpression.Symbol.T;
import static table.FunctionTable.lookupFunction; import static table.FunctionTable.lookupFunction;
import java.text.MessageFormat;
import error.LispException; import error.LispException;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@ -150,7 +149,7 @@ public class EVAL extends LispFunction {
@Override @Override
public String getMessage() { public String getMessage() {
return MessageFormat.format("undefined function: {0}", function); return format("undefined function: {0}", function);
} }
} }
@ -165,7 +164,7 @@ public class EVAL extends LispFunction {
@Override @Override
public String getMessage() { public String getMessage() {
return MessageFormat.format("symbol {0} has no value", symbol); return format("symbol {0} has no value", symbol);
} }
} }

View File

@ -1,12 +1,12 @@
package function.builtin; package function.builtin;
import static function.builtin.EVAL.eval; import static function.builtin.EVAL.eval;
import static java.text.MessageFormat.format;
import static sexpression.Nil.NIL; import static sexpression.Nil.NIL;
import static sexpression.Symbol.T; import static sexpression.Symbol.T;
import static util.Path.getPathPrefix; import static util.Path.getPathPrefix;
import java.io.*; import java.io.*;
import java.text.MessageFormat;
import java.util.Stack; import java.util.Stack;
import environment.RuntimeEnvironment; import environment.RuntimeEnvironment;
@ -105,7 +105,7 @@ public class LOAD extends LispFunction {
@Override @Override
public String getMessage() { public String getMessage() {
return MessageFormat.format("could not load ''{0}''", fileName); return format("could not load ''{0}''", fileName);
} }
} }

View File

@ -1,9 +1,8 @@
package function.builtin; package function.builtin;
import static java.text.MessageFormat.format;
import static table.FunctionTable.lookupFunction; import static table.FunctionTable.lookupFunction;
import java.text.MessageFormat;
import error.LispException; import error.LispException;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@ -38,7 +37,7 @@ public class SYMBOL_FUNCTION extends LispFunction {
String typeIndicator = function instanceof LispSpecialFunction ? "SPECIAL-FUNCTION" : "FUNCTION"; String typeIndicator = function instanceof LispSpecialFunction ? "SPECIAL-FUNCTION" : "FUNCTION";
return new Symbol(MessageFormat.format("#<{0} {1}>", typeIndicator, symbol)); return new Symbol(format("#<{0} {1}>", typeIndicator, symbol));
} }
public static class UndefinedSymbolFunctionException extends LispException { public static class UndefinedSymbolFunctionException extends LispException {
@ -52,7 +51,7 @@ public class SYMBOL_FUNCTION extends LispFunction {
@Override @Override
public String getMessage() { public String getMessage() {
return MessageFormat.format("SYMBOL-FUNCTION: undefined function: {0}", function); return format("SYMBOL-FUNCTION: undefined function: {0}", function);
} }
} }

View File

@ -1,10 +1,9 @@
package function.builtin.special; package function.builtin.special;
import static function.builtin.cons.LIST.makeList; import static function.builtin.cons.LIST.makeList;
import static java.text.MessageFormat.format;
import static table.FunctionTable.*; import static table.FunctionTable.*;
import java.text.MessageFormat;
import environment.RuntimeEnvironment; import environment.RuntimeEnvironment;
import error.LispWarning; import error.LispWarning;
import function.*; import function.*;
@ -67,7 +66,7 @@ public abstract class Define extends LispSpecialFunction {
@Override @Override
public String getMessage() { public String getMessage() {
return MessageFormat.format("redefining function {0}", functionName); return format("redefining function {0}", functionName);
} }
} }

View File

@ -2,7 +2,7 @@ package interpreter;
public class InteractiveLispInterpreter extends LispInterpreter { public class InteractiveLispInterpreter extends LispInterpreter {
private static final String PROMPT = "~ "; public static final String PROMPT = "~ ";
@Override @Override
protected void prompt() { protected void prompt() {

View File

@ -1,5 +1,6 @@
package main; package main;
import static com.googlecode.lanterna.terminal.IOSafeTerminalAdapter.createRuntimeExceptionConvertingAdapter;
import static terminal.LispTerminal.END_OF_SEGMENT; import static terminal.LispTerminal.END_OF_SEGMENT;
import java.io.*; import java.io.*;
@ -8,7 +9,6 @@ import java.util.function.Function;
import com.googlecode.lanterna.terminal.*; import com.googlecode.lanterna.terminal.*;
import interpreter.*; import interpreter.*;
import stream.SafeOutputStream;
import terminal.LispTerminal; import terminal.LispTerminal;
public class LispMain { public class LispMain {
@ -30,58 +30,76 @@ public class LispMain {
lispMain.runWithFile(arguments[0]); lispMain.runWithFile(arguments[0]);
} }
private LispInterpreterBuilder builder;
private PipedInputStream inputReader; private PipedInputStream inputReader;
private PipedOutputStream inputWriter;
private PipedInputStream outputReader;
private PipedOutputStream outputWriter; private PipedOutputStream outputWriter;
private PrintStream outputStream; private PrintStream output;
private SafeOutputStream safeOutputWriter;
private LispTerminal lispTerminal; private LispTerminal lispTerminal;
private void runInteractive() { public LispMain() {
initializeTerminalAndStreams(); this.builder = LispInterpreterBuilderImpl.getInstance();
printGreeting();
lispTerminal.start(); TerminalConfiguration terminalConfiguration = new TerminalConfiguration();
buildInteractiveInterpreter().interpret(); terminalConfiguration.setInputWriter(new PipedOutputStream());
outputStream.close(); terminalConfiguration.setOutputReader(new PipedInputStream());
terminalConfiguration.setTerminal(createIOSafeTerminal());
initializeTerminal(terminalConfiguration);
} }
private void initializeTerminalAndStreams() { private IOSafeTerminal createIOSafeTerminal() {
return createRuntimeExceptionConvertingAdapter(createDefaultTerminal());
}
private Terminal createDefaultTerminal() {
try { try {
initalizeTerminalAndStreamsWithException(); return new DefaultTerminalFactory().createTerminal();
} catch (IOException e) { } catch (IOException e) {
throw new UncheckedIOException(e); throw new UncheckedIOException(e);
} }
} }
private void initalizeTerminalAndStreamsWithException() throws IOException { public LispMain(LispInterpreterBuilder builder, TerminalConfiguration configuration) {
inputReader = new PipedInputStream(); this.builder = builder;
inputWriter = new PipedOutputStream(inputReader); initializeTerminal(configuration);
outputReader = new PipedInputStream();
outputWriter = new PipedOutputStream(outputReader);
outputStream = new PrintStream(outputWriter);
safeOutputWriter = new SafeOutputStream(outputWriter);
lispTerminal = new LispTerminal(createIOSafeTerminal(), inputWriter, outputReader);
} }
private IOSafeTerminal createIOSafeTerminal() throws IOException { private void initializeTerminal(TerminalConfiguration configuration) {
Terminal defaultTerminal = new DefaultTerminalFactory().createTerminal(); try {
initalizeTerminalWithException(configuration);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
return IOSafeTerminalAdapter.createRuntimeExceptionConvertingAdapter(defaultTerminal); private void initalizeTerminalWithException(TerminalConfiguration configuration) throws IOException {
PipedOutputStream inputWriter = configuration.getInputWriter();
PipedInputStream outputReader = configuration.getOutputReader();
inputReader = new PipedInputStream(inputWriter);
outputWriter = new PipedOutputStream(outputReader);
output = new PrintStream(outputWriter);
lispTerminal = new LispTerminal(configuration.getTerminal(), inputWriter, outputReader);
}
public void runInteractive() {
printGreeting();
lispTerminal.start();
buildInteractiveInterpreter().interpret();
shutdownTerminal();
} }
private void printGreeting() { private void printGreeting() {
outputStream.println(GREETING); output.println(GREETING);
outputStream.println(); output.println();
} }
private LispInterpreter buildInteractiveInterpreter() { private LispInterpreter buildInteractiveInterpreter() {
LispInterpreterBuilder builder = LispInterpreterBuilderImpl.getInstance();
builder.setInput(inputReader, "terminal"); builder.setInput(inputReader, "terminal");
builder.setOutput(outputStream); builder.setOutput(output);
builder.setErrorOutput(outputStream); builder.setErrorOutput(output);
builder.setTerminationFunction(this::shutdown); builder.setTerminationFunction(this::shutdownTerminal);
builder.setErrorTerminationFunction(this::shutdown); builder.setErrorTerminationFunction(this::shutdownTerminal);
builder.setPromptDecorator(s -> s + END_OF_SEGMENT); builder.setPromptDecorator(s -> s + END_OF_SEGMENT);
builder.setValueOutputDecorator(makeColorDecorator(ANSI_GREEN)); builder.setValueOutputDecorator(makeColorDecorator(ANSI_GREEN));
builder.setWarningOutputDecorator(makeColorDecorator(ANSI_YELLOW)); builder.setWarningOutputDecorator(makeColorDecorator(ANSI_YELLOW));
@ -91,11 +109,10 @@ public class LispMain {
return builder.build(); return builder.build();
} }
private void shutdown() { private void shutdownTerminal() {
outputStream.println(); output.print(END_OF_SEGMENT);
outputStream.println(END_OF_SEGMENT);
lispTerminal.stop(); lispTerminal.stop();
safeOutputWriter.close(); output.close();
} }
private Function<String, String> makeColorDecorator(String color) { private Function<String, String> makeColorDecorator(String color) {
@ -108,12 +125,11 @@ public class LispMain {
}; };
} }
private void runWithFile(String fileName) { public void runWithFile(String fileName) {
buildFileInterpreter(fileName).interpret(); buildFileInterpreter(fileName).interpret();
} }
private LispInterpreter buildFileInterpreter(String fileName) { private LispInterpreter buildFileInterpreter(String fileName) {
LispInterpreterBuilder builder = LispInterpreterBuilderImpl.getInstance();
builder.useFile(fileName); builder.useFile(fileName);
builder.setOutput(System.out); builder.setOutput(System.out);
builder.setErrorOutput(System.err); builder.setErrorOutput(System.err);
@ -126,4 +142,36 @@ public class LispMain {
return builder.build(); 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;
}
}
} }

View File

@ -1,7 +1,8 @@
package sexpression; package sexpression;
import static java.text.MessageFormat.format;
import java.math.BigInteger; import java.math.BigInteger;
import java.text.MessageFormat;
import error.LispException; import error.LispException;
@ -49,7 +50,7 @@ public class LispNumber extends Atom {
@Override @Override
public String getMessage() { public String getMessage() {
return MessageFormat.format("{0} is not a valid integer", text); return format("{0} is not a valid integer", text);
} }
} }

View File

@ -1,6 +1,7 @@
package table; package table;
import java.text.MessageFormat; import static java.text.MessageFormat.format;
import java.util.*; import java.util.*;
import error.CriticalLispException; import error.CriticalLispException;
@ -141,7 +142,7 @@ public class FunctionTable {
@Override @Override
public String getMessage() { public String getMessage() {
return MessageFormat.format("Could not create an instance of ''{0}''", functionName); return format("Could not create an instance of ''{0}''", functionName);
} }
} }

View File

@ -1,7 +1,6 @@
package terminal; package terminal;
import static java.lang.Character.isDigit; import static java.lang.Character.isDigit;
import static terminal.ControlSequenceLookup.lookupControlSequence;
import static util.Characters.*; import static util.Characters.*;
import stream.SafeInputStream; import stream.SafeInputStream;
@ -12,14 +11,13 @@ class ControlSequenceHandler {
return c == UNICODE_ESCAPE; return c == UNICODE_ESCAPE;
} }
private ControlSequenceLookup controlSequenceLookup;
private SafeInputStream input; private SafeInputStream input;
private String code; private String code;
private int currentCharacter; private int currentCharacter;
public ControlSequenceHandler() { public ControlSequenceHandler() {
this.input = null; this.controlSequenceLookup = new ControlSequenceLookup();
this.code = "";
this.currentCharacter = 0;
} }
public ControlSequence parse(SafeInputStream inputStream) { public ControlSequence parse(SafeInputStream inputStream) {
@ -30,7 +28,7 @@ class ControlSequenceHandler {
if (isExpectedFirstCharacter()) if (isExpectedFirstCharacter())
readCode(); readCode();
return lookupControlSequence((char) currentCharacter, code); return controlSequenceLookup.get((char) currentCharacter, code);
} }
private void readCharacter() { private void readCharacter() {

View File

@ -8,9 +8,15 @@ import terminal.ControlSequence.NullControlSequence;
public class ControlSequenceLookup { public class ControlSequenceLookup {
private static Map<Character, Map<String, ControlSequence>> commands = new HashMap<>(); private Map<Character, Map<String, ControlSequence>> commands;
static { public ControlSequenceLookup() {
this.commands = new HashMap<>();
initializeCommands();
}
private void initializeCommands() {
Map<String, ControlSequence> sgrCodes = new HashMap<>(); Map<String, ControlSequence> sgrCodes = new HashMap<>();
for (SelectGraphicRendition sgr : SelectGraphicRendition.values()) for (SelectGraphicRendition sgr : SelectGraphicRendition.values())
@ -19,7 +25,7 @@ public class ControlSequenceLookup {
commands.put(SGR_COMMAND, sgrCodes); commands.put(SGR_COMMAND, sgrCodes);
} }
public static ControlSequence lookupControlSequence(char command, String code) { public ControlSequence get(char command, String code) {
Map<String, ControlSequence> codes = commands.getOrDefault(command, new HashMap<>()); Map<String, ControlSequence> codes = commands.getOrDefault(command, new HashMap<>());
return codes.getOrDefault(code, new NullControlSequence()); return codes.getOrDefault(code, new NullControlSequence());

View File

@ -115,13 +115,6 @@ public class LispTerminal {
return keyStroke; return keyStroke;
} }
private void doControlC() {
moveCursorToEndOfInput();
terminal.putCharacter('\n');
setOriginToCurrentPosition();
stop();
}
private synchronized void handleKey(KeyStroke keyStroke) { private synchronized void handleKey(KeyStroke keyStroke) {
doKey(keyStroke); doKey(keyStroke);
terminal.flush(); terminal.flush();
@ -142,10 +135,20 @@ public class LispTerminal {
} }
private synchronized void doControlCharacter(KeyStroke keyStroke) { private synchronized void doControlCharacter(KeyStroke keyStroke) {
if (keyStroke.getCharacter() == 'd') if (keyStroke.getCharacter() == 'c')
doControlC();
else if (keyStroke.getCharacter() == 'd')
doControlD(); doControlD();
} }
private void doControlC() {
moveCursorToEndOfInput();
terminal.putCharacter('\n');
inputLine = "";
setOriginToCurrentPosition();
stop();
}
private synchronized void doControlD() { private synchronized void doControlD() {
doEnter(); doEnter();
stop(); stop();
@ -374,7 +377,7 @@ public class LispTerminal {
} }
private synchronized void putOutputToTerminal() { private synchronized void putOutputToTerminal() {
SafeInputStream input = new SafeInputStream(new ByteArrayInputStream(outputSegment.getBytes())); SafeInputStream input = convertOutputToStream();
for (int c = input.read(); c != EOF; c = input.read()) for (int c = input.read(); c != EOF; c = input.read())
if (isEscape((char) c)) if (isEscape((char) c))
@ -383,6 +386,10 @@ public class LispTerminal {
terminal.putCharacter((char) c); terminal.putCharacter((char) c);
} }
private synchronized SafeInputStream convertOutputToStream() {
return new SafeInputStream(new ByteArrayInputStream(outputSegment.getBytes()));
}
private void applyControlSequence(SafeInputStream input) { private void applyControlSequence(SafeInputStream input) {
ControlSequence controlSequence = controlSequenceHandler.parse(input); ControlSequence controlSequence = controlSequenceHandler.parse(input);
controlSequence.applyTo(terminal); controlSequence.applyTo(terminal);

View File

@ -1,6 +1,6 @@
package token; package token;
import java.text.MessageFormat; import static java.text.MessageFormat.format;
import error.*; import error.*;
import file.FilePosition; import file.FilePosition;
@ -37,7 +37,7 @@ public interface TokenFactory {
@Override @Override
public String getMessagePrefix() { public String getMessagePrefix() {
return MessageFormat.format("illegal character >>{0}<<", text); return format("illegal character >>{0}<<", text);
} }
} }

View File

@ -152,10 +152,9 @@ public class ErrorManagerTest {
} }
@Test @Test
public void severityCoverage() { public void severityEnumCoverage() {
Severity.valueOf(WARNING.toString()); for (Severity severity : Severity.values())
Severity.valueOf(ERROR.toString()); Severity.valueOf(severity.toString());
Severity.valueOf(CRITICAL.toString());
} }
} }

View File

@ -1,10 +1,10 @@
package function.builtin; package function.builtin;
import static java.text.MessageFormat.format;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static testutil.TestUtilities.evaluateString; import static testutil.TestUtilities.evaluateString;
import java.io.*; import java.io.*;
import java.text.MessageFormat;
import org.junit.*; import org.junit.*;
@ -40,16 +40,16 @@ public class PRINTTest {
public void printStringWorks() { public void printStringWorks() {
String output = "\"Hello, world!\""; String output = "\"Hello, world!\"";
evaluateString(MessageFormat.format("(print {0})", output)); evaluateString(format("(print {0})", output));
assertPrinted(MessageFormat.format("{0}\n", output)); assertPrinted(format("{0}\n", output));
} }
@Test @Test
public void printSymbolWorks() { public void printSymbolWorks() {
String output = "A"; String output = "A";
evaluateString(MessageFormat.format("(print ''{0})", output)); evaluateString(format("(print ''{0})", output));
assertPrinted(MessageFormat.format("{0}\n", output)); assertPrinted(format("{0}\n", output));
} }
@Test(expected = TooManyArgumentsException.class) @Test(expected = TooManyArgumentsException.class)

View File

@ -1,7 +1,10 @@
package interpreter; package interpreter;
import static error.ErrorManager.Severity.CRITICAL; import static error.ErrorManager.Severity.CRITICAL;
import static interpreter.InteractiveLispInterpreter.PROMPT;
import static java.text.MessageFormat.format;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static testutil.TestUtilities.createInputStreamFromString;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
@ -11,7 +14,7 @@ import org.junit.*;
import environment.RuntimeEnvironment; import environment.RuntimeEnvironment;
import interpreter.LispInterpreterBuilderImpl.InterpreterAlreadyBuiltException; import interpreter.LispInterpreterBuilderImpl.InterpreterAlreadyBuiltException;
public class LispInterpreterBuilderTest { public class LispInterpreterTest {
private static final String TERMINATED = "terminated"; private static final String TERMINATED = "terminated";
@ -21,7 +24,7 @@ public class LispInterpreterBuilderTest {
private RuntimeEnvironment environment; private RuntimeEnvironment environment;
private LispInterpreterBuilder builder; private LispInterpreterBuilder builder;
public LispInterpreterBuilderTest() { public LispInterpreterTest() {
this.environment = RuntimeEnvironment.getInstance(); this.environment = RuntimeEnvironment.getInstance();
this.builder = new LispInterpreterBuilderImpl() { this.builder = new LispInterpreterBuilderImpl() {
@ -68,6 +71,7 @@ public class LispInterpreterBuilderTest {
setCommonFeatures(); setCommonFeatures();
builder.setInput(System.in, "stdin"); builder.setInput(System.in, "stdin");
LispInterpreter interpreter = builder.build(); LispInterpreter interpreter = builder.build();
assertTrue(interpreter instanceof InteractiveLispInterpreter); assertTrue(interpreter instanceof InteractiveLispInterpreter);
} }
@ -77,6 +81,7 @@ public class LispInterpreterBuilderTest {
builder.setInput(System.in, "stdin"); builder.setInput(System.in, "stdin");
builder.setNotInteractive(); builder.setNotInteractive();
LispInterpreter interpreter = builder.build(); LispInterpreter interpreter = builder.build();
assertFalse(interpreter instanceof InteractiveLispInterpreter); assertFalse(interpreter instanceof InteractiveLispInterpreter);
assertFalse(interpreter instanceof FileLispInterpreter); assertFalse(interpreter instanceof FileLispInterpreter);
} }
@ -86,6 +91,7 @@ public class LispInterpreterBuilderTest {
setCommonFeatures(); setCommonFeatures();
builder.useFile("test/interpreter/test-files/file.lisp"); builder.useFile("test/interpreter/test-files/file.lisp");
LispInterpreter interpreter = builder.build(); LispInterpreter interpreter = builder.build();
assertTrue(interpreter instanceof FileLispInterpreter); assertTrue(interpreter instanceof FileLispInterpreter);
} }
@ -98,6 +104,7 @@ public class LispInterpreterBuilderTest {
@Test @Test
public void interpreterAlreadyBuiltException_HasCorrectAttributes() { public void interpreterAlreadyBuiltException_HasCorrectAttributes() {
InterpreterAlreadyBuiltException e = new InterpreterAlreadyBuiltException(); InterpreterAlreadyBuiltException e = new InterpreterAlreadyBuiltException();
assertEquals(CRITICAL, e.getSeverity()); assertEquals(CRITICAL, e.getSeverity());
assertNotNull(e.getMessage()); assertNotNull(e.getMessage());
assertTrue(e.getMessage().length() > 0); assertTrue(e.getMessage().length() > 0);
@ -117,6 +124,7 @@ public class LispInterpreterBuilderTest {
setCommonFeatures(); setCommonFeatures();
builder.useFile("test/interpreter/test-files/does-not-exist.lisp"); builder.useFile("test/interpreter/test-files/does-not-exist.lisp");
builder.build(); builder.build();
assertErrorMessageWritten(); assertErrorMessageWritten();
assertTerminated(); assertTerminated();
} }
@ -124,6 +132,7 @@ public class LispInterpreterBuilderTest {
@Test @Test
public void makeSureDecoratorsAreInitializedWithDefaults() { public void makeSureDecoratorsAreInitializedWithDefaults() {
builder.build(); builder.build();
assertEquals("", environment.decoratePrompt("")); assertEquals("", environment.decoratePrompt(""));
assertEquals("", environment.decorateValueOutput("")); assertEquals("", environment.decorateValueOutput(""));
assertEquals("", environment.decorateWarningOutput("")); assertEquals("", environment.decorateWarningOutput(""));
@ -139,6 +148,7 @@ public class LispInterpreterBuilderTest {
builder.setErrorOutputDecorator(s -> "*" + s + "*"); builder.setErrorOutputDecorator(s -> "*" + s + "*");
builder.setCriticalOutputDecorator(s -> "$" + s + "$"); builder.setCriticalOutputDecorator(s -> "$" + s + "$");
builder.build(); builder.build();
assertEquals("#x#", environment.decoratePrompt("x")); assertEquals("#x#", environment.decoratePrompt("x"));
assertEquals("@x@", environment.decorateValueOutput("x")); assertEquals("@x@", environment.decorateValueOutput("x"));
assertEquals("%x%", environment.decorateWarningOutput("x")); assertEquals("%x%", environment.decorateWarningOutput("x"));
@ -146,4 +156,35 @@ public class LispInterpreterBuilderTest {
assertEquals("$x$", environment.decorateCriticalOutput("x")); assertEquals("$x$", environment.decorateCriticalOutput("x"));
} }
@Test
public void fileBasedInterpreterWorks() {
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\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());
}
} }

View File

@ -0,0 +1 @@
'pickle

View File

@ -0,0 +1,43 @@
package stream;
import static org.junit.Assert.assertEquals;
import static testutil.TestUtilities.*;
import org.junit.*;
public class SafeInputStreamTest {
SafeInputStream safe;
SafeInputStream safeWithException;
@Before
public void setUp() throws Exception {
safe = new SafeInputStream(createInputStreamFromString("a"));
safeWithException = new SafeInputStream(createIOExceptionThrowingInputStream());
}
@After
public void tearDown() throws Exception {}
@Test
public void readWorks() {
assertEquals('a', (char) safe.read());
assertEquals(-1, safe.read());
}
@Test
public void closeWorks() {
safe.close();
}
@Test(expected = UncheckedIOException.class)
public void readThrowsUncheckedException() {
safeWithException.read();
}
@Test(expected = UncheckedIOException.class)
public void closeThrowsUncheckedException() {
safeWithException.close();
}
}

View File

@ -0,0 +1,54 @@
package stream;
import static org.junit.Assert.assertEquals;
import static testutil.TestUtilities.createIOExceptionThrowingOutputStream;
import java.io.ByteArrayOutputStream;
import org.junit.*;
public class SafeOutputStreamTest {
SafeOutputStream safe;
SafeOutputStream safeWithException;
ByteArrayOutputStream output;
@Before
public void setUp() throws Exception {
output = new ByteArrayOutputStream();
safe = new SafeOutputStream(output);
safeWithException = new SafeOutputStream(createIOExceptionThrowingOutputStream());
}
@Test
public void writeWorks() {
safe.write("write".getBytes());
assertEquals("write", output.toString());
}
@Test
public void flushWorks() {
safe.flush();
}
@Test
public void closeWorks() {
safe.close();
}
@Test(expected = UncheckedIOException.class)
public void writeThrowsUncheckedException() {
safeWithException.write("write".getBytes());
}
@Test(expected = UncheckedIOException.class)
public void flushThrowsUncheckedException() {
safeWithException.flush();
}
@Test(expected = UncheckedIOException.class)
public void closeThrowsUncheckedException() {
safeWithException.close();
}
}

View File

@ -1,6 +1,6 @@
package terminal; package terminal;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.*;
import static terminal.SelectGraphicRendition.*; import static terminal.SelectGraphicRendition.*;
import java.util.*; import java.util.*;
@ -79,4 +79,16 @@ public class ControlSequenceTest {
assertTrue(indicatorSet.contains("MAGENTA")); assertTrue(indicatorSet.contains("MAGENTA"));
} }
@Test
public void nullControlSequenceHasCorrectCode() {
ControlSequence nullControlSequence = new NullControlSequence();
assertEquals("", nullControlSequence.getCode());
}
@Test
public void SelectGraphicRenditionEnumCoverage() {
for (SelectGraphicRendition sgr : SelectGraphicRendition.values())
SelectGraphicRendition.valueOf(sgr.toString());
}
} }

View File

@ -347,6 +347,17 @@ public class LispTerminalTest {
assertInputWritten("control-d\n"); assertInputWritten("control-d\n");
} }
@Test
public void controlCWorks() {
enterCharacters("ctrl-c");
enterControlCharacter('c');
produceOutput("");
assertInputStreamClosed();
assertInputWritten("");
assertCharacterPositions(new char[][] { { 'c', 't', 'r', 'l', '-', 'c', ' ', ' ', ' ' },
{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' } });
}
@Test @Test
public void controlDWorksInMiddleOfInput() { public void controlDWorksInMiddleOfInput() {
enterCharacters("control-d"); enterCharacters("control-d");

View File

@ -23,7 +23,37 @@ public final class TestUtilities {
@Override @Override
public int read() throws IOException { public int read() throws IOException {
throw new IOException("test IOException"); throw new IOException("read()");
}
@Override
public void close() throws IOException {
throw new IOException("close()");
}
};
}
public static OutputStream createIOExceptionThrowingOutputStream() {
return new OutputStream() {
@Override
public void write(byte[] b) throws IOException {
throw new IOException("write(byte[])");
}
@Override
public void flush() throws IOException {
throw new IOException("flush()");
}
@Override
public void close() throws IOException {
throw new IOException("close()");
}
@Override
public void write(int arg0) throws IOException {
throw new IOException("write(int)");
} }
}; };
} }