package com.termux.terminal; import android.util.Base64; import java.util.ArrayList; import java.util.List; import java.util.Random; /** "ESC ]" is the Operating System Command. */ public class OperatingSystemControlTest extends TerminalTestCase { public void testSetTitle() throws Exception { List<ChangedTitle> expectedTitleChanges = new ArrayList<>(); withTerminalSized(10, 10); enterString("\033]0;Hello, world\007"); assertEquals("Hello, world", mTerminal.getTitle()); expectedTitleChanges.add(new ChangedTitle(null, "Hello, world")); assertEquals(expectedTitleChanges, mOutput.titleChanges); enterString("\033]0;Goodbye, world\007"); assertEquals("Goodbye, world", mTerminal.getTitle()); expectedTitleChanges.add(new ChangedTitle("Hello, world", "Goodbye, world")); assertEquals(expectedTitleChanges, mOutput.titleChanges); enterString("\033]0;Goodbye, \u00F1 world\007"); assertEquals("Goodbye, \uu00F1 world", mTerminal.getTitle()); expectedTitleChanges.add(new ChangedTitle("Goodbye, world", "Goodbye, \uu00F1 world")); assertEquals(expectedTitleChanges, mOutput.titleChanges); // 2 should work as well (0 sets both title and icon). enterString("\033]2;Updated\007"); assertEquals("Updated", mTerminal.getTitle()); expectedTitleChanges.add(new ChangedTitle("Goodbye, \uu00F1 world", "Updated")); assertEquals(expectedTitleChanges, mOutput.titleChanges); enterString("\033[22;0t"); enterString("\033]0;FIRST\007"); expectedTitleChanges.add(new ChangedTitle("Updated", "FIRST")); assertEquals("FIRST", mTerminal.getTitle()); assertEquals(expectedTitleChanges, mOutput.titleChanges); enterString("\033[22;0t"); enterString("\033]0;SECOND\007"); assertEquals("SECOND", mTerminal.getTitle()); expectedTitleChanges.add(new ChangedTitle("FIRST", "SECOND")); assertEquals(expectedTitleChanges, mOutput.titleChanges); enterString("\033[23;0t"); assertEquals("FIRST", mTerminal.getTitle()); expectedTitleChanges.add(new ChangedTitle("SECOND", "FIRST")); assertEquals(expectedTitleChanges, mOutput.titleChanges); enterString("\033[23;0t"); expectedTitleChanges.add(new ChangedTitle("FIRST", "Updated")); assertEquals(expectedTitleChanges, mOutput.titleChanges); enterString("\033[22;0t"); enterString("\033[22;0t"); enterString("\033[22;0t"); // Popping to same title should not cause changes. enterString("\033[23;0t"); enterString("\033[23;0t"); enterString("\033[23;0t"); assertEquals(expectedTitleChanges, mOutput.titleChanges); } public void testTitleStack() throws Exception { // echo -ne '\e]0;BEFORE\007' # set title // echo -ne '\e[22t' # push to stack // echo -ne '\e]0;AFTER\007' # set new title // echo -ne '\e[23t' # retrieve from stack withTerminalSized(10, 10); enterString("\033]0;InitialTitle\007"); assertEquals("InitialTitle", mTerminal.getTitle()); enterString("\033[22t"); assertEquals("InitialTitle", mTerminal.getTitle()); enterString("\033]0;UpdatedTitle\007"); assertEquals("UpdatedTitle", mTerminal.getTitle()); enterString("\033[23t"); assertEquals("InitialTitle", mTerminal.getTitle()); enterString("\033[23t\033[23t\033[23t"); assertEquals("InitialTitle", mTerminal.getTitle()); } public void testSetColor() throws Exception { // "OSC 4; $INDEX; $COLORSPEC BEL" => Change color $INDEX to the color specified by $COLORSPEC. withTerminalSized(4, 4).enterString("\033]4;5;#00FF00\007"); assertEquals(Integer.toHexString(0xFF00FF00), Integer.toHexString(mTerminal.mColors.mCurrentColors[5])); enterString("\033]4;5;#00FFAB\007"); assertEquals(mTerminal.mColors.mCurrentColors[5], 0xFF00FFAB); enterString("\033]4;255;#ABFFAB\007"); assertEquals(mTerminal.mColors.mCurrentColors[255], 0xFFABFFAB); // Two indexed colors at once: enterString("\033]4;7;#00FF00;8;#0000FF\007"); assertEquals(mTerminal.mColors.mCurrentColors[7], 0xFF00FF00); assertEquals(mTerminal.mColors.mCurrentColors[8], 0xFF0000FF); } void assertIndexColorsMatch(int[] expected) { for (int i = 0; i < 255; i++) assertEquals("index=" + i, expected[i], mTerminal.mColors.mCurrentColors[i]); } public void testResetColor() throws Exception { withTerminalSized(4, 4); int[] initialColors = new int[TextStyle.NUM_INDEXED_COLORS]; System.arraycopy(mTerminal.mColors.mCurrentColors, 0, initialColors, 0, initialColors.length); int[] expectedColors = new int[initialColors.length]; System.arraycopy(mTerminal.mColors.mCurrentColors, 0, expectedColors, 0, expectedColors.length); Random rand = new Random(); for (int endType = 0; endType < 3; endType++) { // Both BEL (7) and ST (ESC \) can end an OSC sequence. String ender = (endType == 0) ? "\007" : "\033\\"; for (int i = 0; i < 255; i++) { expectedColors[i] = 0xFF000000 + (rand.nextInt() & 0xFFFFFF); int r = (expectedColors[i] >> 16) & 0xFF; int g = (expectedColors[i] >> 8) & 0xFF; int b = expectedColors[i] & 0xFF; String rgbHex = String.format("%02x", r) + String.format("%02x", g) + String.format("%02x", b); enterString("\033]4;" + i + ";#" + rgbHex + ender); assertEquals(expectedColors[i], mTerminal.mColors.mCurrentColors[i]); } } enterString("\033]104;0\007"); expectedColors[0] = TerminalColors.COLOR_SCHEME.mDefaultColors[0]; assertIndexColorsMatch(expectedColors); enterString("\033]104;1;2\007"); expectedColors[1] = TerminalColors.COLOR_SCHEME.mDefaultColors[1]; expectedColors[2] = TerminalColors.COLOR_SCHEME.mDefaultColors[2]; assertIndexColorsMatch(expectedColors); enterString("\033]104\007"); // Reset all colors. assertIndexColorsMatch(TerminalColors.COLOR_SCHEME.mDefaultColors); } public void disabledTestSetClipboard() { // Cannot run this as a unit test since Base64 is a android.util class. enterString("\033]52;c;" + Base64.encodeToString("Hello, world".getBytes(), 0) + "\007"); } public void testResettingTerminalResetsColor() throws Exception { // "OSC 4; $INDEX; $COLORSPEC BEL" => Change color $INDEX to the color specified by $COLORSPEC. withTerminalSized(4, 4).enterString("\033]4;5;#00FF00\007"); enterString("\033]4;5;#00FFAB\007").assertColor(5, 0xFF00FFAB); enterString("\033]4;255;#ABFFAB\007").assertColor(255, 0xFFABFFAB); mTerminal.reset(); assertIndexColorsMatch(TerminalColors.COLOR_SCHEME.mDefaultColors); } public void testSettingDynamicColors() { // "${OSC}${DYNAMIC};${COLORSPEC}${BEL_OR_STRINGTERMINATOR}" => Change ${DYNAMIC} color to the color specified by $COLORSPEC where: // DYNAMIC=10: Text foreground color. // DYNAMIC=11: Text background color. // DYNAMIC=12: Text cursor color. withTerminalSized(3, 3).enterString("\033]10;#ABCD00\007").assertColor(TextStyle.COLOR_INDEX_FOREGROUND, 0xFFABCD00); enterString("\033]11;#0ABCD0\007").assertColor(TextStyle.COLOR_INDEX_BACKGROUND, 0xFF0ABCD0); enterString("\033]12;#00ABCD\007").assertColor(TextStyle.COLOR_INDEX_CURSOR, 0xFF00ABCD); // Two special colors at once // ("Each successive parameter changes the next color in the list. The value of P s tells the starting point in the list"): enterString("\033]10;#FF0000;#00FF00\007").assertColor(TextStyle.COLOR_INDEX_FOREGROUND, 0xFFFF0000); assertColor(TextStyle.COLOR_INDEX_BACKGROUND, 0xFF00FF00); // Three at once: enterString("\033]10;#0000FF;#00FF00;#FF0000\007").assertColor(TextStyle.COLOR_INDEX_FOREGROUND, 0xFF0000FF); assertColor(TextStyle.COLOR_INDEX_BACKGROUND, 0xFF00FF00).assertColor(TextStyle.COLOR_INDEX_CURSOR, 0xFFFF0000); // Without ending semicolon: enterString("\033]10;#FF0000\007").assertColor(TextStyle.COLOR_INDEX_FOREGROUND, 0xFFFF0000); // For background and cursor: enterString("\033]11;#FFFF00;\007").assertColor(TextStyle.COLOR_INDEX_BACKGROUND, 0xFFFFFF00); enterString("\033]12;#00FFFF;\007").assertColor(TextStyle.COLOR_INDEX_CURSOR, 0xFF00FFFF); // Using string terminator: String stringTerminator = "\033\\"; enterString("\033]10;#FF0000" + stringTerminator).assertColor(TextStyle.COLOR_INDEX_FOREGROUND, 0xFFFF0000); // For background and cursor: enterString("\033]11;#FFFF00;" + stringTerminator).assertColor(TextStyle.COLOR_INDEX_BACKGROUND, 0xFFFFFF00); enterString("\033]12;#00FFFF;" + stringTerminator).assertColor(TextStyle.COLOR_INDEX_CURSOR, 0xFF00FFFF); } public void testReportSpecialColors() { // "${OSC}${DYNAMIC};?${BEL}" => Terminal responds with the control sequence which would set the current color. // Both xterm and libvte (gnome-terminal and others) use the longest color representation, which means that // the response is "${OSC}rgb:RRRR/GGGG/BBBB" withTerminalSized(3, 3).enterString("\033]10;#ABCD00\007").assertColor(TextStyle.COLOR_INDEX_FOREGROUND, 0xFFABCD00); assertEnteringStringGivesResponse("\033]10;?\007", "\033]10;rgb:abab/cdcd/0000\007"); // Same as above but with string terminator. xterm uses the same string terminator in the response, which // e.g. script posted at http://superuser.com/questions/157563/programmatic-access-to-current-xterm-background-color // relies on: assertEnteringStringGivesResponse("\033]10;?\033\\", "\033]10;rgb:abab/cdcd/0000\033\\"); } }