ConsoleExtension.java
package net.morimekta.testing.junit5;
import net.morimekta.io.tty.TTY;
import net.morimekta.io.tty.TTYMode;
import net.morimekta.io.tty.TTYSize;
import net.morimekta.testing.console.Console;
import net.morimekta.testing.console.ConsoleManager;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;
import org.junit.jupiter.api.extension.TestWatcher;
import static net.morimekta.testing.console.Console.DEFAULT_TERMINAL_SIZE;
import static net.morimekta.testing.junit5.AnnotationUtil.getTopAnnotation;
import static net.morimekta.testing.junit5.AnnotationUtil.isAnnotationPresent;
/**
* Extension for adding a fully virtual TTY and I/O for testing. This will forcefully replace standard in, out and err
* while the test is running, falling back to default (system streams) when completed. This means any test that uses
* normal system I/O to print ongoing status will not work with this extension.
*
* <pre>{@code
* {@literal@}ExtendWith(ConsoleExtension.class)
* public class MyTest {
* {@literal@}Test
* public void testMyThing(Console console) {
* // use the console I/O or TTY or both.
* }
* }
* }</pre>
*/
public class ConsoleExtension extends ConsoleManager
implements BeforeEachCallback, AfterEachCallback, TestWatcher, ParameterResolver {
/**
* Instantiate the extension.
*/
public ConsoleExtension() {}
@Override
public void beforeEach(ExtensionContext context) {
setTerminalSize(getTopAnnotation(context, ConsoleSize.class)
.map(a -> new TTYSize(a.rows(), a.cols()))
.orElse(DEFAULT_TERMINAL_SIZE));
setInteractive(getTopAnnotation(context, ConsoleInteractive.class)
.map(ConsoleInteractive::value)
.orElse(true));
setTTYMode(getTopAnnotation(context, ConsoleMode.class)
.map(ConsoleMode::value)
.orElse(TTYMode.COOKED));
setDumpOutputOnFailure(isAnnotationPresent(context, ConsoleDumpOutputOnFailure.class));
setForkOutput(!isAnnotationPresent(context, ConsoleDumpOutputOnFailure.class));
setDumpErrorOnFailure(isAnnotationPresent(context, ConsoleDumpErrorOnFailure.class));
setForkError(!isAnnotationPresent(context, ConsoleDumpErrorOnFailure.class));
doBeforeEach();
}
@Override
public void testFailed(ExtensionContext context, Throwable cause) {
super.onTestFailed(context.getDisplayName());
}
@Override
public void afterEach(ExtensionContext context) {
super.doAfterEach();
}
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
Class<?> type = parameterContext.getParameter().getType();
return type.equals(Console.class) || type.equals(TTY.class);
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
Class<?> type = parameterContext.getParameter().getType();
if (type.equals(Console.class)) {
return getConsole();
}
if (type.equals(TTY.class)) {
return getTTY();
}
throw new ParameterResolutionException("No Console param for type " + type.getName());
}
}