ConsoleWatcher.java
package net.morimekta.testing.junit4;
import net.morimekta.io.tty.TTY;
import net.morimekta.io.tty.TTYSize;
import net.morimekta.testing.console.Console;
import net.morimekta.testing.console.ConsoleManager;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
/**
* Printed output watcher rule.
*
* <pre>{@code
* public class Test {
* {@literal@}Rule
* public ConsoleWatcher watcher = new ConsoleWatcher();
*
* {@literal@}Test
* public void testOutput() {
* System.err.println("woot!");
*
* assertThat(console.console().error(), is("woot!\n"));
* }
* }
* }</pre>
* <p>
* Note that this watcher hijacks the default {@link System#in},
* {@link System#out} and {@link System#err} streams. This may cause problems
* when other systems prints out to those streams and expects the output to
* be present for others. In some cases maven may balk at this, but it seems
* to pass silently (and successfully) in other cases.
*/
public class ConsoleWatcher
extends TestWatcher {
private final ConsoleManager manager = new ConsoleManager();
private boolean started = false;
private TTYSize defaultTerminalSize = Console.DEFAULT_TERMINAL_SIZE;
private boolean defaultInteractive = false;
private boolean defaultDumpOutputOnFailure = false;
private boolean defaultDumpErrorOnFailure = false;
/**
* @return The testing TTY
*/
public TTY tty() {
return manager.getTTY();
}
/**
* @return The testing console.
*/
public Console console() {
return manager.getConsole();
}
/**
* Set the current terminal size.
*
* @param rows Row count.
* @param cols Column count.
* @return The console watcher.
*/
public ConsoleWatcher withTerminalSize(int rows, int cols) {
var terminalSize = new TTYSize(rows, cols);
if (started) {
manager.setTerminalSize(terminalSize);
} else {
defaultTerminalSize = terminalSize;
}
return this;
}
/**
* Set input mode to non-interactive. This makes the terminal no longer
* behave like an interactive terminal (the default for ConsoleWatcher),
* but as a wrapped shell script.
*
* @return The console watcher.
*/
public ConsoleWatcher nonInteractive() {
if (started) {
manager.setInteractive(false);
} else {
defaultInteractive = false;
}
return this;
}
/**
* Set input mode to interactive. This makes the terminal behave like an
* interactive terminal (the default for ConsoleWatcher).
*
* @return The console watcher.
*/
public ConsoleWatcher interactive() {
if (started) {
manager.setInteractive(true);
} else {
defaultInteractive = true;
}
return this;
}
/**
* Dump stdout to error output on failure.
*
* @return The console watcher.
*/
public ConsoleWatcher dumpOutputOnFailure() {
if (started) {
manager.setDumpOutputOnFailure(true);
manager.setForkOutput(false);
} else {
defaultDumpOutputOnFailure = true;
}
return this;
}
/**
* Dump stderr to error output on failure.
*
* @return The console watcher.
*/
public ConsoleWatcher dumpErrorOnFailure() {
if (started) {
manager.setDumpErrorOnFailure(true);
manager.setForkError(false);
} else {
defaultDumpErrorOnFailure = true;
}
return this;
}
/**
* Dump both stdout and stderr to error output on failure.
*
* @return The console watcher.
*/
public ConsoleWatcher dumpOnFailure() {
dumpOutputOnFailure();
dumpErrorOnFailure();
return this;
}
@Override
protected void starting(Description description) {
started = true;
manager.setTerminalSize(defaultTerminalSize);
manager.setInteractive(defaultInteractive);
manager.setDumpErrorOnFailure(defaultDumpErrorOnFailure);
manager.setForkError(!defaultDumpErrorOnFailure);
manager.setDumpOutputOnFailure(defaultDumpOutputOnFailure);
manager.setForkOutput(!defaultDumpOutputOnFailure);
manager.doBeforeEach();
}
@Override
protected void failed(Throwable e, Description description) {
manager.onTestFailed(description.getMethodName());
}
@Override
protected void finished(Description description) {
manager.doAfterEach();
}
}