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:
parent
327026010b
commit
a14c962ff2
4 changed files with 291 additions and 1 deletions
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue