mirror of
https://github.com/fankes/termux-app.git
synced 2025-09-08 11:34:07 +08:00
Restructure library modules
terminal/ -> terminal-emulator/ view/ -> terminal-view/
This commit is contained in:
@@ -0,0 +1,310 @@
|
||||
package com.termux.terminal;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class TerminalTestCase extends TestCase {
|
||||
|
||||
public static class MockTerminalOutput extends TerminalOutput {
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
public final List<ChangedTitle> titleChanges = new ArrayList<>();
|
||||
public final List<String> clipboardPuts = new ArrayList<>();
|
||||
public int bellsRung = 0;
|
||||
public int colorsChanged = 0;
|
||||
|
||||
@Override
|
||||
public void write(byte[] data, int offset, int count) {
|
||||
baos.write(data, offset, count);
|
||||
}
|
||||
|
||||
public String getOutputAndClear() {
|
||||
try {
|
||||
String result = new String(baos.toByteArray(), "UTF-8");
|
||||
baos.reset();
|
||||
return result;
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void titleChanged(String oldTitle, String newTitle) {
|
||||
titleChanges.add(new ChangedTitle(oldTitle, newTitle));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clipboardText(String text) {
|
||||
clipboardPuts.add(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBell() {
|
||||
bellsRung++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onColorsChanged() {
|
||||
colorsChanged++;
|
||||
}
|
||||
}
|
||||
|
||||
public TerminalEmulator mTerminal;
|
||||
public MockTerminalOutput mOutput;
|
||||
|
||||
public static final class ChangedTitle {
|
||||
final String oldTitle;
|
||||
final String newTitle;
|
||||
|
||||
public ChangedTitle(String oldTitle, String newTitle) {
|
||||
this.oldTitle = oldTitle;
|
||||
this.newTitle = newTitle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof ChangedTitle)) return false;
|
||||
ChangedTitle other = (ChangedTitle) o;
|
||||
return Objects.equals(oldTitle, other.oldTitle) && Objects.equals(newTitle, other.newTitle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(oldTitle, newTitle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ChangedTitle[oldTitle=" + oldTitle + ", newTitle=" + newTitle + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public TerminalTestCase enterString(String s) {
|
||||
byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
|
||||
mTerminal.append(bytes, bytes.length);
|
||||
assertInvariants();
|
||||
return this;
|
||||
}
|
||||
|
||||
public void assertEnteringStringGivesResponse(String input, String expectedResponse) {
|
||||
enterString(input);
|
||||
String response = mOutput.getOutputAndClear();
|
||||
assertEquals(expectedResponse, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
mOutput = new MockTerminalOutput();
|
||||
}
|
||||
|
||||
protected TerminalTestCase withTerminalSized(int columns, int rows) {
|
||||
mTerminal = new TerminalEmulator(mOutput, columns, rows, rows * 2);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void assertHistoryStartsWith(String... rows) {
|
||||
assertTrue("About to check " + rows.length + " lines, but only " + mTerminal.getScreen().getActiveTranscriptRows() + " in history",
|
||||
mTerminal.getScreen().getActiveTranscriptRows() >= rows.length);
|
||||
for (int i = 0; i < rows.length; i++) {
|
||||
assertLineIs(-i - 1, rows[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class LineWrapper {
|
||||
final TerminalRow mLine;
|
||||
|
||||
public LineWrapper(TerminalRow line) {
|
||||
mLine = line;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return System.identityHashCode(mLine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof LineWrapper && ((LineWrapper) o).mLine == mLine;
|
||||
}
|
||||
}
|
||||
|
||||
protected TerminalTestCase assertInvariants() {
|
||||
TerminalBuffer screen = mTerminal.getScreen();
|
||||
TerminalRow[] lines = screen.mLines;
|
||||
|
||||
Set<LineWrapper> linesSet = new HashSet<>();
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
if (lines[i] == null) continue;
|
||||
assertTrue("Line exists at multiple places: " + i, linesSet.add(new LineWrapper(lines[i])));
|
||||
char[] text = lines[i].mText;
|
||||
int usedChars = lines[i].getSpaceUsed();
|
||||
int currentColumn = 0;
|
||||
for (int j = 0; j < usedChars; j++) {
|
||||
char c = text[j];
|
||||
int codePoint;
|
||||
if (Character.isHighSurrogate(c)) {
|
||||
char lowSurrogate = text[++j];
|
||||
assertTrue("High surrogate without following low surrogate", Character.isLowSurrogate(lowSurrogate));
|
||||
codePoint = Character.toCodePoint(c, lowSurrogate);
|
||||
} else {
|
||||
assertFalse("Low surrogate without preceding high surrogate", Character.isLowSurrogate(c));
|
||||
codePoint = c;
|
||||
}
|
||||
assertFalse("Screen should never contain unassigned characters", Character.getType(codePoint) == Character.UNASSIGNED);
|
||||
int width = WcWidth.width(codePoint);
|
||||
assertFalse("The first column should not start with combining character", currentColumn == 0 && width < 0);
|
||||
if (width > 0) currentColumn += width;
|
||||
}
|
||||
assertEquals("Line whose width does not match screens. line=" + new String(lines[i].mText, 0, lines[i].getSpaceUsed()),
|
||||
screen.mColumns, currentColumn);
|
||||
}
|
||||
|
||||
assertEquals("The alt buffer should have have no history", mTerminal.mAltBuffer.mTotalRows, mTerminal.mAltBuffer.mScreenRows);
|
||||
if (mTerminal.isAlternateBufferActive()) {
|
||||
assertEquals("The alt buffer should be the same size as the screen", mTerminal.mRows, mTerminal.mAltBuffer.mTotalRows);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
protected void assertLineIs(int line, String expected) {
|
||||
TerminalRow l = mTerminal.getScreen().allocateFullLineIfNecessary(mTerminal.getScreen().externalToInternalRow(line));
|
||||
char[] chars = l.mText;
|
||||
int textLen = l.getSpaceUsed();
|
||||
if (textLen != expected.length()) fail("Expected '" + expected + "' (len=" + expected.length() + "), was='"
|
||||
+ new String(chars, 0, textLen) + "' (len=" + textLen + ")");
|
||||
for (int i = 0; i < textLen; i++) {
|
||||
if (expected.charAt(i) != chars[i])
|
||||
fail("Expected '" + expected + "', was='" + new String(chars, 0, textLen) + "' - first different at index=" + i);
|
||||
}
|
||||
}
|
||||
|
||||
public TerminalTestCase assertLinesAre(String... lines) {
|
||||
assertEquals(lines.length, mTerminal.getScreen().mScreenRows);
|
||||
for (int i = 0; i < lines.length; i++)
|
||||
try {
|
||||
assertLineIs(i, lines[i]);
|
||||
} catch (AssertionFailedError e) {
|
||||
throw new AssertionFailedError("Line: " + i + " - " + e.getMessage());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public TerminalTestCase resize(int cols, int rows) {
|
||||
mTerminal.resize(cols, rows);
|
||||
assertInvariants();
|
||||
return this;
|
||||
}
|
||||
|
||||
public TerminalTestCase assertLineWraps(boolean... lines) {
|
||||
for (int i = 0; i < lines.length; i++)
|
||||
assertEquals("line=" + i, lines[i], mTerminal.getScreen().mLines[mTerminal.getScreen().externalToInternalRow(i)].mLineWrap);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected TerminalTestCase assertLineStartsWith(int line, int... codePoints) {
|
||||
char[] chars = mTerminal.getScreen().mLines[mTerminal.getScreen().externalToInternalRow(line)].mText;
|
||||
int charIndex = 0;
|
||||
for (int i = 0; i < codePoints.length; i++) {
|
||||
int lineCodePoint = chars[charIndex++];
|
||||
if (Character.isHighSurrogate((char) lineCodePoint)) {
|
||||
lineCodePoint = Character.toCodePoint((char) lineCodePoint, chars[charIndex++]);
|
||||
}
|
||||
assertEquals("Differing a code point index=" + i, codePoints[i], lineCodePoint);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
protected TerminalTestCase placeCursorAndAssert(int row, int col) {
|
||||
// +1 due to escape sequence being one based.
|
||||
enterString("\033[" + (row + 1) + ";" + (col + 1) + "H");
|
||||
assertCursorAt(row, col);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TerminalTestCase assertCursorAt(int row, int col) {
|
||||
int actualRow = mTerminal.getCursorRow();
|
||||
int actualCol = mTerminal.getCursorCol();
|
||||
if (!(row == actualRow && col == actualCol))
|
||||
fail("Expected cursor at (row,col)=(" + row + ", " + col + ") but was (" + actualRow + ", " + actualCol + ")");
|
||||
return this;
|
||||
}
|
||||
|
||||
/** For testing only. Encoded style according to {@link TextStyle}. */
|
||||
public long getStyleAt(int externalRow, int column) {
|
||||
return mTerminal.getScreen().getStyleAt(externalRow, column);
|
||||
}
|
||||
|
||||
public static class EffectLine {
|
||||
final int[] styles;
|
||||
|
||||
public EffectLine(int[] styles) {
|
||||
this.styles = styles;
|
||||
}
|
||||
}
|
||||
|
||||
protected EffectLine effectLine(int... bits) {
|
||||
return new EffectLine(bits);
|
||||
}
|
||||
|
||||
public TerminalTestCase assertEffectAttributesSet(EffectLine... lines) {
|
||||
assertEquals(lines.length, mTerminal.getScreen().mScreenRows);
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
int[] line = lines[i].styles;
|
||||
for (int j = 0; j < line.length; j++) {
|
||||
int effectsAtCell = TextStyle.decodeEffect(getStyleAt(i, j));
|
||||
int attributes = line[j];
|
||||
if ((effectsAtCell & attributes) != attributes) fail("Line=" + i + ", column=" + j + ", expected "
|
||||
+ describeStyle(attributes) + " set, was " + describeStyle(effectsAtCell));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public TerminalTestCase assertForegroundIndices(EffectLine... lines) {
|
||||
assertEquals(lines.length, mTerminal.getScreen().mScreenRows);
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
int[] line = lines[i].styles;
|
||||
for (int j = 0; j < line.length; j++) {
|
||||
int actualColor = TextStyle.decodeForeColor(getStyleAt(i, j));
|
||||
int expectedColor = line[j];
|
||||
if (actualColor != expectedColor) fail("Line=" + i + ", column=" + j + ", expected color "
|
||||
+ Integer.toHexString(expectedColor) + " set, was " + Integer.toHexString(actualColor));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private static String describeStyle(int styleBits) {
|
||||
return "'" + ((styleBits & TextStyle.CHARACTER_ATTRIBUTE_BLINK) != 0 ? ":BLINK:" : "")
|
||||
+ ((styleBits & TextStyle.CHARACTER_ATTRIBUTE_BOLD) != 0 ? ":BOLD:" : "")
|
||||
+ ((styleBits & TextStyle.CHARACTER_ATTRIBUTE_INVERSE) != 0 ? ":INVERSE:" : "")
|
||||
+ ((styleBits & TextStyle.CHARACTER_ATTRIBUTE_INVISIBLE) != 0 ? ":INVISIBLE:" : "")
|
||||
+ ((styleBits & TextStyle.CHARACTER_ATTRIBUTE_ITALIC) != 0 ? ":ITALIC:" : "")
|
||||
+ ((styleBits & TextStyle.CHARACTER_ATTRIBUTE_PROTECTED) != 0 ? ":PROTECTED:" : "")
|
||||
+ ((styleBits & TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH) != 0 ? ":STRIKETHROUGH:" : "")
|
||||
+ ((styleBits & TextStyle.CHARACTER_ATTRIBUTE_UNDERLINE) != 0 ? ":UNDERLINE:" : "") + "'";
|
||||
}
|
||||
|
||||
public void assertForegroundColorAt(int externalRow, int column, int color) {
|
||||
long style = mTerminal.getScreen().mLines[mTerminal.getScreen().externalToInternalRow(externalRow)].getStyle(column);
|
||||
assertEquals(color, TextStyle.decodeForeColor(style));
|
||||
}
|
||||
|
||||
public TerminalTestCase assertColor(int colorIndex, int expected) {
|
||||
int actual = mTerminal.mColors.mCurrentColors[colorIndex];
|
||||
if (expected != actual) {
|
||||
fail("Color index=" + colorIndex + ", expected=" + Integer.toHexString(expected) + ", was=" + Integer.toHexString(actual));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user