1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-21 21:52:10 +02:00

Bug 573108: Add test harness for VT100Emulator

This contains basic tests so far, as bugs are fixed other tests
can be added here, leading to easier development of escape sequences.

Change-Id: Iadd0b24d400f72927c446d61386f19cfd8bd05a0
This commit is contained in:
Jonah Graham 2021-04-22 21:19:25 -04:00
parent 327026010b
commit a14c962ff2
4 changed files with 291 additions and 1 deletions

View file

@ -55,6 +55,16 @@ public final class Logger {
private static PrintStream logStream;
private static StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
private static boolean underTest = false;
/**
* When underTest we want exception that are deep inside the code to be surfaced to the test
* @noreference This method is not intended to be referenced by clients.
*/
public static void setUnderTest(boolean underTest) {
Logger.underTest = underTest;
}
static {
// Any of the known debugging options turns on the creation of the log file
boolean createLogFile = TerminalPlugin.isOptionEnabled(TRACE_DEBUG_LOG)
@ -187,6 +197,9 @@ public final class Logger {
* Writes an exception to the Terminal log and the Eclipse log
*/
public static final void logException(Exception ex) {
if (underTest) {
throw new RuntimeException("Terminal Under Test - examine cause for real failure", ex); //$NON-NLS-1$
}
logStatus(new Status(IStatus.ERROR, TerminalPlugin.PLUGIN_ID, IStatus.OK, ex.getMessage(), ex));
}

View file

@ -8,7 +8,9 @@ Bundle-Localization: plugin
Require-Bundle: org.junit,
org.eclipse.tm.terminal.control;bundle-version="4.5.0",
org.eclipse.core.runtime,
org.eclipse.ui
org.eclipse.ui,
org.junit.jupiter.api,
org.opentest4j
Bundle-RequiredExecutionEnvironment: JavaSE-11
Export-Package: org.eclipse.tm.internal.terminal.connector;x-internal:=true,
org.eclipse.tm.internal.terminal.emulator;x-internal:=true,

View file

@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright (c) 2021 Kichwa Coders Canada Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.tm.internal.terminal.emulator;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.tm.internal.terminal.control.impl.ITerminalControlForText;
import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector;
import org.eclipse.tm.internal.terminal.provisional.api.TerminalState;
public class MockTerminalControlForText implements ITerminalControlForText {
private List<String> allTitles = new ArrayList<>();
@Override
public TerminalState getState() {
throw new UnsupportedOperationException();
}
@Override
public void setState(TerminalState state) {
throw new UnsupportedOperationException();
}
@Override
public void setTerminalTitle(String title) {
allTitles.add(title);
}
public List<String> getAllTitles() {
return Collections.unmodifiableList(allTitles);
}
@Override
public ITerminalConnector getTerminalConnector() {
return null;
}
@Override
public OutputStream getOutputStream() {
throw new UnsupportedOperationException();
}
@Override
public void enableApplicationCursorKeys(boolean enable) {
throw new UnsupportedOperationException();
}
}

View file

@ -0,0 +1,215 @@
/*******************************************************************************
* Copyright (c) 2021 Kichwa Coders Canada Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.tm.internal.terminal.emulator;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringJoiner;
import org.eclipse.tm.internal.terminal.provisional.api.Logger;
import org.eclipse.tm.terminal.model.ITerminalTextData;
import org.eclipse.tm.terminal.model.TerminalTextDataFactory;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class VT100EmulatorTest {
private static final int WINDOW_COLUMNS = 80;
private static final int WINDOW_LINES = 24;
private static final String CLEAR_CURSOR_TO_EOL = "\033[K";
private static final String CURSOR_POSITION_TOP_LEFT = "\033[H";
/**
* Set the cursor position to line/column. Note that this is the logical
* line and column, so 1, 1 is the top left.
*/
private static String CURSOR_POSITION(int line, int column) {
return "\033[" + line + ";" + column + "H";
}
@BeforeAll
public static void beforeAll() {
Logger.setUnderTest(true);
}
@AfterAll
public static void afterAll() {
Logger.setUnderTest(false);
}
private ITerminalTextData data;
private MockTerminalControlForText control = new MockTerminalControlForText();
private VT100Emulator emulator;
@BeforeEach
public void before() {
data = TerminalTextDataFactory.makeTerminalTextData();
emulator = new VT100Emulator(data, control, null);
emulator.resetState();
emulator.setDimensions(WINDOW_LINES, WINDOW_COLUMNS);
}
private Reader input(String... input) {
StringReader reader = new StringReader(String.join("", input));
emulator.setInputStreamReader(reader);
return reader;
}
private void run(String... input) {
Reader reader = input(input);
emulator.processText();
try {
assertEquals(-1, reader.read());
} catch (IOException e) {
throw new RuntimeException("Wrap exception so that run can be called in functions", e);
}
}
/**
* Convert the data's char arrays into a string that can be compared with
* an expected array of lines. Each line in the data has its \0 characters
* changed to spaces and then stripTrailing is run.
*
* @param expectedArray lines that are joined with \n before testing against actual
*/
private void assertTextEquals(String... expectedArray) {
int height = data.getHeight();
StringJoiner sj = new StringJoiner("\n");
for (int i = 0; i < height; i++) {
char[] chars = data.getChars(i);
String line = chars == null ? "" : new String(chars);
String lineCleanedup = line.replace('\0', ' ');
String stripTrailing = lineCleanedup.stripTrailing();
sj.add(stripTrailing);
}
String expected = String.join("\n", expectedArray).stripTrailing();
String actual = sj.toString().stripTrailing();
assertEquals(expected, actual);
}
private void assertTextEquals(List<String> expected) {
assertTextEquals(expected.toArray(String[]::new));
}
private void assertCursorLocation(int line, int column) {
assertAll(() -> assertEquals(line, data.getCursorLine(), "cursor line"),
() -> assertEquals(column, data.getCursorColumn(), "cursor column"));
}
/**
* This tests the test harness ({@link #assertTextEquals(String...)} as much as the code.
*/
@Test
public void testBasicOperaiion() {
assertAll(() -> assertCursorLocation(0, 0), () -> assertTextEquals(""));
run("Hello");
assertAll(() -> assertCursorLocation(0, 5), () -> assertTextEquals("Hello"));
emulator.clearTerminal();
assertAll(() -> assertCursorLocation(0, 0), () -> assertTextEquals(""));
// test multiline
emulator.clearTerminal();
run("Hello 1\r\nHello 2");
// test both ways of asserting multiple lines
assertAll(() -> assertCursorLocation(1, 7), //
() -> assertTextEquals("Hello 1\nHello 2"), //
() -> assertTextEquals("Hello 1", "Hello 2"));
// test with no carriage return
emulator.clearTerminal();
run("Hello 1\nHello 2");
assertTextEquals("Hello 1", " Hello 2");
// test \b backspace
emulator.clearTerminal();
run("Hello 1");
assertAll(() -> assertCursorLocation(0, 7), () -> assertTextEquals("Hello 1"));
run("\b\b");
assertAll(() -> assertCursorLocation(0, 5), () -> assertTextEquals("Hello 1"));
run(CLEAR_CURSOR_TO_EOL);
assertAll(() -> assertCursorLocation(0, 5), () -> assertTextEquals("Hello"));
}
@Test
public void testMultiline() {
List<String> expected = new ArrayList<>();
for (int i = 0; i < data.getHeight(); i++) {
String line = "Hello " + i;
expected.add(line);
run(line);
if (i != data.getHeight() - 1) {
run("\r\n");
}
}
assertTextEquals(expected);
// add the final newline and check that the first line has been scrolled away
run("\r\n");
expected.remove(0);
assertTextEquals(expected);
}
@Test
public void testScrollBack() {
data.setMaxHeight(1000);
List<String> expected = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
String line = "Hello " + i;
run(line + "\r\n");
expected.add(line);
}
expected.remove(0);
assertTextEquals(expected);
}
@Test
public void testCursorPosition() {
run(CURSOR_POSITION_TOP_LEFT);
assertAll(() -> assertCursorLocation(0, 0), () -> assertTextEquals(""));
run("Hello");
assertAll(() -> assertCursorLocation(0, 5), () -> assertTextEquals("Hello"));
run(CURSOR_POSITION_TOP_LEFT);
assertAll(() -> assertCursorLocation(0, 0), () -> assertTextEquals("Hello"));
run(CURSOR_POSITION(2, 2));
assertAll(() -> assertCursorLocation(1, 1), () -> assertTextEquals("Hello"));
emulator.clearTerminal();
data.setMaxHeight(1000);
List<String> expected = new ArrayList<>();
for (int i = 0; i < WINDOW_LINES; i++) {
String line = "Hello " + i;
run(line + "\r\n");
expected.add(line);
}
assertAll(() -> assertCursorLocation(WINDOW_LINES, 0), () -> assertTextEquals(expected));
run(CURSOR_POSITION_TOP_LEFT);
// because we added WINDOW_LINES number of lines, and ended it with a \r\n the first
// line we added is now in the scrollback, so the cursor is at line 1
assertAll(() -> assertCursorLocation(1, 0), () -> assertTextEquals(expected));
run("Bye \r\n");
expected.set(1, "Bye");
assertAll(() -> assertCursorLocation(2, 0), () -> assertTextEquals(expected));
run(CURSOR_POSITION_TOP_LEFT);
assertAll(() -> assertCursorLocation(1, 0), () -> assertTextEquals(expected));
run(CURSOR_POSITION(2, 2));
assertAll(() -> assertCursorLocation(2, 1), () -> assertTextEquals(expected));
}
}