diff options
Diffstat (limited to 'src/libvterm/t')
44 files changed, 4836 insertions, 0 deletions
diff --git a/src/libvterm/t/02parser.test b/src/libvterm/t/02parser.test new file mode 100644 index 0000000..2cc51dc --- /dev/null +++ b/src/libvterm/t/02parser.test @@ -0,0 +1,266 @@ +INIT +UTF8 0 +WANTPARSER + +!Basic text +PUSH "hello" + text 0x68, 0x65, 0x6c, 0x6c, 0x6f + +!C0 +PUSH "\x03" + control 3 + +PUSH "\x1f" + control 0x1f + +!C1 8bit +PUSH "\x83" + control 0x83 + +PUSH "\x99" + control 0x99 + +!C1 7bit +PUSH "\e\x43" + control 0x83 + +PUSH "\e\x59" + control 0x99 + +!High bytes +PUSH "\xa0\xcc\xfe" + text 0xa0, 0xcc, 0xfe + +!Mixed +PUSH "1\n2" + text 0x31 + control 10 + text 0x32 + +!Escape +PUSH "\e=" + escape "=" + +!Escape 2-byte +PUSH "\e(X" + escape "(X" + +!Split write Escape +PUSH "\e(" +PUSH "Y" + escape "(Y" + +!Escape cancels Escape, starts another +PUSH "\e(\e)Z" + escape ")Z" + +!CAN cancels Escape, returns to normal mode +PUSH "\e(\x{18}AB" + text 0x41, 0x42 + +!C0 in Escape interrupts and continues +PUSH "\e(\nX" + control 10 + escape "(X" + +!CSI 0 args +PUSH "\e[a" + csi 0x61 * + +!CSI 1 arg +PUSH "\e[9b" + csi 0x62 9 + +!CSI 2 args +PUSH "\e[3;4c" + csi 0x63 3,4 + +!CSI 1 arg 1 sub +PUSH "\e[1:2c" + csi 0x63 1+,2 + +!CSI many digits +PUSH "\e[678d" + csi 0x64 678 + +!CSI leading zero +PUSH "\e[007e" + csi 0x65 7 + +!CSI qmark +PUSH "\e[?2;7f" + csi 0x66 L=3f 2,7 + +!CSI greater +PUSH "\e[>c" + csi 0x63 L=3e * + +!CSI SP +PUSH "\e[12 q" + csi 0x71 12 I=20 + +!Mixed CSI +PUSH "A\e[8mB" + text 0x41 + csi 0x6d 8 + text 0x42 + +!Split write +PUSH "\e" +PUSH "[a" + csi 0x61 * +PUSH "foo\e[" + text 0x66, 0x6f, 0x6f +PUSH "4b" + csi 0x62 4 +PUSH "\e[12;" +PUSH "3c" + csi 0x63 12,3 + +!Escape cancels CSI, starts Escape +PUSH "\e[123\e9" + escape "9" + +!CAN cancels CSI, returns to normal mode +PUSH "\e[12\x{18}AB" + text 0x41, 0x42 + +!C0 in Escape interrupts and continues +PUSH "\e[12\n;3X" + control 10 + csi 0x58 12,3 + +!OSC BEL +PUSH "\e]1;Hello\x07" + osc [1 "Hello"] + +!OSC ST (7bit) +PUSH "\e]1;Hello\e\\" + osc [1 "Hello"] + +!OSC ST (8bit) +PUSH "\x{9d}1;Hello\x9c" + osc [1 "Hello"] + +!OSC in parts +PUSH "\e]52;abc" + osc [52 "abc" +PUSH "def" + osc "def" +PUSH "ghi\e\\" + osc "ghi"] + +!OSC BEL without semicolon +PUSH "\e]1234\x07" + osc [1234 ] + +!OSC ST without semicolon +PUSH "\e]1234\e\\" + osc [1234 ] + +!Escape cancels OSC, starts Escape +PUSH "\e]Something\e9" + escape "9" + +!CAN cancels OSC, returns to normal mode +PUSH "\e]12\x{18}AB" + text 0x41, 0x42 + +!C0 in OSC interrupts and continues +PUSH "\e]2;\nBye\x07" + osc [2 "" + control 10 + osc "Bye"] + +!DCS BEL +PUSH "\ePHello\x07" + dcs ["Hello"] + +!DCS ST (7bit) +PUSH "\ePHello\e\\" + dcs ["Hello"] + +!DCS ST (8bit) +PUSH "\x{90}Hello\x9c" + dcs ["Hello"] + +!Split write of 7bit ST +PUSH "\ePABC\e" + dcs ["ABC" +PUSH "\\" + dcs ] + +!Escape cancels DCS, starts Escape +PUSH "\ePSomething\e9" + escape "9" + +!CAN cancels DCS, returns to normal mode +PUSH "\eP12\x{18}AB" + text 0x41, 0x42 + +!C0 in OSC interrupts and continues +PUSH "\ePBy\ne\x07" + dcs ["By" + control 10 + dcs "e"] + +!APC BEL +PUSH "\e_Hello\x07" + apc ["Hello"] + +!APC ST (7bit) +PUSH "\e_Hello\e\\" + apc ["Hello"] + +!APC ST (8bit) +PUSH "\x{9f}Hello\x9c" + apc ["Hello"] + +!PM BEL +PUSH "\e^Hello\x07" + pm ["Hello"] + +!PM ST (7bit) +PUSH "\e^Hello\e\\" + pm ["Hello"] + +!PM ST (8bit) +PUSH "\x{9e}Hello\x9c" + pm ["Hello"] + +!SOS BEL +PUSH "\eXHello\x07" + sos ["Hello"] + +!SOS ST (7bit) +PUSH "\eXHello\e\\" + sos ["Hello"] + +!SOS ST (8bit) +PUSH "\x{98}Hello\x9c" + sos ["Hello"] + +!SOS can contain any C0 or C1 code +PUSH "\eXABC\x01DEF\e\\" + sos ["ABC\x01DEF"] +PUSH "\eXABC\x99DEF\e\\" + sos ["ABC\x{99}DEF"] + +!NUL ignored +PUSH "\x{00}" + +!NUL ignored within CSI +PUSH "\e[12\x{00}3m" + csi 0x6d 123 + +!DEL ignored +PUSH "\x{7f}" + +!DEL ignored within CSI +PUSH "\e[12\x{7f}3m" + csi 0x6d 123 + +!DEL inside text" +PUSH "AB\x{7f}C" + text 0x41,0x42 + text 0x43 diff --git a/src/libvterm/t/03encoding_utf8.test b/src/libvterm/t/03encoding_utf8.test new file mode 100644 index 0000000..7ee16ac --- /dev/null +++ b/src/libvterm/t/03encoding_utf8.test @@ -0,0 +1,122 @@ +INIT +WANTENCODING + +!Low +ENCIN "123" + encout 0x31,0x32,0x33 + +# We want to prove the UTF-8 parser correctly handles all the sequences. +# Easy way to do this is to check it does low/high boundary cases, as that +# leaves only two for each sequence length +# +# These ranges are therefore: +# +# Two bytes: +# U+0080 = 000 10000000 => 00010 000000 +# => 11000010 10000000 = C2 80 +# U+07FF = 111 11111111 => 11111 111111 +# => 11011111 10111111 = DF BF +# +# Three bytes: +# U+0800 = 00001000 00000000 => 0000 100000 000000 +# => 11100000 10100000 10000000 = E0 A0 80 +# U+FFFD = 11111111 11111101 => 1111 111111 111101 +# => 11101111 10111111 10111101 = EF BF BD +# (We avoid U+FFFE and U+FFFF as they're invalid codepoints) +# +# Four bytes: +# U+10000 = 00001 00000000 00000000 => 000 010000 000000 000000 +# => 11110000 10010000 10000000 10000000 = F0 90 80 80 +# U+1FFFFF = 11111 11111111 11111111 => 111 111111 111111 111111 +# => 11110111 10111111 10111111 10111111 = F7 BF BF BF + +!2 byte +ENCIN "\xC2\x80\xDF\xBF" + encout 0x0080, 0x07FF + +!3 byte +ENCIN "\xE0\xA0\x80\xEF\xBF\xBD" + encout 0x0800,0xFFFD + +!4 byte +ENCIN "\xF0\x90\x80\x80\xF7\xBF\xBF\xBF" + encout 0x10000,0x1fffff + +# Next up, we check some invalid sequences +# + Early termination (back to low bytes too soon) +# + Early restart (another sequence introduction before the previous one was finished) + +!Early termination +ENCIN "\xC2!" + encout 0xfffd,0x21 + +ENCIN "\xE0!\xE0\xA0!" + encout 0xfffd,0x21,0xfffd,0x21 + +ENCIN "\xF0!\xF0\x90!\xF0\x90\x80!" + encout 0xfffd,0x21,0xfffd,0x21,0xfffd,0x21 + +!Early restart +ENCIN "\xC2\xC2\x90" + encout 0xfffd,0x0090 + +ENCIN "\xE0\xC2\x90\xE0\xA0\xC2\x90" + encout 0xfffd,0x0090,0xfffd,0x0090 + +ENCIN "\xF0\xC2\x90\xF0\x90\xC2\x90\xF0\x90\x80\xC2\x90" + encout 0xfffd,0x0090,0xfffd,0x0090,0xfffd,0x0090 + +# Test the overlong sequences by giving an overlong encoding of U+0000 and +# an encoding of the highest codepoint still too short +# +# Two bytes: +# U+0000 = C0 80 +# U+007F = 000 01111111 => 00001 111111 => +# => 11000001 10111111 => C1 BF +# +# Three bytes: +# U+0000 = E0 80 80 +# U+07FF = 00000111 11111111 => 0000 011111 111111 +# => 11100000 10011111 10111111 = E0 9F BF +# +# Four bytes: +# U+0000 = F0 80 80 80 +# U+FFFF = 11111111 11111111 => 000 001111 111111 111111 +# => 11110000 10001111 10111111 10111111 = F0 8F BF BF + +!Overlong +ENCIN "\xC0\x80\xC1\xBF" + encout 0xfffd,0xfffd + +ENCIN "\xE0\x80\x80\xE0\x9F\xBF" + encout 0xfffd,0xfffd + +ENCIN "\xF0\x80\x80\x80\xF0\x8F\xBF\xBF" + encout 0xfffd,0xfffd + +# UTF-16 surrogates U+D800 and U+DFFF +!UTF-16 Surrogates +ENCIN "\xED\xA0\x80\xED\xBF\xBF" + encout 0xfffd,0xfffd + +!Split write +ENCIN "\xC2" +ENCIN "\xA0" + encout 0x000A0 + +ENCIN "\xE0" +ENCIN "\xA0\x80" + encout 0x00800 +ENCIN "\xE0\xA0" +ENCIN "\x80" + encout 0x00800 + +ENCIN "\xF0" +ENCIN "\x90\x80\x80" + encout 0x10000 +ENCIN "\xF0\x90" +ENCIN "\x80\x80" + encout 0x10000 +ENCIN "\xF0\x90\x80" +ENCIN "\x80" + encout 0x10000 diff --git a/src/libvterm/t/10state_putglyph.test b/src/libvterm/t/10state_putglyph.test new file mode 100644 index 0000000..c82c525 --- /dev/null +++ b/src/libvterm/t/10state_putglyph.test @@ -0,0 +1,74 @@ +INIT +UTF8 1 +WANTSTATE g + +!Low +RESET +PUSH "ABC" + putglyph 0x41 1 0,0 + putglyph 0x42 1 0,1 + putglyph 0x43 1 0,2 + +!UTF-8 1 char +# U+00C1 = 0xC3 0x81 name: LATIN CAPITAL LETTER A WITH ACUTE +# U+00E9 = 0xC3 0xA9 name: LATIN SMALL LETTER E WITH ACUTE +RESET +PUSH "\xC3\x81\xC3\xA9" + putglyph 0xc1 1 0,0 + putglyph 0xe9 1 0,1 + +!UTF-8 split writes +RESET +PUSH "\xC3" +PUSH "\x81" + putglyph 0xc1 1 0,0 + +!UTF-8 wide char +# U+FF10 = 0xEF 0xBC 0x90 name: FULLWIDTH DIGIT ZERO +RESET +PUSH "\xEF\xBC\x90 " + putglyph 0xff10 2 0,0 + putglyph 0x20 1 0,2 + +!UTF-8 emoji wide char +# U+1F600 = 0xF0 0x9F 0x98 0x80 name: GRINNING FACE +RESET +PUSH "\xF0\x9F\x98\x80 " + putglyph 0x1f600 2 0,0 + putglyph 0x20 1 0,2 + +!UTF-8 combining chars +# U+0301 = 0xCC 0x81 name: COMBINING ACUTE +RESET +PUSH "e\xCC\x81Z" + putglyph 0x65,0x301 1 0,0 + putglyph 0x5a 1 0,1 + +!Combining across buffers +RESET +PUSH "e" + putglyph 0x65 1 0,0 +PUSH "\xCC\x81Z" + putglyph 0x65,0x301 1 0,0 + putglyph 0x5a 1 0,1 + +!Spare combining chars get truncated +RESET +PUSH "e" . "\xCC\x81" x 10 + putglyph 0x65,0x301,0x301,0x301,0x301,0x301 1 0,0 + # and nothing more + +RESET +PUSH "e" + putglyph 0x65 1 0,0 +PUSH "\xCC\x81" + putglyph 0x65,0x301 1 0,0 +PUSH "\xCC\x82" + putglyph 0x65,0x301,0x302 1 0,0 + +!DECSCA protected +RESET +PUSH "A\e[1\"qB\e[2\"qC" + putglyph 0x41 1 0,0 + putglyph 0x42 1 0,1 prot + putglyph 0x43 1 0,2 diff --git a/src/libvterm/t/11state_movecursor.test b/src/libvterm/t/11state_movecursor.test new file mode 100644 index 0000000..26e447e --- /dev/null +++ b/src/libvterm/t/11state_movecursor.test @@ -0,0 +1,224 @@ +INIT +UTF8 1 +WANTSTATE + +!Implicit +PUSH "ABC" + ?cursor = 0,3 +!Backspace +PUSH "\b" + ?cursor = 0,2 +!Horizontal Tab +PUSH "\t" + ?cursor = 0,8 +!Carriage Return +PUSH "\r" + ?cursor = 0,0 +!Linefeed +PUSH "\n" + ?cursor = 1,0 + +!Backspace bounded by lefthand edge +PUSH "\e[4;2H" + ?cursor = 3,1 +PUSH "\b" + ?cursor = 3,0 +PUSH "\b" + ?cursor = 3,0 + +!Backspace cancels phantom +PUSH "\e[4;80H" + ?cursor = 3,79 +PUSH "X" + ?cursor = 3,79 +PUSH "\b" + ?cursor = 3,78 + +!HT bounded by righthand edge +PUSH "\e[1;78H" + ?cursor = 0,77 +PUSH "\t" + ?cursor = 0,79 +PUSH "\t" + ?cursor = 0,79 + +RESET + +!Index +PUSH "ABC\eD" + ?cursor = 1,3 +!Reverse Index +PUSH "\eM" + ?cursor = 0,3 +!Newline +PUSH "\eE" + ?cursor = 1,0 + +RESET + +!Cursor Forward +PUSH "\e[B" + ?cursor = 1,0 +PUSH "\e[3B" + ?cursor = 4,0 +PUSH "\e[0B" + ?cursor = 5,0 + +!Cursor Down +PUSH "\e[C" + ?cursor = 5,1 +PUSH "\e[3C" + ?cursor = 5,4 +PUSH "\e[0C" + ?cursor = 5,5 + +!Cursor Up +PUSH "\e[A" + ?cursor = 4,5 +PUSH "\e[3A" + ?cursor = 1,5 +PUSH "\e[0A" + ?cursor = 0,5 + +!Cursor Backward +PUSH "\e[D" + ?cursor = 0,4 +PUSH "\e[3D" + ?cursor = 0,1 +PUSH "\e[0D" + ?cursor = 0,0 + +!Cursor Next Line +PUSH " " + ?cursor = 0,3 +PUSH "\e[E" + ?cursor = 1,0 +PUSH " " + ?cursor = 1,3 +PUSH "\e[2E" + ?cursor = 3,0 +PUSH "\e[0E" + ?cursor = 4,0 + +!Cursor Previous Line +PUSH " " + ?cursor = 4,3 +PUSH "\e[F" + ?cursor = 3,0 +PUSH " " + ?cursor = 3,3 +PUSH "\e[2F" + ?cursor = 1,0 +PUSH "\e[0F" + ?cursor = 0,0 + +!Cursor Horizontal Absolute +PUSH "\n" + ?cursor = 1,0 +PUSH "\e[20G" + ?cursor = 1,19 +PUSH "\e[G" + ?cursor = 1,0 + +!Cursor Position +PUSH "\e[10;5H" + ?cursor = 9,4 +PUSH "\e[8H" + ?cursor = 7,0 +PUSH "\e[H" + ?cursor = 0,0 + +!Cursor Position cancels phantom +PUSH "\e[10;78H" + ?cursor = 9,77 +PUSH "ABC" + ?cursor = 9,79 +PUSH "\e[10;80H" +PUSH "C" + ?cursor = 9,79 +PUSH "X" + ?cursor = 10,1 + +RESET + +!Bounds Checking +PUSH "\e[A" + ?cursor = 0,0 +PUSH "\e[D" + ?cursor = 0,0 +PUSH "\e[25;80H" + ?cursor = 24,79 +PUSH "\e[B" + ?cursor = 24,79 +PUSH "\e[C" + ?cursor = 24,79 +PUSH "\e[E" + ?cursor = 24,0 +PUSH "\e[H" + ?cursor = 0,0 +PUSH "\e[F" + ?cursor = 0,0 +PUSH "\e[999G" + ?cursor = 0,79 +PUSH "\e[99;99H" + ?cursor = 24,79 + +RESET + +!Horizontal Position Absolute +PUSH "\e[5`" + ?cursor = 0,4 + +!Horizontal Position Relative +PUSH "\e[3a" + ?cursor = 0,7 + +!Horizontal Position Backward +PUSH "\e[3j" + ?cursor = 0,4 + +!Horizontal and Vertical Position +PUSH "\e[3;3f" + ?cursor = 2,2 + +!Vertical Position Absolute +PUSH "\e[5d" + ?cursor = 4,2 + +!Vertical Position Relative +PUSH "\e[2e" + ?cursor = 6,2 + +!Vertical Position Backward +PUSH "\e[2k" + ?cursor = 4,2 + +RESET + +!Horizontal Tab +PUSH "\t" + ?cursor = 0,8 +PUSH " " + ?cursor = 0,11 +PUSH "\t" + ?cursor = 0,16 +PUSH " " + ?cursor = 0,23 +PUSH "\t" + ?cursor = 0,24 +PUSH " " + ?cursor = 0,32 +PUSH "\t" + ?cursor = 0,40 + +!Cursor Horizontal Tab +PUSH "\e[I" + ?cursor = 0,48 +PUSH "\e[2I" + ?cursor = 0,64 + +!Cursor Backward Tab +PUSH "\e[Z" + ?cursor = 0,56 +PUSH "\e[2Z" + ?cursor = 0,40 diff --git a/src/libvterm/t/12state_scroll.test b/src/libvterm/t/12state_scroll.test new file mode 100644 index 0000000..c1f2791 --- /dev/null +++ b/src/libvterm/t/12state_scroll.test @@ -0,0 +1,156 @@ +INIT +UTF8 1 +WANTSTATE s + +!Linefeed +PUSH "\n"x24 + ?cursor = 24,0 +PUSH "\n" + scrollrect 0..25,0..80 => +1,+0 + ?cursor = 24,0 + +RESET + +!Index +PUSH "\e[25H" +PUSH "\eD" + scrollrect 0..25,0..80 => +1,+0 + +RESET + +!Reverse Index +PUSH "\eM" + scrollrect 0..25,0..80 => -1,+0 + +RESET + +!Linefeed in DECSTBM +PUSH "\e[1;10r" + ?cursor = 0,0 +PUSH "\n"x9 + ?cursor = 9,0 +PUSH "\n" + scrollrect 0..10,0..80 => +1,+0 + ?cursor = 9,0 + +!Linefeed outside DECSTBM +PUSH "\e[20H" + ?cursor = 19,0 +PUSH "\n" + ?cursor = 20,0 + +!Index in DECSTBM +PUSH "\e[9;10r" +PUSH "\e[10H" +PUSH "\eM" + ?cursor = 8,0 +PUSH "\eM" + scrollrect 8..10,0..80 => -1,+0 + +!Reverse Index in DECSTBM +PUSH "\e[25H" + ?cursor = 24,0 +PUSH "\n" + # no scrollrect + ?cursor = 24,0 + +!Linefeed in DECSTBM+DECSLRM +PUSH "\e[?69h" +PUSH "\e[3;10r\e[10;40s" +PUSH "\e[10;10H\n" + scrollrect 2..10,9..40 => +1,+0 + +!IND/RI in DECSTBM+DECSLRM +PUSH "\eD" + scrollrect 2..10,9..40 => +1,+0 +PUSH "\e[3;10H\eM" + scrollrect 2..10,9..40 => -1,+0 + +!DECRQSS on DECSTBM +PUSH "\eP\$qr\e\\" + output "\eP1\$r3;10r\e\\" + +!DECRQSS on DECSLRM +PUSH "\eP\$qs\e\\" + output "\eP1\$r10;40s\e\\" + +!Setting invalid DECSLRM with !DECVSSM is still rejected +PUSH "\e[?69l\e[;0s\e[?69h" + +RESET + +!Scroll Down +PUSH "\e[S" + scrollrect 0..25,0..80 => +1,+0 + ?cursor = 0,0 +PUSH "\e[2S" + scrollrect 0..25,0..80 => +2,+0 + ?cursor = 0,0 +PUSH "\e[100S" + scrollrect 0..25,0..80 => +25,+0 + +!Scroll Up +PUSH "\e[T" + scrollrect 0..25,0..80 => -1,+0 + ?cursor = 0,0 +PUSH "\e[2T" + scrollrect 0..25,0..80 => -2,+0 + ?cursor = 0,0 +PUSH "\e[100T" + scrollrect 0..25,0..80 => -25,+0 + +!SD/SU in DECSTBM +PUSH "\e[5;20r" +PUSH "\e[S" + scrollrect 4..20,0..80 => +1,+0 +PUSH "\e[T" + scrollrect 4..20,0..80 => -1,+0 + +RESET + +!SD/SU in DECSTBM+DECSLRM +PUSH "\e[?69h" +PUSH "\e[3;10r\e[10;40s" + ?cursor = 0,0 +PUSH "\e[3;10H" + ?cursor = 2,9 +PUSH "\e[S" + scrollrect 2..10,9..40 => +1,+0 +PUSH "\e[?69l" +PUSH "\e[S" + scrollrect 2..10,0..80 => +1,+0 + +!Invalid boundaries +RESET + +PUSH "\e[100;105r\eD" +PUSH "\e[5;2r\eD" + +RESET +WANTSTATE -s+me + +!Scroll Down move+erase emulation +PUSH "\e[S" + moverect 1..25,0..80 -> 0..24,0..80 + erase 24..25,0..80 + ?cursor = 0,0 +PUSH "\e[2S" + moverect 2..25,0..80 -> 0..23,0..80 + erase 23..25,0..80 + ?cursor = 0,0 + +!Scroll Up move+erase emulation +PUSH "\e[T" + moverect 0..24,0..80 -> 1..25,0..80 + erase 0..1,0..80 + ?cursor = 0,0 +PUSH "\e[2T" + moverect 0..23,0..80 -> 2..25,0..80 + erase 0..2,0..80 + ?cursor = 0,0 + +!DECSTBM resets cursor position +PUSH "\e[5;5H" + ?cursor = 4,4 +PUSH "\e[r" + ?cursor = 0,0 diff --git a/src/libvterm/t/13state_edit.test b/src/libvterm/t/13state_edit.test new file mode 100644 index 0000000..d3f3e9e --- /dev/null +++ b/src/libvterm/t/13state_edit.test @@ -0,0 +1,304 @@ +INIT +UTF8 1 +WANTSTATE seb + +!ICH +RESET + erase 0..25,0..80 + ?cursor = 0,0 +PUSH "ACD" +PUSH "\e[2D" + ?cursor = 0,1 +PUSH "\e[@" + scrollrect 0..1,1..80 => +0,-1 + ?cursor = 0,1 +PUSH "B" + ?cursor = 0,2 +PUSH "\e[3@" + scrollrect 0..1,2..80 => +0,-3 + +!ICH with DECSLRM +PUSH "\e[?69h" +PUSH "\e[;50s" +PUSH "\e[20G\e[@" + scrollrect 0..1,19..50 => +0,-1 + +!ICH outside DECSLRM +PUSH "\e[70G\e[@" + # nothing happens + +!DCH +RESET + erase 0..25,0..80 + ?cursor = 0,0 +PUSH "ABBC" +PUSH "\e[3D" + ?cursor = 0,1 +PUSH "\e[P" + scrollrect 0..1,1..80 => +0,+1 + ?cursor = 0,1 +PUSH "\e[3P" + scrollrect 0..1,1..80 => +0,+3 + ?cursor = 0,1 + +!DCH with DECSLRM +PUSH "\e[?69h" +PUSH "\e[;50s" +PUSH "\e[20G\e[P" + scrollrect 0..1,19..50 => +0,+1 + +!DCH outside DECSLRM +PUSH "\e[70G\e[P" + # nothing happens + +!ECH +RESET + erase 0..25,0..80 + ?cursor = 0,0 +PUSH "ABC" +PUSH "\e[2D" + ?cursor = 0,1 +PUSH "\e[X" + erase 0..1,1..2 + ?cursor = 0,1 +PUSH "\e[3X" + erase 0..1,1..4 + ?cursor = 0,1 +# ECH more columns than there are should be bounded +PUSH "\e[100X" + erase 0..1,1..80 + +!IL +RESET + erase 0..25,0..80 + ?cursor = 0,0 +PUSH "A\r\nC" + ?cursor = 1,1 +PUSH "\e[L" + scrollrect 1..25,0..80 => -1,+0 + # TODO: ECMA-48 says we should move to line home, but neither xterm nor + # xfce4-terminal do this + ?cursor = 1,1 +PUSH "\rB" + ?cursor = 1,1 +PUSH "\e[3L" + scrollrect 1..25,0..80 => -3,+0 + +!IL with DECSTBM +PUSH "\e[5;15r" +PUSH "\e[5H\e[L" + scrollrect 4..15,0..80 => -1,+0 + +!IL outside DECSTBM +PUSH "\e[20H\e[L" + # nothing happens + +!IL with DECSTBM+DECSLRM +PUSH "\e[?69h" +PUSH "\e[10;50s" +PUSH "\e[5;10H\e[L" + scrollrect 4..15,9..50 => -1,+0 + +!DL +RESET + erase 0..25,0..80 + ?cursor = 0,0 +PUSH "A\r\nB\r\nB\r\nC" + ?cursor = 3,1 +PUSH "\e[2H" + ?cursor = 1,0 +PUSH "\e[M" + scrollrect 1..25,0..80 => +1,+0 + ?cursor = 1,0 +PUSH "\e[3M" + scrollrect 1..25,0..80 => +3,+0 + ?cursor = 1,0 + +!DL with DECSTBM +PUSH "\e[5;15r" +PUSH "\e[5H\e[M" + scrollrect 4..15,0..80 => +1,+0 + +!DL outside DECSTBM +PUSH "\e[20H\e[M" + # nothing happens + +!DL with DECSTBM+DECSLRM +PUSH "\e[?69h" +PUSH "\e[10;50s" +PUSH "\e[5;10H\e[M" + scrollrect 4..15,9..50 => +1,+0 + +!DECIC +RESET + erase 0..25,0..80 +PUSH "\e[20G\e[5'}" + scrollrect 0..25,19..80 => +0,-5 + +!DECIC with DECSTBM+DECSLRM +PUSH "\e[?69h" +PUSH "\e[4;20r\e[20;60s" +PUSH "\e[4;20H\e[3'}" + scrollrect 3..20,19..60 => +0,-3 + +!DECIC outside DECSLRM +PUSH "\e[70G\e['}" + # nothing happens + +!DECDC +RESET + erase 0..25,0..80 +PUSH "\e[20G\e[5'~" + scrollrect 0..25,19..80 => +0,+5 + +!DECDC with DECSTBM+DECSLRM +PUSH "\e[?69h" +PUSH "\e[4;20r\e[20;60s" +PUSH "\e[4;20H\e[3'~" + scrollrect 3..20,19..60 => +0,+3 + +!DECDC outside DECSLRM +PUSH "\e[70G\e['~" + # nothing happens + +!EL 0 +RESET + erase 0..25,0..80 + ?cursor = 0,0 +PUSH "ABCDE" +PUSH "\e[3D" + ?cursor = 0,2 +PUSH "\e[0K" + erase 0..1,2..80 + ?cursor = 0,2 + +!EL 1 +RESET + erase 0..25,0..80 + ?cursor = 0,0 +PUSH "ABCDE" +PUSH "\e[3D" + ?cursor = 0,2 +PUSH "\e[1K" + erase 0..1,0..3 + ?cursor = 0,2 + +!EL 2 +RESET + erase 0..25,0..80 + ?cursor = 0,0 +PUSH "ABCDE" +PUSH "\e[3D" + ?cursor = 0,2 +PUSH "\e[2K" + erase 0..1,0..80 + ?cursor = 0,2 + +!SEL +RESET + erase 0..25,0..80 + ?cursor = 0,0 +PUSH "\e[11G" + ?cursor = 0,10 +PUSH "\e[?0K" + erase 0..1,10..80 selective + ?cursor = 0,10 +PUSH "\e[?1K" + erase 0..1,0..11 selective + ?cursor = 0,10 +PUSH "\e[?2K" + erase 0..1,0..80 selective + ?cursor = 0,10 + +!ED 0 +RESET + erase 0..25,0..80 + ?cursor = 0,0 +PUSH "\e[2;2H" + ?cursor = 1,1 +PUSH "\e[0J" + erase 1..2,1..80 + erase 2..25,0..80 + ?cursor = 1,1 + +!ED 1 +RESET + erase 0..25,0..80 + ?cursor = 0,0 +PUSH "\e[2;2H" + ?cursor = 1,1 +PUSH "\e[1J" + erase 0..1,0..80 + erase 1..2,0..2 + ?cursor = 1,1 + +!ED 2 +RESET + erase 0..25,0..80 + ?cursor = 0,0 +PUSH "\e[2;2H" + ?cursor = 1,1 +PUSH "\e[2J" + erase 0..25,0..80 + ?cursor = 1,1 + +!ED 3 +PUSH "\e[3J" + sb_clear + +!SED +RESET + erase 0..25,0..80 +PUSH "\e[5;5H" + ?cursor = 4,4 +PUSH "\e[?0J" + erase 4..5,4..80 selective + erase 5..25,0..80 selective + ?cursor = 4,4 +PUSH "\e[?1J" + erase 0..4,0..80 selective + erase 4..5,0..5 selective + ?cursor = 4,4 +PUSH "\e[?2J" + erase 0..25,0..80 selective + ?cursor = 4,4 + +!DECRQSS on DECSCA +PUSH "\e[2\"q" +PUSH "\eP\$q\"q\e\\" + output "\eP1\$r2\"q\e\\" + +WANTSTATE -s+m + +!ICH move+erase emuation +RESET + erase 0..25,0..80 + ?cursor = 0,0 +PUSH "ACD" +PUSH "\e[2D" + ?cursor = 0,1 +PUSH "\e[@" + moverect 0..1,1..79 -> 0..1,2..80 + erase 0..1,1..2 + ?cursor = 0,1 +PUSH "B" + ?cursor = 0,2 +PUSH "\e[3@" + moverect 0..1,2..77 -> 0..1,5..80 + erase 0..1,2..5 + +!DCH move+erase emulation +RESET + erase 0..25,0..80 + ?cursor = 0,0 +PUSH "ABBC" +PUSH "\e[3D" + ?cursor = 0,1 +PUSH "\e[P" + moverect 0..1,2..80 -> 0..1,1..79 + erase 0..1,79..80 + ?cursor = 0,1 +PUSH "\e[3P" + moverect 0..1,4..80 -> 0..1,1..77 + erase 0..1,77..80 + ?cursor = 0,1 diff --git a/src/libvterm/t/14state_encoding.test b/src/libvterm/t/14state_encoding.test new file mode 100644 index 0000000..b1f5d69 --- /dev/null +++ b/src/libvterm/t/14state_encoding.test @@ -0,0 +1,105 @@ +INIT +WANTSTATE g + +!Default +RESET +PUSH "#" + putglyph 0x23 1 0,0 + +!Designate G0=UK +RESET +PUSH "\e(A" +PUSH "#" + putglyph 0x00a3 1 0,0 + +!Designate G0=DEC drawing +RESET +PUSH "\e(0" +PUSH "a" + putglyph 0x2592 1 0,0 + +!Designate G1 + LS1 +RESET +PUSH "\e)0" +PUSH "a" + putglyph 0x61 1 0,0 +PUSH "\x0e" +PUSH "a" + putglyph 0x2592 1 0,1 +!LS0 +PUSH "\x0f" +PUSH "a" + putglyph 0x61 1 0,2 + +!Designate G2 + LS2 +PUSH "\e*0" +PUSH "a" + putglyph 0x61 1 0,3 +PUSH "\en" +PUSH "a" + putglyph 0x2592 1 0,4 +PUSH "\x0f" +PUSH "a" + putglyph 0x61 1 0,5 + +!Designate G3 + LS3 +PUSH "\e+0" +PUSH "a" + putglyph 0x61 1 0,6 +PUSH "\eo" +PUSH "a" + putglyph 0x2592 1 0,7 +PUSH "\x0f" +PUSH "a" + putglyph 0x61 1 0,8 + +!SS2 +PUSH "a\x{8e}aa" + putglyph 0x61 1 0,9 + putglyph 0x2592 1 0,10 + putglyph 0x61 1 0,11 + +!SS3 +PUSH "a\x{8f}aa" + putglyph 0x61 1 0,12 + putglyph 0x2592 1 0,13 + putglyph 0x61 1 0,14 + +!LS1R +RESET +PUSH "\e~" +PUSH "\xe1" + putglyph 0x61 1 0,0 +PUSH "\e)0" +PUSH "\xe1" + putglyph 0x2592 1 0,1 + +!LS2R +RESET +PUSH "\e}" +PUSH "\xe1" + putglyph 0x61 1 0,0 +PUSH "\e*0" +PUSH "\xe1" + putglyph 0x2592 1 0,1 + +!LS3R +RESET +PUSH "\e|" +PUSH "\xe1" + putglyph 0x61 1 0,0 +PUSH "\e+0" +PUSH "\xe1" + putglyph 0x2592 1 0,1 + +UTF8 1 + +!Mixed US-ASCII and UTF-8 +# U+0108 == 0xc4 0x88 +RESET +PUSH "\e(B" +PUSH "AB\xc4\x88D" + putglyph 0x0041 1 0,0 + putglyph 0x0042 1 0,1 + putglyph 0x0108 1 0,2 + putglyph 0x0044 1 0,3 diff --git a/src/libvterm/t/15state_mode.test b/src/libvterm/t/15state_mode.test new file mode 100644 index 0000000..b7917ad --- /dev/null +++ b/src/libvterm/t/15state_mode.test @@ -0,0 +1,86 @@ +INIT +UTF8 1 +WANTSTATE gme + +!Insert/Replace Mode +RESET + erase 0..25,0..80 + ?cursor = 0,0 +PUSH "AC\e[DB" + putglyph 0x41 1 0,0 + putglyph 0x43 1 0,1 + putglyph 0x42 1 0,1 +PUSH "\e[4h" +PUSH "\e[G" +PUSH "AC\e[DB" + moverect 0..1,0..79 -> 0..1,1..80 + erase 0..1,0..1 + putglyph 0x41 1 0,0 + moverect 0..1,1..79 -> 0..1,2..80 + erase 0..1,1..2 + putglyph 0x43 1 0,1 + moverect 0..1,1..79 -> 0..1,2..80 + erase 0..1,1..2 + putglyph 0x42 1 0,1 + +!Insert mode only happens once for UTF-8 combining +PUSH "e" + moverect 0..1,2..79 -> 0..1,3..80 + erase 0..1,2..3 + putglyph 0x65 1 0,2 +PUSH "\xCC\x81" + putglyph 0x65,0x301 1 0,2 + +!Newline/Linefeed mode +RESET + erase 0..25,0..80 + ?cursor = 0,0 +PUSH "\e[5G\n" + ?cursor = 1,4 +PUSH "\e[20h" +PUSH "\e[5G\n" + ?cursor = 2,0 + +!DEC origin mode +RESET + erase 0..25,0..80 + ?cursor = 0,0 +PUSH "\e[5;15r" +PUSH "\e[H" + ?cursor = 0,0 +PUSH "\e[3;3H" + ?cursor = 2,2 +PUSH "\e[?6h" +PUSH "\e[H" + ?cursor = 4,0 +PUSH "\e[3;3H" + ?cursor = 6,2 + +!DECRQM on DECOM +PUSH "\e[?6h" +PUSH "\e[?6\$p" + output "\e[?6;1\$y" +PUSH "\e[?6l" +PUSH "\e[?6\$p" + output "\e[?6;2\$y" + +!Origin mode with DECSLRM +PUSH "\e[?6h" +PUSH "\e[?69h" +PUSH "\e[20;60s" +PUSH "\e[H" + ?cursor = 4,19 + +PUSH "\e[?69l" + +!Origin mode bounds cursor to scrolling region +PUSH "\e[H" +PUSH "\e[10A" + ?cursor = 4,0 +PUSH "\e[20B" + ?cursor = 14,0 + +!Origin mode without scroll region +PUSH "\e[?6l" +PUSH "\e[r\e[?6h" + ?cursor = 0,0 diff --git a/src/libvterm/t/16state_resize.test b/src/libvterm/t/16state_resize.test new file mode 100644 index 0000000..42c77c7 --- /dev/null +++ b/src/libvterm/t/16state_resize.test @@ -0,0 +1,48 @@ +INIT +WANTSTATE g + +!Placement +RESET +PUSH "AB\e[79GCDE" + putglyph 0x41 1 0,0 + putglyph 0x42 1 0,1 + putglyph 0x43 1 0,78 + putglyph 0x44 1 0,79 + putglyph 0x45 1 1,0 + +!Resize +RESET +RESIZE 27,85 +PUSH "AB\e[79GCDE" + putglyph 0x41 1 0,0 + putglyph 0x42 1 0,1 + putglyph 0x43 1 0,78 + putglyph 0x44 1 0,79 + putglyph 0x45 1 0,80 + ?cursor = 0,81 + +!Resize without reset +RESIZE 28,90 + ?cursor = 0,81 +PUSH "FGHI" + putglyph 0x46 1 0,81 + putglyph 0x47 1 0,82 + putglyph 0x48 1 0,83 + putglyph 0x49 1 0,84 + ?cursor = 0,85 + +!Resize shrink moves cursor +RESIZE 25,80 + ?cursor = 0,79 + +!Resize grow doesn't cancel phantom +RESET +PUSH "\e[79GAB" + putglyph 0x41 1 0,78 + putglyph 0x42 1 0,79 + ?cursor = 0,79 +RESIZE 30,100 + ?cursor = 0,80 +PUSH "C" + putglyph 0x43 1 0,80 + ?cursor = 0,81 diff --git a/src/libvterm/t/17state_mouse.test b/src/libvterm/t/17state_mouse.test new file mode 100644 index 0000000..e5ba29b --- /dev/null +++ b/src/libvterm/t/17state_mouse.test @@ -0,0 +1,181 @@ +INIT +WANTSTATE p + +!DECRQM on with mouse off +PUSH "\e[?1000\$p" + output "\e[?1000;2\$y" +PUSH "\e[?1002\$p" + output "\e[?1002;2\$y" +PUSH "\e[?1003\$p" + output "\e[?1003;2\$y" + +!Mouse in simple button report mode +RESET + settermprop 1 true + settermprop 2 true + settermprop 7 1 +PUSH "\e[?1000h" + settermprop 8 1 + +!Press 1 +MOUSEMOVE 0,0 0 +MOUSEBTN d 1 0 + output "\e[M\x20\x21\x21" + +!Release 1 +MOUSEBTN u 1 0 + output "\e[M\x23\x21\x21" + +!Ctrl-Press 1 +MOUSEBTN d 1 C + output "\e[M\x30\x21\x21" +MOUSEBTN u 1 C + output "\e[M\x33\x21\x21" + +!Button 2 +MOUSEBTN d 2 0 + output "\e[M\x21\x21\x21" +MOUSEBTN u 2 0 + output "\e[M\x23\x21\x21" + +!Position +MOUSEMOVE 10,20 0 +MOUSEBTN d 1 0 + output "\e[M\x20\x35\x2b" + +MOUSEBTN u 1 0 + output "\e[M\x23\x35\x2b" +MOUSEMOVE 10,21 0 + # no output + +!Wheel events +MOUSEBTN d 4 0 + output "\e[M\x60\x36\x2b" +MOUSEBTN d 4 0 + output "\e[M\x60\x36\x2b" +MOUSEBTN d 5 0 + output "\e[M\x61\x36\x2b" + +!DECRQM on mouse button mode +PUSH "\e[?1000\$p" + output "\e[?1000;1\$y" +PUSH "\e[?1002\$p" + output "\e[?1002;2\$y" +PUSH "\e[?1003\$p" + output "\e[?1003;2\$y" + +!Drag events +RESET + settermprop 1 true + settermprop 2 true + settermprop 7 1 +PUSH "\e[?1002h" + settermprop 8 2 + +MOUSEMOVE 5,5 0 +MOUSEBTN d 1 0 + output "\e[M\x20\x26\x26" +MOUSEMOVE 5,6 0 + output "\e[M\x40\x27\x26" +MOUSEMOVE 6,6 0 + output "\e[M\x40\x27\x27" +MOUSEMOVE 6,6 0 + # no output +MOUSEBTN u 1 0 + output "\e[M\x23\x27\x27" +MOUSEMOVE 6,7 + # no output + +!DECRQM on mouse drag mode +PUSH "\e[?1000\$p" + output "\e[?1000;2\$y" +PUSH "\e[?1002\$p" + output "\e[?1002;1\$y" +PUSH "\e[?1003\$p" + output "\e[?1003;2\$y" + +!Non-drag motion events +PUSH "\e[?1003h" + settermprop 8 3 + +MOUSEMOVE 6,8 0 + output "\e[M\x43\x29\x27" + +!DECRQM on mouse motion mode +PUSH "\e[?1000\$p" + output "\e[?1000;2\$y" +PUSH "\e[?1002\$p" + output "\e[?1002;2\$y" +PUSH "\e[?1003\$p" + output "\e[?1003;1\$y" + +!Bounds checking +MOUSEMOVE 300,300 0 + output "\e[M\x43\xff\xff" +MOUSEBTN d 1 0 + output "\e[M\x20\xff\xff" +MOUSEBTN u 1 0 + output "\e[M\x23\xff\xff" + +!DECRQM on standard encoding mode +PUSH "\e[?1005\$p" + output "\e[?1005;2\$y" +PUSH "\e[?1006\$p" + output "\e[?1006;2\$y" +PUSH "\e[?1015\$p" + output "\e[?1015;2\$y" + +!UTF-8 extended encoding mode +# 300 + 32 + 1 = 333 = U+014d = \xc5\x8d +PUSH "\e[?1005h" +MOUSEBTN d 1 0 + output "\e[M\x20\xc5\x8d\xc5\x8d" +MOUSEBTN u 1 0 + output "\e[M\x23\xc5\x8d\xc5\x8d" + +!DECRQM on UTF-8 extended encoding mode +PUSH "\e[?1005\$p" + output "\e[?1005;1\$y" +PUSH "\e[?1006\$p" + output "\e[?1006;2\$y" +PUSH "\e[?1015\$p" + output "\e[?1015;2\$y" + +!SGR extended encoding mode +PUSH "\e[?1006h" +MOUSEBTN d 1 0 + output "\e[<0;301;301M" +MOUSEBTN u 1 0 + output "\e[<0;301;301m" + +!DECRQM on SGR extended encoding mode +PUSH "\e[?1005\$p" + output "\e[?1005;2\$y" +PUSH "\e[?1006\$p" + output "\e[?1006;1\$y" +PUSH "\e[?1015\$p" + output "\e[?1015;2\$y" + +!rxvt extended encoding mode +PUSH "\e[?1015h" +MOUSEBTN d 1 0 + output "\e[0;301;301M" +MOUSEBTN u 1 0 + output "\e[3;301;301M" + +!DECRQM on rxvt extended encoding mode +PUSH "\e[?1005\$p" + output "\e[?1005;2\$y" +PUSH "\e[?1006\$p" + output "\e[?1006;2\$y" +PUSH "\e[?1015\$p" + output "\e[?1015;1\$y" + +!Mouse disabled reports nothing +RESET + settermprop 1 true + settermprop 2 true + settermprop 7 1 +MOUSEMOVE 0,0 0 +MOUSEBTN d 1 0 +MOUSEBTN u 1 0 diff --git a/src/libvterm/t/18state_termprops.test b/src/libvterm/t/18state_termprops.test new file mode 100644 index 0000000..83c333f --- /dev/null +++ b/src/libvterm/t/18state_termprops.test @@ -0,0 +1,42 @@ +INIT +WANTSTATE p + +RESET + settermprop 1 true + settermprop 2 true + settermprop 7 1 + +!Cursor visibility +PUSH "\e[?25h" + settermprop 1 true +PUSH "\e[?25\$p" + output "\e[?25;1\$y" +PUSH "\e[?25l" + settermprop 1 false +PUSH "\e[?25\$p" + output "\e[?25;2\$y" + +!Cursor blink +PUSH "\e[?12h" + settermprop 2 true +PUSH "\e[?12\$p" + output "\e[?12;1\$y" +PUSH "\e[?12l" + settermprop 2 false +PUSH "\e[?12\$p" + output "\e[?12;2\$y" + +!Cursor shape +PUSH "\e[3 q" + settermprop 2 true + settermprop 7 2 + +!Title +PUSH "\e]2;Here is my title\a" + settermprop 4 ["Here is my title"] + +!Title split write +PUSH "\e]2;Here is" + settermprop 4 ["Here is" +PUSH " another title\a" + settermprop 4 " another title"] diff --git a/src/libvterm/t/20state_wrapping.test b/src/libvterm/t/20state_wrapping.test new file mode 100644 index 0000000..606fa06 --- /dev/null +++ b/src/libvterm/t/20state_wrapping.test @@ -0,0 +1,69 @@ +INIT +UTF8 1 +WANTSTATE gm + +!79th Column +PUSH "\e[75G" +PUSH "A"x5 + putglyph 0x41 1 0,74 + putglyph 0x41 1 0,75 + putglyph 0x41 1 0,76 + putglyph 0x41 1 0,77 + putglyph 0x41 1 0,78 + ?cursor = 0,79 + +!80th Column Phantom +PUSH "A" + putglyph 0x41 1 0,79 + ?cursor = 0,79 + +!Line Wraparound +PUSH "B" + putglyph 0x42 1 1,0 + ?cursor = 1,1 + +!Line Wraparound during combined write +PUSH "\e[78G" +PUSH "BBBCC" + putglyph 0x42 1 1,77 + putglyph 0x42 1 1,78 + putglyph 0x42 1 1,79 + putglyph 0x43 1 2,0 + putglyph 0x43 1 2,1 + ?cursor = 2,2 + +!DEC Auto Wrap Mode +RESET +PUSH "\e[?7l" +PUSH "\e[75G" +PUSH "D"x6 + putglyph 0x44 1 0,74 + putglyph 0x44 1 0,75 + putglyph 0x44 1 0,76 + putglyph 0x44 1 0,77 + putglyph 0x44 1 0,78 + putglyph 0x44 1 0,79 + ?cursor = 0,79 +PUSH "D" + putglyph 0x44 1 0,79 + ?cursor = 0,79 +PUSH "\e[?7h" + +!80th column causes linefeed on wraparound +PUSH "\e[25;78HABC" + putglyph 0x41 1 24,77 + putglyph 0x42 1 24,78 + putglyph 0x43 1 24,79 + ?cursor = 24,79 +PUSH "D" + moverect 1..25,0..80 -> 0..24,0..80 + putglyph 0x44 1 24,0 + +!80th column phantom linefeed phantom cancelled by explicit cursor move +PUSH "\e[25;78HABC" + putglyph 0x41 1 24,77 + putglyph 0x42 1 24,78 + putglyph 0x43 1 24,79 + ?cursor = 24,79 +PUSH "\e[25;1HD" + putglyph 0x44 1 24,0 diff --git a/src/libvterm/t/21state_tabstops.test b/src/libvterm/t/21state_tabstops.test new file mode 100644 index 0000000..df4a589 --- /dev/null +++ b/src/libvterm/t/21state_tabstops.test @@ -0,0 +1,60 @@ +INIT +WANTSTATE g + +!Initial +RESET +PUSH "\tX" + putglyph 0x58 1 0,8 +PUSH "\tX" + putglyph 0x58 1 0,16 + ?cursor = 0,17 + +!HTS +PUSH "\e[5G\eH" +PUSH "\e[G\tX" + putglyph 0x58 1 0,4 + ?cursor = 0,5 + +!TBC 0 +PUSH "\e[9G\e[g" +PUSH "\e[G\tX\tX" + putglyph 0x58 1 0,4 + putglyph 0x58 1 0,16 + ?cursor = 0,17 + +!TBC 3 +PUSH "\e[3g\e[50G\eH\e[G" + ?cursor = 0,0 +PUSH "\tX" + putglyph 0x58 1 0,49 + ?cursor = 0,50 + +!Tabstops after resize +RESET +RESIZE 30,100 +# Should be 100/8 = 12 tabstops +PUSH "\tX" + putglyph 0x58 1 0,8 +PUSH "\tX" + putglyph 0x58 1 0,16 +PUSH "\tX" + putglyph 0x58 1 0,24 +PUSH "\tX" + putglyph 0x58 1 0,32 +PUSH "\tX" + putglyph 0x58 1 0,40 +PUSH "\tX" + putglyph 0x58 1 0,48 +PUSH "\tX" + putglyph 0x58 1 0,56 +PUSH "\tX" + putglyph 0x58 1 0,64 +PUSH "\tX" + putglyph 0x58 1 0,72 +PUSH "\tX" + putglyph 0x58 1 0,80 +PUSH "\tX" + putglyph 0x58 1 0,88 +PUSH "\tX" + putglyph 0x58 1 0,96 + ?cursor = 0,97 diff --git a/src/libvterm/t/22state_save.test b/src/libvterm/t/22state_save.test new file mode 100644 index 0000000..81e9226 --- /dev/null +++ b/src/libvterm/t/22state_save.test @@ -0,0 +1,64 @@ +INIT +WANTSTATE p + +RESET + settermprop 1 true + settermprop 2 true + settermprop 7 1 + +!Set up state +PUSH "\e[2;2H" + ?cursor = 1,1 +PUSH "\e[1m" + ?pen bold = on + +!Save +PUSH "\e[?1048h" + +!Change state +PUSH "\e[5;5H" + ?cursor = 4,4 +PUSH "\e[4 q" + settermprop 2 false + settermprop 7 2 +PUSH "\e[22;4m" + ?pen bold = off + ?pen underline = 1 + +!Restore +PUSH "\e[?1048l" + settermprop 1 true + settermprop 2 true + settermprop 7 1 + ?cursor = 1,1 + ?pen bold = on + ?pen underline = 0 + +!Save/restore using DECSC/DECRC +PUSH "\e[2;2H\e7" + ?cursor = 1,1 + +PUSH "\e[5;5H" + ?cursor = 4,4 +PUSH "\e8" + settermprop 1 true + settermprop 2 true + settermprop 7 1 + ?cursor = 1,1 + +!Save twice, restore twice happens on both edge transitions +PUSH "\e[2;10H\e[?1048h\e[6;10H\e[?1048h" +PUSH "\e[H" + ?cursor = 0,0 +PUSH "\e[?1048l" + settermprop 1 true + settermprop 2 true + settermprop 7 1 + ?cursor = 5,9 +PUSH "\e[H" + ?cursor = 0,0 +PUSH "\e[?1048l" + settermprop 1 true + settermprop 2 true + settermprop 7 1 + ?cursor = 5,9 diff --git a/src/libvterm/t/25state_input.test b/src/libvterm/t/25state_input.test new file mode 100644 index 0000000..4eb4c6a --- /dev/null +++ b/src/libvterm/t/25state_input.test @@ -0,0 +1,155 @@ +INIT +WANTSTATE + +!Unmodified ASCII +INCHAR 0 41 + output "A" +INCHAR 0 61 + output "a" + +!Ctrl modifier on ASCII letters +INCHAR C 41 + output "\e[65;5u" +INCHAR C 61 + output "\x01" + +!Alt modifier on ASCII letters +INCHAR A 41 + output "\eA" +INCHAR A 61 + output "\ea" + +!Ctrl-Alt modifier on ASCII letters +INCHAR CA 41 + output "\e[65;7u" +INCHAR CA 61 + output "\e\x01" + +!Special handling of Ctrl-I +INCHAR 0 49 + output "I" +INCHAR 0 69 + output "i" +INCHAR C 49 + output "\e[73;5u" +INCHAR C 69 + output "\e[105;5u" +INCHAR A 49 + output "\eI" +INCHAR A 69 + output "\ei" +INCHAR CA 49 + output "\e[73;7u" +INCHAR CA 69 + output "\e[105;7u" + +!Special handling of Space +INCHAR 0 20 + output " " +INCHAR S 20 + output "\e[32;2u" +INCHAR C 20 + output "\0" +INCHAR SC 20 + output "\e[32;6u" +INCHAR A 20 + output "\e " +INCHAR SA 20 + output "\e[32;4u" +INCHAR CA 20 + output "\e\0" +INCHAR SCA 20 + output "\e[32;8u" + +!Cursor keys in reset (cursor) mode +INKEY 0 Up + output "\e[A" +INKEY S Up + output "\e[1;2A" +INKEY C Up + output "\e[1;5A" +INKEY SC Up + output "\e[1;6A" +INKEY A Up + output "\e[1;3A" +INKEY SA Up + output "\e[1;4A" +INKEY CA Up + output "\e[1;7A" +INKEY SCA Up + output "\e[1;8A" + +!Cursor keys in application mode +PUSH "\e[?1h" +# Plain "Up" should be SS3 A now +INKEY 0 Up + output "\eOA" +# Modified keys should still use CSI +INKEY S Up + output "\e[1;2A" +INKEY C Up + output "\e[1;5A" + +!Shift-Tab should be different +INKEY 0 Tab + output "\x09" +INKEY S Tab + output "\e[Z" +INKEY C Tab + output "\e[9;5u" +INKEY A Tab + output "\e\x09" +INKEY CA Tab + output "\e[9;7u" + +!Enter in linefeed mode +INKEY 0 Enter + output "\x0d" + +!Enter in newline mode +PUSH "\e[20h" +INKEY 0 Enter + output "\x0d\x0a" + +!Unmodified F1 is SS3 P +INKEY 0 F1 + output "\eOP" + +!Modified F1 is CSI P +INKEY S F1 + output "\e[1;2P" +INKEY A F1 + output "\e[1;3P" +INKEY C F1 + output "\e[1;5P" + +!Keypad in DECKPNM +INKEY 0 KP0 + output "0" + +!Keypad in DECKPAM +PUSH "\e=" +INKEY 0 KP0 + output "\eOp" + +!Bracketed paste mode off +PASTE START +PASTE END + +!Bracketed paste mode on +PUSH "\e[?2004h" +PASTE START + output "\e[200~" +PASTE END + output "\e[201~" + +!Focus reporting disabled +FOCUS IN +FOCUS OUT + +!Focus reporting enabled +PUSH "\e[?1004h" +FOCUS IN + output "\e[I" +FOCUS OUT + output "\e[O" diff --git a/src/libvterm/t/26state_query.test b/src/libvterm/t/26state_query.test new file mode 100644 index 0000000..41e7cf8 --- /dev/null +++ b/src/libvterm/t/26state_query.test @@ -0,0 +1,67 @@ +INIT +WANTSTATE + +!DA +RESET +PUSH "\e[c" + output "\e[?1;2c" + +!XTVERSION +RESET +PUSH "\e[>q" + output "\eP>|libvterm(0.3)\e\\" + +!DSR +RESET +PUSH "\e[5n" + output "\e[0n" + +!CPR +PUSH "\e[6n" + output "\e[1;1R" +PUSH "\e[10;10H\e[6n" + output "\e[10;10R" + +!DECCPR +PUSH "\e[?6n" + output "\e[?10;10R" + +!DECRQSS on DECSCUSR +PUSH "\e[3 q" +PUSH "\eP\$q q\e\\" + output "\eP1\$r3 q\e\\" + +!DECRQSS on SGR +PUSH "\e[1;5;7m" +PUSH "\eP\$qm\e\\" + output "\eP1\$r1;5;7m\e\\" + +!DECRQSS on SGR ANSI colours +PUSH "\e[0;31;42m" +PUSH "\eP\$qm\e\\" + output "\eP1\$r31;42m\e\\" + +!DECRQSS on SGR ANSI hi-bright colours +PUSH "\e[0;93;104m" +PUSH "\eP\$qm\e\\" + output "\eP1\$r93;104m\e\\" + +##!DECRQSS on SGR 256-palette colours +#PUSH "\e[0;38:5:56;48:5:78m" +#PUSH "\eP\$qm\e\\" +# output "\eP1\$r38:5:56;48:5:78m\e\\" + +!DECRQSS on SGR RGB8 colours +PUSH "\e[0;38:2:24:68:112;48:2:13:57:101m" +PUSH "\eP\$qm\e\\" + output "\eP1\$r38:2:24:68:112;48:2:13:57:101m\e\\" + +!S8C1T on DSR +PUSH "\e G" +PUSH "\e[5n" + output "\x{9b}0n" +PUSH "\e F" + +#!Truncation on attempted buffer overflow +#PUSH "\e[6n" x 30 +# output "\e[10;10R" x 25 diff --git a/src/libvterm/t/27state_reset.test b/src/libvterm/t/27state_reset.test new file mode 100644 index 0000000..254f994 --- /dev/null +++ b/src/libvterm/t/27state_reset.test @@ -0,0 +1,32 @@ +INIT +WANTSTATE + +RESET + +!RIS homes cursor +PUSH "\e[5;5H" + ?cursor = 4,4 +WANTSTATE +m +PUSH "\ec" + ?cursor = 0,0 +WANTSTATE -m + +!RIS cancels scrolling region +PUSH "\e[5;10r" +WANTSTATE +s +PUSH "\ec\e[25H\n" + scrollrect 0..25,0..80 => +1,+0 +WANTSTATE -s + +!RIS erases screen +PUSH "ABCDE" +WANTSTATE +e +PUSH "\ec" + erase 0..25,0..80 +WANTSTATE -e + +!RIS clears tabstops +PUSH "\e[5G\eH\e[G\t" + ?cursor = 0,4 +PUSH "\ec\t" + ?cursor = 0,8 diff --git a/src/libvterm/t/28state_dbl_wh.test b/src/libvterm/t/28state_dbl_wh.test new file mode 100644 index 0000000..596194d --- /dev/null +++ b/src/libvterm/t/28state_dbl_wh.test @@ -0,0 +1,61 @@ +INIT +WANTSTATE g + +!Single Width, Single Height +RESET +PUSH "\e#5" +PUSH "Hello" + putglyph 0x48 1 0,0 + putglyph 0x65 1 0,1 + putglyph 0x6c 1 0,2 + putglyph 0x6c 1 0,3 + putglyph 0x6f 1 0,4 + +!Double Width, Single Height +RESET +PUSH "\e#6" +PUSH "Hello" + putglyph 0x48 1 0,0 dwl + putglyph 0x65 1 0,1 dwl + putglyph 0x6c 1 0,2 dwl + putglyph 0x6c 1 0,3 dwl + putglyph 0x6f 1 0,4 dwl + ?cursor = 0,5 +PUSH "\e[40GAB" + putglyph 0x41 1 0,39 dwl + putglyph 0x42 1 1,0 + ?cursor = 1,1 + +!Double Height +RESET +PUSH "\e#3" +PUSH "Hello" + putglyph 0x48 1 0,0 dwl dhl-top + putglyph 0x65 1 0,1 dwl dhl-top + putglyph 0x6c 1 0,2 dwl dhl-top + putglyph 0x6c 1 0,3 dwl dhl-top + putglyph 0x6f 1 0,4 dwl dhl-top + ?cursor = 0,5 +PUSH "\r\n\e#4" +PUSH "Hello" + putglyph 0x48 1 1,0 dwl dhl-bottom + putglyph 0x65 1 1,1 dwl dhl-bottom + putglyph 0x6c 1 1,2 dwl dhl-bottom + putglyph 0x6c 1 1,3 dwl dhl-bottom + putglyph 0x6f 1 1,4 dwl dhl-bottom + ?cursor = 1,5 + +!Double Width scrolling +RESET +PUSH "\e[20H\e#6ABC" + putglyph 0x41 1 19,0 dwl + putglyph 0x42 1 19,1 dwl + putglyph 0x43 1 19,2 dwl +PUSH "\e[25H\n" +PUSH "\e[19;4HDE" + putglyph 0x44 1 18,3 dwl + putglyph 0x45 1 18,4 dwl +PUSH "\e[H\eM" +PUSH "\e[20;6HFG" + putglyph 0x46 1 19,5 dwl + putglyph 0x47 1 19,6 dwl diff --git a/src/libvterm/t/29state_fallback.test b/src/libvterm/t/29state_fallback.test new file mode 100644 index 0000000..4ab2e18 --- /dev/null +++ b/src/libvterm/t/29state_fallback.test @@ -0,0 +1,31 @@ +INIT +WANTSTATE f +RESET + +!Unrecognised control +PUSH "\x03" + control 03 + +!Unrecognised CSI +PUSH "\e[?15;2z" + csi 0x7a L=3f 15,2 + +!Unrecognised OSC +PUSH "\e]27;Something\e\\" + osc [27 "Something"] + +!Unrecognised DCS +PUSH "\ePz123\e\\" + dcs ["z123"] + +!Unrecognised APC +PUSH "\e_z123\e\\" + apc ["z123"] + +!Unrecognised PM +PUSH "\e^z123\e\\" + pm ["z123"] + +!Unrecognised SOS +PUSH "\eXz123\e\\" + sos ["z123"] diff --git a/src/libvterm/t/30state_pen.test b/src/libvterm/t/30state_pen.test new file mode 100644 index 0000000..92cf01d --- /dev/null +++ b/src/libvterm/t/30state_pen.test @@ -0,0 +1,125 @@ +INIT +UTF8 1 +WANTSTATE + +!Reset +PUSH "\e[m" + ?pen bold = off + ?pen underline = 0 + ?pen italic = off + ?pen blink = off + ?pen reverse = off + ?pen font = 0 + ?pen foreground = rgb(240,240,240,is_default_fg) + ?pen background = rgb(0,0,0,is_default_bg) + +!Bold +PUSH "\e[1m" + ?pen bold = on +PUSH "\e[22m" + ?pen bold = off +PUSH "\e[1m\e[m" + ?pen bold = off + +!Underline +PUSH "\e[4m" + ?pen underline = 1 +PUSH "\e[21m" + ?pen underline = 2 +PUSH "\e[24m" + ?pen underline = 0 +PUSH "\e[4m\e[4:0m" + ?pen underline = 0 +PUSH "\e[4:1m" + ?pen underline = 1 +PUSH "\e[4:2m" + ?pen underline = 2 +PUSH "\e[4:3m" + ?pen underline = 3 +PUSH "\e[4m\e[m" + ?pen underline = 0 + +!Italic +PUSH "\e[3m" + ?pen italic = on +PUSH "\e[23m" + ?pen italic = off +PUSH "\e[3m\e[m" + ?pen italic = off + +!Blink +PUSH "\e[5m" + ?pen blink = on +PUSH "\e[25m" + ?pen blink = off +PUSH "\e[5m\e[m" + ?pen blink = off + +!Reverse +PUSH "\e[7m" + ?pen reverse = on +PUSH "\e[27m" + ?pen reverse = off +PUSH "\e[7m\e[m" + ?pen reverse = off + +!Font Selection +PUSH "\e[11m" + ?pen font = 1 +PUSH "\e[19m" + ?pen font = 9 +PUSH "\e[10m" + ?pen font = 0 +PUSH "\e[11m\e[m" + ?pen font = 0 + +!Foreground +PUSH "\e[31m" + ?pen foreground = idx(1) +PUSH "\e[32m" + ?pen foreground = idx(2) +PUSH "\e[34m" + ?pen foreground = idx(4) +PUSH "\e[91m" + ?pen foreground = idx(9) +PUSH "\e[38:2:10:20:30m" + ?pen foreground = rgb(10,20,30) +PUSH "\e[38:5:1m" + ?pen foreground = idx(1) +PUSH "\e[39m" + ?pen foreground = rgb(240,240,240,is_default_fg) + +!Background +PUSH "\e[41m" + ?pen background = idx(1) +PUSH "\e[42m" + ?pen background = idx(2) +PUSH "\e[44m" + ?pen background = idx(4) +PUSH "\e[101m" + ?pen background = idx(9) +PUSH "\e[48:2:10:20:30m" + ?pen background = rgb(10,20,30) +PUSH "\e[48:5:1m" + ?pen background = idx(1) +PUSH "\e[49m" + ?pen background = rgb(0,0,0,is_default_bg) + +!Bold+ANSI colour == highbright +PUSH "\e[m\e[1;37m" + ?pen bold = on + ?pen foreground = idx(15) +PUSH "\e[m\e[37;1m" + ?pen bold = on + ?pen foreground = idx(15) + +!Super/Subscript +PUSH "\e[73m" + ?pen small = on + ?pen baseline = raise +PUSH "\e[74m" + ?pen small = on + ?pen baseline = lower +PUSH "\e[75m" + ?pen small = off + ?pen baseline = normal diff --git a/src/libvterm/t/31state_rep.test b/src/libvterm/t/31state_rep.test new file mode 100644 index 0000000..f820e67 --- /dev/null +++ b/src/libvterm/t/31state_rep.test @@ -0,0 +1,128 @@ +INIT +UTF8 1 +WANTSTATE g + +!REP no argument +RESET +PUSH "a\e[b" + putglyph 0x61 1 0,0 + putglyph 0x61 1 0,1 + +!REP zero (zero should be interpreted as one) +RESET +PUSH "a\e[0b" + putglyph 0x61 1 0,0 + putglyph 0x61 1 0,1 + +!REP lowercase a times two +RESET +PUSH "a\e[2b" + putglyph 0x61 1 0,0 + putglyph 0x61 1 0,1 + putglyph 0x61 1 0,2 + +!REP with UTF-8 1 char +# U+00E9 = 0xC3 0xA9 name: LATIN SMALL LETTER E WITH ACUTE +RESET +PUSH "\xC3\xA9\e[b" + putglyph 0xe9 1 0,0 + putglyph 0xe9 1 0,1 + +!REP with UTF-8 wide char +# U+00E9 = 0xC3 0xA9 name: LATIN SMALL LETTER E WITH ACUTE +RESET +PUSH "\xEF\xBC\x90\e[b" + putglyph 0xff10 2 0,0 + putglyph 0xff10 2 0,2 + +!REP with UTF-8 combining character +RESET +PUSH "e\xCC\x81\e[b" + putglyph 0x65,0x301 1 0,0 + putglyph 0x65,0x301 1 0,1 + +!REP till end of line +RESET +PUSH "a\e[1000bb" + putglyph 0x61 1 0,0 + putglyph 0x61 1 0,1 + putglyph 0x61 1 0,2 + putglyph 0x61 1 0,3 + putglyph 0x61 1 0,4 + putglyph 0x61 1 0,5 + putglyph 0x61 1 0,6 + putglyph 0x61 1 0,7 + putglyph 0x61 1 0,8 + putglyph 0x61 1 0,9 + putglyph 0x61 1 0,10 + putglyph 0x61 1 0,11 + putglyph 0x61 1 0,12 + putglyph 0x61 1 0,13 + putglyph 0x61 1 0,14 + putglyph 0x61 1 0,15 + putglyph 0x61 1 0,16 + putglyph 0x61 1 0,17 + putglyph 0x61 1 0,18 + putglyph 0x61 1 0,19 + putglyph 0x61 1 0,20 + putglyph 0x61 1 0,21 + putglyph 0x61 1 0,22 + putglyph 0x61 1 0,23 + putglyph 0x61 1 0,24 + putglyph 0x61 1 0,25 + putglyph 0x61 1 0,26 + putglyph 0x61 1 0,27 + putglyph 0x61 1 0,28 + putglyph 0x61 1 0,29 + putglyph 0x61 1 0,30 + putglyph 0x61 1 0,31 + putglyph 0x61 1 0,32 + putglyph 0x61 1 0,33 + putglyph 0x61 1 0,34 + putglyph 0x61 1 0,35 + putglyph 0x61 1 0,36 + putglyph 0x61 1 0,37 + putglyph 0x61 1 0,38 + putglyph 0x61 1 0,39 + putglyph 0x61 1 0,40 + putglyph 0x61 1 0,41 + putglyph 0x61 1 0,42 + putglyph 0x61 1 0,43 + putglyph 0x61 1 0,44 + putglyph 0x61 1 0,45 + putglyph 0x61 1 0,46 + putglyph 0x61 1 0,47 + putglyph 0x61 1 0,48 + putglyph 0x61 1 0,49 + putglyph 0x61 1 0,50 + putglyph 0x61 1 0,51 + putglyph 0x61 1 0,52 + putglyph 0x61 1 0,53 + putglyph 0x61 1 0,54 + putglyph 0x61 1 0,55 + putglyph 0x61 1 0,56 + putglyph 0x61 1 0,57 + putglyph 0x61 1 0,58 + putglyph 0x61 1 0,59 + putglyph 0x61 1 0,60 + putglyph 0x61 1 0,61 + putglyph 0x61 1 0,62 + putglyph 0x61 1 0,63 + putglyph 0x61 1 0,64 + putglyph 0x61 1 0,65 + putglyph 0x61 1 0,66 + putglyph 0x61 1 0,67 + putglyph 0x61 1 0,68 + putglyph 0x61 1 0,69 + putglyph 0x61 1 0,70 + putglyph 0x61 1 0,71 + putglyph 0x61 1 0,72 + putglyph 0x61 1 0,73 + putglyph 0x61 1 0,74 + putglyph 0x61 1 0,75 + putglyph 0x61 1 0,76 + putglyph 0x61 1 0,77 + putglyph 0x61 1 0,78 + putglyph 0x61 1 0,79 + putglyph 0x62 1 1,0 + diff --git a/src/libvterm/t/32state_flow.test b/src/libvterm/t/32state_flow.test new file mode 100644 index 0000000..84a13df --- /dev/null +++ b/src/libvterm/t/32state_flow.test @@ -0,0 +1,28 @@ +INIT +WANTSTATE + +# Many of these test cases inspired by +# https://blueprints.launchpad.net/libvterm/+spec/reflow-cases + +!Spillover text marks continuation on second line +RESET +PUSH "A"x100 +PUSH "\r\n" + ?lineinfo 0 = + ?lineinfo 1 = cont + +!CRLF in column 80 does not mark continuation +RESET +PUSH "B"x80 +PUSH "\r\n" +PUSH "B"x20 +PUSH "\r\n" + ?lineinfo 0 = + ?lineinfo 1 = + +!EL cancels continuation of following line +RESET +PUSH "D"x100 + ?lineinfo 1 = cont +PUSH "\eM\e[79G\e[K" + ?lineinfo 1 = diff --git a/src/libvterm/t/40state_selection.test b/src/libvterm/t/40state_selection.test new file mode 100644 index 0000000..6ed8972 --- /dev/null +++ b/src/libvterm/t/40state_selection.test @@ -0,0 +1,55 @@ +INIT +UTF8 1 +WANTSTATE + +!Set clipboard; final chunk len 4 +PUSH "\e]52;c;SGVsbG8s\e\\" + selection-set mask=0001 ["Hello,"] + +!Set clipboard; final chunk len 3 +PUSH "\e]52;c;SGVsbG8sIHc=\e\\" + selection-set mask=0001 ["Hello, w"] + +!Set clipboard; final chunk len 2 +PUSH "\e]52;c;SGVsbG8sIHdvcmxkCg==\e\\" + selection-set mask=0001 ["Hello, world\n"] + +!Set clipboard; split between chunks +PUSH "\e]52;c;SGVs" + selection-set mask=0001 ["Hel" +PUSH "bG8s\e\\" + selection-set mask=0001 "lo,"] + +!Set clipboard; split within chunk +PUSH "\e]52;c;SGVsbG" + selection-set mask=0001 ["Hel" +PUSH "8s\e\\" + selection-set mask=0001 "lo,"] + +!Query clipboard +PUSH "\e]52;c;?\e\\" + selection-query mask=0001 + +!Send clipboard; final chunk len 4 +SELECTION 1 ["Hello,"] + output "\e]52;c;SGVsbG8s\e\\" + +!Send clipboard; final chunk len 3 +SELECTION 1 ["Hello, w"] + output "\e]52;c;SGVsbG8sIHc=\e\\" + +!Send clipboard; final chunk len 2 +SELECTION 1 ["Hello, world\n"] + output "\e]52;c;SGVsbG8sIHdvcmxkCg==\e\\" + +!Send clipboard; split between chunks +SELECTION 1 ["Hel" + output "\e]52;c;SGVs" +SELECTION 1 "lo,"] + output "bG8s\e\\" + +!Send clipboard; split within chunk +SELECTION 1 ["Hello" + output "\e]52;c;SGVs" +SELECTION 1 ","] + output "bG8s\e\\" diff --git a/src/libvterm/t/60screen_ascii.test b/src/libvterm/t/60screen_ascii.test new file mode 100644 index 0000000..57729c5 --- /dev/null +++ b/src/libvterm/t/60screen_ascii.test @@ -0,0 +1,69 @@ +INIT +WANTSCREEN ac + +!Get +RESET +PUSH "ABC" + movecursor 0,3 + ?screen_chars 0,0,1,3 = "ABC" + ?screen_chars 0,0,1,80 = "ABC" + ?screen_text 0,0,1,3 = 0x41,0x42,0x43 + ?screen_text 0,0,1,80 = 0x41,0x42,0x43 + ?screen_cell 0,0 = {0x41} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0) + ?screen_cell 0,1 = {0x42} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0) + ?screen_cell 0,2 = {0x43} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0) + ?screen_row 0 = "ABC" + ?screen_eol 0,0 = 0 + ?screen_eol 0,2 = 0 + ?screen_eol 0,3 = 1 +PUSH "\e[H" + movecursor 0,0 + ?screen_row 0 = "ABC" + ?screen_text 0,0,1,80 = 0x41,0x42,0x43 +PUSH "E" + movecursor 0,1 + ?screen_row 0 = "EBC" + ?screen_text 0,0,1,80 = 0x45,0x42,0x43 + +WANTSCREEN -c + +!Erase +RESET +PUSH "ABCDE\e[H\e[K" + ?screen_row 0 = "" + ?screen_text 0,0,1,80 = + +!Copycell +RESET +PUSH "ABC\e[H\e[@" +PUSH "1" + ?screen_row 0 = "1ABC" + +RESET +PUSH "ABC\e[H\e[P" + ?screen_chars 0,0,1,1 = "B" + ?screen_chars 0,1,1,2 = "C" + ?screen_chars 0,0,1,80 = "BC" + +!Space padding +RESET +PUSH "Hello\e[CWorld" + ?screen_row 0 = "Hello World" + ?screen_text 0,0,1,80 = 0x48,0x65,0x6c,0x6c,0x6f,0x20,0x57,0x6f,0x72,0x6c,0x64 + +!Linefeed padding +RESET +PUSH "Hello\r\nWorld" + ?screen_chars 0,0,2,80 = "Hello\nWorld" + ?screen_text 0,0,2,80 = 0x48,0x65,0x6c,0x6c,0x6f,0x0a,0x57,0x6f,0x72,0x6c,0x64 + +!Altscreen +RESET +PUSH "P" + ?screen_row 0 = "P" +PUSH "\e[?1049h" + ?screen_row 0 = "" +PUSH "\e[2K\e[HA" + ?screen_row 0 = "A" +PUSH "\e[?1049l" + ?screen_row 0 = "P" diff --git a/src/libvterm/t/61screen_unicode.test b/src/libvterm/t/61screen_unicode.test new file mode 100644 index 0000000..8bde2bd --- /dev/null +++ b/src/libvterm/t/61screen_unicode.test @@ -0,0 +1,47 @@ +INIT +UTF8 1 +WANTSCREEN + +!Single width UTF-8 +# U+00C1 = 0xC3 0x81 name: LATIN CAPITAL LETTER A WITH ACUTE +# U+00E9 = 0xC3 0xA9 name: LATIN SMALL LETTER E WITH ACUTE +RESET +PUSH "\xC3\x81\xC3\xA9" + ?screen_row 0 = 0xc1,0xe9 + ?screen_text 0,0,1,80 = 0xc3,0x81,0xc3,0xa9 + ?screen_cell 0,0 = {0xc1} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0) + +!Wide char +# U+FF10 = 0xEF 0xBC 0x90 name: FULLWIDTH DIGIT ZERO +RESET +PUSH "0123\e[H" +PUSH "\xEF\xBC\x90" + ?screen_row 0 = 0xff10,0x32,0x33 + ?screen_text 0,0,1,80 = 0xef,0xbc,0x90,0x32,0x33 + ?screen_cell 0,0 = {0xff10} width=2 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0) + +!Combining char +# U+0301 = 0xCC 0x81 name: COMBINING ACUTE +RESET +PUSH "0123\e[H" +PUSH "e\xCC\x81" + ?screen_row 0 = 0x65,0x301,0x31,0x32,0x33 + ?screen_text 0,0,1,80 = 0x65,0xcc,0x81,0x31,0x32,0x33 + ?screen_cell 0,0 = {0x65,0x301} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0) + +!10 combining accents should not crash +RESET +PUSH "e\xCC\x81\xCC\x82\xCC\x83\xCC\x84\xCC\x85\xCC\x86\xCC\x87\xCC\x88\xCC\x89\xCC\x8A" + ?screen_cell 0,0 = {0x65,0x301,0x302,0x303,0x304,0x305} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0) + +!40 combining accents in two split writes of 20 should not crash +RESET +PUSH "e\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81" +PUSH "\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81" + ?screen_cell 0,0 = {0x65,0x301,0x301,0x301,0x301,0x301} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0) + +!Outputting CJK doublewidth in 80th column should wraparound to next line and not crash" +RESET +PUSH "\e[80G\xEF\xBC\x90" + ?screen_cell 0,79 = {} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0) + ?screen_cell 1,0 = {0xff10} width=2 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0) diff --git a/src/libvterm/t/62screen_damage.test b/src/libvterm/t/62screen_damage.test new file mode 100644 index 0000000..3b1b238 --- /dev/null +++ b/src/libvterm/t/62screen_damage.test @@ -0,0 +1,155 @@ +INIT +WANTSCREEN aDb + +!Putglyph +RESET + damage 0..25,0..80 +PUSH "123" + damage 0..1,0..1 = 0<31> + damage 0..1,1..2 = 0<32> + damage 0..1,2..3 = 0<33> + +!Erase +PUSH "\e[H" +PUSH "\e[3X" + damage 0..1,0..3 + +!Scroll damages entire line in two chunks +PUSH "\e[H\e[5@" + damage 0..1,5..80 + damage 0..1,0..5 + +!Scroll down damages entire screen in two chunks +PUSH "\e[T" + damage 1..25,0..80 + damage 0..1,0..80 + +!Altscreen damages entire area +PUSH "\e[?1049h" + damage 0..25,0..80 +PUSH "\e[?1049l" + damage 0..25,0..80 + +WANTSCREEN m + +!Scroll invokes moverect but not damage +PUSH "\e[5@" + moverect 0..1,0..75 -> 0..1,5..80 + damage 0..1,0..5 + +WANTSCREEN -m + +!Merge to cells +RESET + damage 0..25,0..80 +DAMAGEMERGE CELL + +PUSH "A" + damage 0..1,0..1 = 0<41> +PUSH "B" + damage 0..1,1..2 = 0<42> +PUSH "C" + damage 0..1,2..3 = 0<43> + +!Merge entire rows +RESET + damage 0..25,0..80 +DAMAGEMERGE ROW + +PUSH "ABCDE\r\nEFGH" + damage 0..1,0..5 = 0<41 42 43 44 45> +DAMAGEFLUSH + damage 1..2,0..4 = 1<45 46 47 48> +PUSH "\e[3;6r\e[6H\eD" + damage 2..5,0..80 +DAMAGEFLUSH + damage 5..6,0..80 + +!Merge entire screen +RESET + damage 0..25,0..80 +DAMAGEMERGE SCREEN + +PUSH "ABCDE\r\nEFGH" +DAMAGEFLUSH + damage 0..2,0..5 = 0<41 42 43 44 45> 1<45 46 47 48> +PUSH "\e[3;6r\e[6H\eD" +DAMAGEFLUSH + damage 2..6,0..80 + +!Merge entire screen with moverect +WANTSCREEN m + +RESET + damage 0..25,0..80 +DAMAGEMERGE SCREEN + +PUSH "ABCDE\r\nEFGH" +PUSH "\e[3;6r\e[6H\eD" + damage 0..2,0..5 = 0<41 42 43 44 45> 1<45 46 47 48> + moverect 3..6,0..80 -> 2..5,0..80 +DAMAGEFLUSH + damage 5..6,0..80 + +!Merge scroll +RESET + damage 0..25,0..80 +DAMAGEMERGE SCROLL + +PUSH "\e[H1\r\n2\r\n3" +PUSH "\e[25H\n\n\n" + sb_pushline 80 = 31 + sb_pushline 80 = 32 + sb_pushline 80 = 33 +DAMAGEFLUSH + moverect 3..25,0..80 -> 0..22,0..80 + damage 0..25,0..80 + +!Merge scroll with damage +PUSH "\e[25H" +PUSH "ABCDE\r\nEFGH\r\n" + sb_pushline 80 = + sb_pushline 80 = +DAMAGEFLUSH + moverect 2..25,0..80 -> 0..23,0..80 + damage 22..25,0..80 = 22<41 42 43 44 45> 23<45 46 47 48> + +!Merge scroll with damage past region +PUSH "\e[3;6r\e[6H1\r\n2\r\n3\r\n4\r\n5" +DAMAGEFLUSH + damage 2..6,0..80 = 2<32> 3<33> 4<34> 5<35> + +!Damage entirely outside scroll region +PUSH "\e[HABC\e[3;6r\e[6H\r\n6" + damage 0..1,0..3 = 0<41 42 43> +DAMAGEFLUSH + moverect 3..6,0..80 -> 2..5,0..80 + damage 5..6,0..80 = 5<36> + +!Damage overlapping scroll region +PUSH "\e[H\e[2J" +DAMAGEFLUSH + damage 0..25,0..80 + +PUSH "\e[HABCD\r\nEFGH\r\nIJKL\e[2;5r\e[5H\r\nMNOP" +DAMAGEFLUSH + moverect 2..5,0..80 -> 1..4,0..80 + damage 0..5,0..80 = 0<41 42 43 44> 1<49 4A 4B 4C> + ## TODO: is this right? + +!Merge scroll*2 with damage +RESET + damage 0..25,0..80 +DAMAGEMERGE SCROLL + +PUSH "\e[25H\r\nABCDE\b\b\b\e[2P\r\n" + sb_pushline 80 = + moverect 1..25,0..80 -> 0..24,0..80 + damage 24..25,0..80 = 24<41 42 43 44 45> + moverect 24..25,4..80 -> 24..25,2..78 + damage 24..25,78..80 + sb_pushline 80 = +DAMAGEFLUSH + moverect 1..25,0..80 -> 0..24,0..80 + damage 24..25,0..80 + ?screen_row 23 = "ABE" diff --git a/src/libvterm/t/63screen_resize.test b/src/libvterm/t/63screen_resize.test new file mode 100644 index 0000000..6835222 --- /dev/null +++ b/src/libvterm/t/63screen_resize.test @@ -0,0 +1,117 @@ +INIT +WANTSTATE +WANTSCREEN + +!Resize wider preserves cells +RESET +RESIZE 25,80 +PUSH "AB\r\nCD" + ?screen_chars 0,0,1,80 = "AB" + ?screen_chars 1,0,2,80 = "CD" +RESIZE 25,100 + ?screen_chars 0,0,1,100 = "AB" + ?screen_chars 1,0,2,100 = "CD" + +!Resize wider allows print in new area +RESET +RESIZE 25,80 +PUSH "AB\e[79GCD" + ?screen_chars 0,0,1,2 = "AB" + ?screen_chars 0,78,1,80 = "CD" +RESIZE 25,100 + ?screen_chars 0,0,1,2 = "AB" + ?screen_chars 0,78,1,80 = "CD" +PUSH "E" + ?screen_chars 0,78,1,81 = "CDE" + +!Resize shorter with blanks just truncates +RESET +RESIZE 25,80 +PUSH "Top\e[10HLine 10" + ?screen_row 0 = "Top" + ?screen_row 9 = "Line 10" + ?cursor = 9,7 +RESIZE 20,80 + ?screen_row 0 = "Top" + ?screen_row 9 = "Line 10" + ?cursor = 9,7 + +!Resize shorter with content must scroll +RESET +RESIZE 25,80 +PUSH "Top\e[25HLine 25\e[15H" + ?screen_row 0 = "Top" + ?screen_row 24 = "Line 25" + ?cursor = 14,0 +WANTSCREEN b +RESIZE 20,80 + sb_pushline 80 = 54 6F 70 + sb_pushline 80 = + sb_pushline 80 = + sb_pushline 80 = + sb_pushline 80 = + ?screen_row 0 = "" + ?screen_row 19 = "Line 25" + ?cursor = 9,0 + +!Resize shorter does not lose line with cursor +# See also https://github.com/neovim/libvterm/commit/1b745d29d45623aa8d22a7b9288c7b0e331c7088 +RESET +WANTSCREEN -b +RESIZE 25,80 +WANTSCREEN b +PUSH "\e[24HLine 24\r\nLine 25\r\n" + sb_pushline 80 = + ?screen_row 23 = "Line 25" + ?cursor = 24,0 +RESIZE 24,80 + sb_pushline 80 = + ?screen_row 22 = "Line 25" + ?cursor = 23,0 + +!Resize shorter does not send the cursor to a negative row +# See also https://github.com/vim/vim/pull/6141 +RESET +WANTSCREEN -b +RESIZE 25,80 +WANTSCREEN b +PUSH "\e[24HLine 24\r\nLine 25\e[H" + ?cursor = 0,0 +RESIZE 20,80 + sb_pushline 80 = + sb_pushline 80 = + sb_pushline 80 = + sb_pushline 80 = + sb_pushline 80 = + ?cursor = 0,0 + +!Resize taller attempts to pop scrollback +RESET +WANTSCREEN -b +RESIZE 25,80 +PUSH "Line 1\e[25HBottom\e[15H" + ?screen_row 0 = "Line 1" + ?screen_row 24 = "Bottom" + ?cursor = 14,0 +WANTSCREEN b +RESIZE 30,80 + sb_popline 80 + sb_popline 80 + sb_popline 80 + sb_popline 80 + sb_popline 80 + ?screen_row 0 = "ABCDE" + ?screen_row 5 = "Line 1" + ?screen_row 29 = "Bottom" + ?cursor = 19,0 +WANTSCREEN -b + +!Resize can operate on altscreen +RESET +WANTSCREEN a +RESIZE 25,80 +PUSH "Main screen\e[?1049h\e[HAlt screen" +RESIZE 30,80 + ?screen_row 0 = "Alt screen" +PUSH "\e[?1049l" + ?screen_row 0 = "Main screen" diff --git a/src/libvterm/t/64screen_pen.test b/src/libvterm/t/64screen_pen.test new file mode 100644 index 0000000..1cb6324 --- /dev/null +++ b/src/libvterm/t/64screen_pen.test @@ -0,0 +1,61 @@ +INIT +WANTSCREEN + +RESET + +!Plain +PUSH "A" + ?screen_cell 0,0 = {0x41} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0) + +!Bold +PUSH "\e[1mB" + ?screen_cell 0,1 = {0x42} width=1 attrs={B} fg=rgb(240,240,240) bg=rgb(0,0,0) + +!Italic +PUSH "\e[3mC" + ?screen_cell 0,2 = {0x43} width=1 attrs={BI} fg=rgb(240,240,240) bg=rgb(0,0,0) + +!Underline +PUSH "\e[4mD" + ?screen_cell 0,3 = {0x44} width=1 attrs={BU1I} fg=rgb(240,240,240) bg=rgb(0,0,0) + +!Reset +PUSH "\e[mE" + ?screen_cell 0,4 = {0x45} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0) + +!Font +PUSH "\e[11mF\e[m" + ?screen_cell 0,5 = {0x46} width=1 attrs={F1} fg=rgb(240,240,240) bg=rgb(0,0,0) + +!Foreground +PUSH "\e[31mG\e[m" + ?screen_cell 0,6 = {0x47} width=1 attrs={} fg=idx(1) bg=rgb(0,0,0) + +!Background +PUSH "\e[42mH\e[m" + ?screen_cell 0,7 = {0x48} width=1 attrs={} fg=rgb(240,240,240) bg=idx(2) + +!Super/subscript +PUSH "x\e[74m0\e[73m2\e[m" + ?screen_cell 0,8 = {0x78} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0) + ?screen_cell 0,9 = {0x30} width=1 attrs={S_} fg=rgb(240,240,240) bg=rgb(0,0,0) + ?screen_cell 0,10 = {0x32} width=1 attrs={S^} fg=rgb(240,240,240) bg=rgb(0,0,0) + +!EL sets reverse and colours to end of line +PUSH "\e[H\e[7;33;44m\e[K" + ?screen_cell 0,0 = {} width=1 attrs={R} fg=idx(3) bg=idx(4) + ?screen_cell 0,79 = {} width=1 attrs={R} fg=idx(3) bg=idx(4) + +!DECSCNM xors reverse for entire screen +PUSH "\e[?5h" + ?screen_cell 0,0 = {} width=1 attrs={} fg=idx(3) bg=idx(4) + ?screen_cell 0,79 = {} width=1 attrs={} fg=idx(3) bg=idx(4) + ?screen_cell 1,0 = {} width=1 attrs={R} fg=rgb(240,240,240) bg=rgb(0,0,0) +PUSH "\e[?5\$p" + output "\e[?5;1\$y" +PUSH "\e[?5l" + ?screen_cell 0,0 = {} width=1 attrs={R} fg=idx(3) bg=idx(4) + ?screen_cell 0,79 = {} width=1 attrs={R} fg=idx(3) bg=idx(4) + ?screen_cell 1,0 = {} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0) +PUSH "\e[?5\$p" + output "\e[?5;2\$y" diff --git a/src/libvterm/t/65screen_protect.test b/src/libvterm/t/65screen_protect.test new file mode 100644 index 0000000..ec412a5 --- /dev/null +++ b/src/libvterm/t/65screen_protect.test @@ -0,0 +1,16 @@ +INIT +WANTSCREEN + +!Selective erase +RESET +PUSH "A\e[1\"qB\e[\"qC" + ?screen_row 0 = "ABC" +PUSH "\e[G\e[?J" + ?screen_row 0 = " B" + +!Non-selective erase +RESET +PUSH "A\e[1\"qB\e[\"qC" + ?screen_row 0 = "ABC" +PUSH "\e[G\e[J" + ?screen_row 0 = "" diff --git a/src/libvterm/t/66screen_extent.test b/src/libvterm/t/66screen_extent.test new file mode 100644 index 0000000..a126cec --- /dev/null +++ b/src/libvterm/t/66screen_extent.test @@ -0,0 +1,11 @@ +INIT +WANTSCREEN + +!Bold extent +RESET +PUSH "AB\e[1mCD\e[mE" + ?screen_attrs_extent 0,0 = 0,0-1,1 + ?screen_attrs_extent 0,1 = 0,0-1,1 + ?screen_attrs_extent 0,2 = 0,2-1,3 + ?screen_attrs_extent 0,3 = 0,2-1,3 + ?screen_attrs_extent 0,4 = 0,4-1,79 diff --git a/src/libvterm/t/67screen_dbl_wh.test b/src/libvterm/t/67screen_dbl_wh.test new file mode 100644 index 0000000..9c81e83 --- /dev/null +++ b/src/libvterm/t/67screen_dbl_wh.test @@ -0,0 +1,38 @@ +INIT +WANTSCREEN + +RESET + +!Single Width, Single Height +RESET +PUSH "\e#5" +PUSH "abcde" + ?screen_cell 0,0 = {0x61} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0) + +!Double Width, Single Height +RESET +PUSH "\e#6" +PUSH "abcde" + ?screen_cell 0,0 = {0x61} width=1 attrs={} dwl fg=rgb(240,240,240) bg=rgb(0,0,0) + +!Double Height +RESET +PUSH "\e#3" +PUSH "abcde" +PUSH "\r\n\e#4" +PUSH "abcde" + ?screen_cell 0,0 = {0x61} width=1 attrs={} dwl dhl-top fg=rgb(240,240,240) bg=rgb(0,0,0) + ?screen_cell 1,0 = {0x61} width=1 attrs={} dwl dhl-bottom fg=rgb(240,240,240) bg=rgb(0,0,0) + +!Late change +RESET +PUSH "abcde" + ?screen_cell 0,0 = {0x61} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0) +PUSH "\e#6" + ?screen_cell 0,0 = {0x61} width=1 attrs={} dwl fg=rgb(240,240,240) bg=rgb(0,0,0) + +!DWL doesn't spill over on scroll +RESET +PUSH "\e[25H\e#6Final\r\n" + ?screen_cell 23,0 = {0x46} width=1 attrs={} dwl fg=rgb(240,240,240) bg=rgb(0,0,0) + ?screen_cell 24,0 = {} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0) diff --git a/src/libvterm/t/68screen_termprops.test b/src/libvterm/t/68screen_termprops.test new file mode 100644 index 0000000..bba6660 --- /dev/null +++ b/src/libvterm/t/68screen_termprops.test @@ -0,0 +1,17 @@ +INIT +WANTSCREEN p + +RESET + settermprop 1 true + settermprop 2 true + settermprop 7 1 + +!Cursor visibility +PUSH "\e[?25h" + settermprop 1 true +PUSH "\e[?25l" + settermprop 1 false + +!Title +PUSH "\e]2;Here is my title\a" + settermprop 4 ["Here is my title"] diff --git a/src/libvterm/t/69screen_reflow.test b/src/libvterm/t/69screen_reflow.test new file mode 100644 index 0000000..278cc5b --- /dev/null +++ b/src/libvterm/t/69screen_reflow.test @@ -0,0 +1,79 @@ +INIT +# Run these tests on a much smaller default screen, so debug output is +# nowhere near as noisy +RESIZE 5,10 +WANTSTATE +WANTSCREEN r +RESET + +!Resize wider reflows wide lines +RESET +PUSH "A"x12 + ?screen_row 0 = "AAAAAAAAAA" + ?screen_row 1 = "AA" + ?lineinfo 1 = cont + ?cursor = 1,2 +RESIZE 5,15 + ?screen_row 0 = "AAAAAAAAAAAA" + ?screen_row 1 = + ?lineinfo 1 = + ?cursor = 0,12 +RESIZE 5,20 + ?screen_row 0 = "AAAAAAAAAAAA" + ?screen_row 1 = + ?lineinfo 1 = + ?cursor = 0,12 + +!Resize narrower can create continuation lines +RESET +RESIZE 5,10 +PUSH "ABCDEFGHI" + ?screen_row 0 = "ABCDEFGHI" + ?screen_row 1 = "" + ?lineinfo 1 = + ?cursor = 0,9 +RESIZE 5,8 + ?screen_row 0 = "ABCDEFGH" + ?screen_row 1 = "I" + ?lineinfo 1 = cont + ?cursor = 1,1 +RESIZE 5,6 + ?screen_row 0 = "ABCDEF" + ?screen_row 1 = "GHI" + ?lineinfo 1 = cont + ?cursor = 1,3 + +!Shell wrapped prompt behaviour +RESET +RESIZE 5,10 +PUSH "PROMPT GOES HERE\r\n> \r\n\r\nPROMPT GOES HERE\r\n> " + ?screen_row 0 = "> " + ?screen_row 1 = "" + ?screen_row 2 = "PROMPT GOE" + ?screen_row 3 = "S HERE" + ?lineinfo 3 = cont + ?screen_row 4 = "> " + ?cursor = 4,2 +RESIZE 5,11 + ?screen_row 0 = "> " + ?screen_row 1 = "" + ?screen_row 2 = "PROMPT GOES" + ?screen_row 3 = " HERE" + ?lineinfo 3 = cont + ?screen_row 4 = "> " + ?cursor = 4,2 +RESIZE 5,12 + ?screen_row 0 = "> " + ?screen_row 1 = "" + ?screen_row 2 = "PROMPT GOES " + ?screen_row 3 = "HERE" + ?lineinfo 3 = cont + ?screen_row 4 = "> " + ?cursor = 4,2 +RESIZE 5,16 + ?screen_row 0 = "> " + ?screen_row 1 = "" + ?screen_row 2 = "PROMPT GOES HERE" + ?lineinfo 3 = + ?screen_row 3 = "> " + ?cursor = 3,2 diff --git a/src/libvterm/t/90vttest_01-movement-1.test b/src/libvterm/t/90vttest_01-movement-1.test new file mode 100644 index 0000000..c1a8cb9 --- /dev/null +++ b/src/libvterm/t/90vttest_01-movement-1.test @@ -0,0 +1,87 @@ +INIT +WANTSTATE +WANTSCREEN + +RESET + +PUSH "\e#8" + +PUSH "\e[9;10H\e[1J" +PUSH "\e[18;60H\e[0J\e[1K" +PUSH "\e[9;71H\e[0K" + +$SEQ 10 16: PUSH "\e[\#;10H\e[1K\e[\#;71H\e[0K" + +PUSH "\e[17;30H\e[2K" + +$SEQ 1 80: PUSH "\e[24;\#f*\e[1;\#f*" + +PUSH "\e[2;2H" + +$REP 22: PUSH "+\e[1D\eD" + +PUSH "\e[23;79H" +$REP 22: PUSH "+\e[1D\eM" + +PUSH "\e[2;1H" +$SEQ 2 23: PUSH "*\e[\#;80H*\e[10D\eE" + +PUSH "\e[2;10H\e[42D\e[2C" +$REP 76: PUSH "+\e[0C\e[2D\e[1C" + +PUSH "\e[23;70H\e[42C\e[2D" + +$REP 76: PUSH "+\e[1D\e[1C\e[0D\b" + +PUSH "\e[1;1H" +PUSH "\e[10A" +PUSH "\e[1A" +PUSH "\e[0A" +PUSH "\e[24;80H" +PUSH "\e[10B" +PUSH "\e[1B" +PUSH "\e[0B" +PUSH "\e[10;12H" + +$REP 58: PUSH " " +PUSH "\e[1B\e[58D" + +$REP 58: PUSH " " +PUSH "\e[1B\e[58D" + +$REP 58: PUSH " " +PUSH "\e[1B\e[58D" + +$REP 58: PUSH " " +PUSH "\e[1B\e[58D" + +$REP 58: PUSH " " +PUSH "\e[1B\e[58D" + +$REP 58: PUSH " " +PUSH "\e[1B\e[58D" + +PUSH "\e[5A\e[1CThe screen should be cleared, and have an unbroken bor-" +PUSH "\e[12;13Hder of *'s and +'s around the edge, and exactly in the" +PUSH "\e[13;13Hmiddle there should be a frame of E's around this text" +PUSH "\e[14;13Hwith one (1) free position around it. Push <RETURN>" + +# And the result is... + +!Output + ?screen_row 0 = "********************************************************************************" + ?screen_row 1 = "*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*" +$SEQ 2 7: ?screen_row \# = "*+ +*" + ?screen_row 8 = "*+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*" + ?screen_row 9 = "*+ E E +*" + ?screen_row 10 = "*+ E The screen should be cleared, and have an unbroken bor- E +*" + ?screen_row 11 = "*+ E der of *'s and +'s around the edge, and exactly in the E +*" + ?screen_row 12 = "*+ E middle there should be a frame of E's around this text E +*" + ?screen_row 13 = "*+ E with one (1) free position around it. Push <RETURN> E +*" + ?screen_row 14 = "*+ E E +*" + ?screen_row 15 = "*+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*" +$SEQ 16 21: ?screen_row \# = "*+ +*" + ?screen_row 22 = "*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*" + ?screen_row 23 = "********************************************************************************" + +?cursor = 13,67 diff --git a/src/libvterm/t/90vttest_01-movement-2.test b/src/libvterm/t/90vttest_01-movement-2.test new file mode 100644 index 0000000..3a515e3 --- /dev/null +++ b/src/libvterm/t/90vttest_01-movement-2.test @@ -0,0 +1,40 @@ +INIT +WANTSTATE +WANTSCREEN + +RESET + +PUSH "\e[3;21r" +PUSH "\e[?6h" + +PUSH "\e[19;1HA\e[19;80Ha\x0a\e[18;80HaB\e[19;80HB\b b\x0a\e[19;80HC\b\b\t\tc\e[19;2H\bC\x0a\e[19;80H\x0a\e[18;1HD\e[18;80Hd" +PUSH "\e[19;1HE\e[19;80He\x0a\e[18;80HeF\e[19;80HF\b f\x0a\e[19;80HG\b\b\t\tg\e[19;2H\bG\x0a\e[19;80H\x0a\e[18;1HH\e[18;80Hh" +PUSH "\e[19;1HI\e[19;80Hi\x0a\e[18;80HiJ\e[19;80HJ\b j\x0a\e[19;80HK\b\b\t\tk\e[19;2H\bK\x0a\e[19;80H\x0a\e[18;1HL\e[18;80Hl" +PUSH "\e[19;1HM\e[19;80Hm\x0a\e[18;80HmN\e[19;80HN\b n\x0a\e[19;80HO\b\b\t\to\e[19;2H\bO\x0a\e[19;80H\x0a\e[18;1HP\e[18;80Hp" +PUSH "\e[19;1HQ\e[19;80Hq\x0a\e[18;80HqR\e[19;80HR\b r\x0a\e[19;80HS\b\b\t\ts\e[19;2H\bS\x0a\e[19;80H\x0a\e[18;1HT\e[18;80Ht" +PUSH "\e[19;1HU\e[19;80Hu\x0a\e[18;80HuV\e[19;80HV\b v\x0a\e[19;80HW\b\b\t\tw\e[19;2H\bW\x0a\e[19;80H\x0a\e[18;1HX\e[18;80Hx" +PUSH "\e[19;1HY\e[19;80Hy\x0a\e[18;80HyZ\e[19;80HZ\b z\x0a" + +!Output + +?screen_row 2 = "I i" +?screen_row 3 = "J j" +?screen_row 4 = "K k" +?screen_row 5 = "L l" +?screen_row 6 = "M m" +?screen_row 7 = "N n" +?screen_row 8 = "O o" +?screen_row 9 = "P p" +?screen_row 10 = "Q q" +?screen_row 11 = "R r" +?screen_row 12 = "S s" +?screen_row 13 = "T t" +?screen_row 14 = "U u" +?screen_row 15 = "V v" +?screen_row 16 = "W w" +?screen_row 17 = "X x" +?screen_row 18 = "Y y" +?screen_row 19 = "Z z" +?screen_row 20 = "" + +?cursor = 20,79 diff --git a/src/libvterm/t/90vttest_01-movement-3.test b/src/libvterm/t/90vttest_01-movement-3.test new file mode 100644 index 0000000..f9a99bf --- /dev/null +++ b/src/libvterm/t/90vttest_01-movement-3.test @@ -0,0 +1,21 @@ +# Test of cursor-control characters inside ESC sequences +INIT +WANTSTATE +WANTSCREEN + +RESET + +PUSH "A B C D E F G H I" +PUSH "\x0d\x0a" +PUSH "A\e[2\bCB\e[2\bCC\e[2\bCD\e[2\bCE\e[2\bCF\e[2\bCG\e[2\bCH\e[2\bCI" +PUSH "\x0d\x0a" +PUSH "A \e[\x0d2CB\e[\x0d4CC\e[\x0d6CD\e[\x0d8CE\e[\x0d10CF\e[\x0d12CG\e[\x0d14CH\e[\x0d16CI" +PUSH "\x0d\x0a" +PUSH "A \e[1\x0bAB \e[1\x0bAC \e[1\x0bAD \e[1\x0bAE \e[1\x0bAF \e[1\x0bAG \e[1\x0bAH \e[1\x0bAI \e[1\x0bA" + +!Output + +$SEQ 0 2: ?screen_row \# = "A B C D E F G H I" + ?screen_row 3 = "A B C D E F G H I " + +?cursor = 3,18 diff --git a/src/libvterm/t/90vttest_01-movement-4.test b/src/libvterm/t/90vttest_01-movement-4.test new file mode 100644 index 0000000..0dab3c7 --- /dev/null +++ b/src/libvterm/t/90vttest_01-movement-4.test @@ -0,0 +1,36 @@ +# Test of leading zeroes in ESC sequences +INIT +WANTSCREEN + +RESET + +PUSH "\e[00000000004;000000001HT" +PUSH "\e[00000000004;000000002Hh" +PUSH "\e[00000000004;000000003Hi" +PUSH "\e[00000000004;000000004Hs" +PUSH "\e[00000000004;000000005H " +PUSH "\e[00000000004;000000006Hi" +PUSH "\e[00000000004;000000007Hs" +PUSH "\e[00000000004;000000008H " +PUSH "\e[00000000004;000000009Ha" +PUSH "\e[00000000004;0000000010H " +PUSH "\e[00000000004;0000000011Hc" +PUSH "\e[00000000004;0000000012Ho" +PUSH "\e[00000000004;0000000013Hr" +PUSH "\e[00000000004;0000000014Hr" +PUSH "\e[00000000004;0000000015He" +PUSH "\e[00000000004;0000000016Hc" +PUSH "\e[00000000004;0000000017Ht" +PUSH "\e[00000000004;0000000018H " +PUSH "\e[00000000004;0000000019Hs" +PUSH "\e[00000000004;0000000020He" +PUSH "\e[00000000004;0000000021Hn" +PUSH "\e[00000000004;0000000022Ht" +PUSH "\e[00000000004;0000000023He" +PUSH "\e[00000000004;0000000024Hn" +PUSH "\e[00000000004;0000000025Hc" +PUSH "\e[00000000004;0000000026He" + +!Output + +?screen_row 3 = "This is a correct sentence" diff --git a/src/libvterm/t/90vttest_02-screen-1.test b/src/libvterm/t/90vttest_02-screen-1.test new file mode 100644 index 0000000..003d56f --- /dev/null +++ b/src/libvterm/t/90vttest_02-screen-1.test @@ -0,0 +1,18 @@ +# Test of WRAP AROUND mode setting. +INIT +WANTSCREEN + +RESET + +PUSH "\e[?7h" +$REP 170: PUSH "*" + +PUSH "\e[?7l\e[3;1H" +$REP 177: PUSH "*" + +PUSH "\e[?7h\e[5;1HOK" + +!Output +$SEQ 0 2: ?screen_row \# = "********************************************************************************" + ?screen_row 3 = "" + ?screen_row 4 = "OK" diff --git a/src/libvterm/t/90vttest_02-screen-2.test b/src/libvterm/t/90vttest_02-screen-2.test new file mode 100644 index 0000000..1c3a6a7 --- /dev/null +++ b/src/libvterm/t/90vttest_02-screen-2.test @@ -0,0 +1,29 @@ +# TAB setting/resetting +INIT +WANTSTATE +WANTSCREEN + +RESET + +PUSH "\e[2J\e[3g" + +PUSH "\e[1;1H" +$REP 26: PUSH "\e[3C\eH" + +PUSH "\e[1;4H" +$REP 13: PUSH "\e[0g\e[6C" + +PUSH "\e[1;7H" +PUSH "\e[1g\e[2g" + +PUSH "\e[1;1H" +$REP 13: PUSH "\t*" + +PUSH "\e[2;2H" +$REP 13: PUSH " *" + +!Output +?screen_row 0 = " * * * * * * * * * * * * *" +?screen_row 1 = " * * * * * * * * * * * * *" + +?cursor = 1,79 diff --git a/src/libvterm/t/90vttest_02-screen-3.test b/src/libvterm/t/90vttest_02-screen-3.test new file mode 100644 index 0000000..8cdf8df --- /dev/null +++ b/src/libvterm/t/90vttest_02-screen-3.test @@ -0,0 +1,16 @@ +# Origin mode +INIT +WANTSCREEN + +RESET + +PUSH "\e[?6h" +PUSH "\e[23;24r" +PUSH "\n" +PUSH "Bottom" +PUSH "\e[1;1H" +PUSH "Above" + +!Output +?screen_row 22 = "Above" +?screen_row 23 = "Bottom" diff --git a/src/libvterm/t/90vttest_02-screen-4.test b/src/libvterm/t/90vttest_02-screen-4.test new file mode 100644 index 0000000..44d51f1 --- /dev/null +++ b/src/libvterm/t/90vttest_02-screen-4.test @@ -0,0 +1,17 @@ +# Origin mode (2) +INIT +WANTSCREEN + +RESET + +PUSH "\e[?6l" +PUSH "\e[23;24r" +PUSH "\e[24;1H" +PUSH "Bottom" +PUSH "\e[1;1H" +PUSH "Top" + +!Output +?screen_row 23 = "Bottom" +?screen_row 0 = "Top" + diff --git a/src/libvterm/t/92lp1640917.test b/src/libvterm/t/92lp1640917.test new file mode 100644 index 0000000..70de439 --- /dev/null +++ b/src/libvterm/t/92lp1640917.test @@ -0,0 +1,13 @@ +INIT +WANTSTATE + +!Mouse reporting should not break by idempotent DECSM 1002 +PUSH "\e[?1002h" +MOUSEMOVE 0,0 0 +MOUSEBTN d 1 0 + output "\e[M\x20\x21\x21" +MOUSEMOVE 1,0 0 + output "\e[M\x40\x21\x22" +PUSH "\e[?1002h" +MOUSEMOVE 2,0 0 + output "\e[M\x40\x21\x23" diff --git a/src/libvterm/t/harness.c b/src/libvterm/t/harness.c new file mode 100644 index 0000000..d12c120 --- /dev/null +++ b/src/libvterm/t/harness.c @@ -0,0 +1,1233 @@ +#include "vterm.h" +#include "../src/vterm_internal.h" // We pull in some internal bits too + +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#define streq(a,b) (!strcmp(a,b)) +#define strstartswith(a,b) (!strncmp(a,b,strlen(b))) + +static size_t inplace_hex2bytes(char *s) +{ + char *inpos = s, *outpos = s; + + while(*inpos) { + unsigned int ch; + if(sscanf(inpos, "%2x", &ch) < 1) + break; + *outpos = ch; + outpos += 1; inpos += 2; + } + + return outpos - s; +} + +static VTermModifier strpe_modifiers(char **strp) +{ + VTermModifier state = 0; + + while((*strp)[0]) { + switch(((*strp)++)[0]) { + case 'S': state |= VTERM_MOD_SHIFT; break; + case 'C': state |= VTERM_MOD_CTRL; break; + case 'A': state |= VTERM_MOD_ALT; break; + default: return state; + } + } + + return state; +} + +static VTermKey strp_key(char *str) +{ + static struct { + char *name; + VTermKey key; + } keys[] = { + { "Up", VTERM_KEY_UP }, + { "Tab", VTERM_KEY_TAB }, + { "Enter", VTERM_KEY_ENTER }, + { "KP0", VTERM_KEY_KP_0 }, + { "F1", VTERM_KEY_FUNCTION(1) }, + { NULL, VTERM_KEY_NONE }, + }; + int i; + + for(i = 0; keys[i].name; i++) { + if(streq(str, keys[i].name)) + return keys[i].key; + } + + return VTERM_KEY_NONE; +} + +static void print_color(const VTermColor *col) +{ + if (VTERM_COLOR_IS_RGB(col)) { + printf("rgb(%d,%d,%d", col->red, col->green, col->blue); + } + else if (VTERM_COLOR_IS_INDEXED(col)) { + printf("idx(%d", col->index); + } + else { + printf("invalid(%d", col->type); + } + if (VTERM_COLOR_IS_DEFAULT_FG(col)) { + printf(",is_default_fg"); + } + if (VTERM_COLOR_IS_DEFAULT_BG(col)) { + printf(",is_default_bg"); + } + printf(")"); +} + +static VTerm *vt; +static VTermState *state; +static VTermScreen *screen; + +static VTermEncodingInstance encoding; + +static void term_output(const char *s, size_t len, void *user UNUSED) +{ + size_t i; + + printf("output "); + for(i = 0; i < len; i++) + printf("%x%s", (unsigned char)s[i], i < len-1 ? "," : "\n"); +} + +static void printhex(const char *s, size_t len) +{ + while(len--) + printf("%02x", (uint8_t)(s++)[0]); +} + +static int parser_text(const char bytes[], size_t len, void *user UNUSED) +{ + size_t i; + + printf("text "); + for(i = 0; i < len; i++) { + unsigned char b = bytes[i]; + if(b < 0x20 || b == 0x7f || (b >= 0x80 && b < 0xa0)) + break; + printf(i ? ",%x" : "%x", b); + } + printf("\n"); + + return i; +} + +static int parser_control(unsigned char control, void *user UNUSED) +{ + printf("control %02x\n", control); + + return 1; +} + +static int parser_escape(const char bytes[], size_t len, void *user UNUSED) +{ + if(bytes[0] >= 0x20 && bytes[0] < 0x30) { + if(len < 2) + return -1; + len = 2; + } + else { + len = 1; + } + + printf("escape "); + printhex(bytes, len); + printf("\n"); + + return len; +} + +static int parser_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user UNUSED) +{ + int i; + printf("csi %02x", command); + + if(leader && leader[0]) { + printf(" L="); + for(i = 0; leader[i]; i++) + printf("%02x", leader[i]); + } + + for(i = 0; i < argcount; i++) { + char sep = i ? ',' : ' '; + + if(args[i] == CSI_ARG_MISSING) + printf("%c*", sep); + else + printf("%c%ld%s", sep, CSI_ARG(args[i]), CSI_ARG_HAS_MORE(args[i]) ? "+" : ""); + } + + if(intermed && intermed[0]) { + printf(" I="); + for(i = 0; intermed[i]; i++) + printf("%02x", intermed[i]); + } + + printf("\n"); + + return 1; +} + +static int parser_osc(int command, VTermStringFragment frag, void *user UNUSED) +{ + + printf("osc "); + + if(frag.initial) { + if(command == -1) + printf("["); + else + printf("[%d;", command); + } + + printhex(frag.str, frag.len); + + if(frag.final) + printf("]"); + + printf("\n"); + + return 1; +} + +static int parser_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user UNUSED) +{ + printf("dcs "); + + if(frag.initial) { + size_t i; + printf("["); + for(i = 0; i < commandlen; i++) + printf("%02x", command[i]); + } + + printhex(frag.str, frag.len); + + if(frag.final) + printf("]"); + + printf("\n"); + + return 1; +} + +static int parser_apc(VTermStringFragment frag, void *user UNUSED) +{ + printf("apc "); + + if(frag.initial) + printf("["); + + printhex(frag.str, frag.len); + + if(frag.final) + printf("]"); + + printf("\n"); + + return 1; +} + +static int parser_pm(VTermStringFragment frag, void *user UNUSED) +{ + printf("pm "); + + if(frag.initial) + printf("["); + + printhex(frag.str, frag.len); + + if(frag.final) + printf("]"); + + printf("\n"); + + return 1; +} + +static int parser_sos(VTermStringFragment frag, void *user UNUSED) +{ + printf("sos "); + + if(frag.initial) + printf("["); + + printhex(frag.str, frag.len); + + if(frag.final) + printf("]"); + + printf("\n"); + + return 1; +} + +static VTermParserCallbacks parser_cbs = { + parser_text, // text + parser_control, // control + parser_escape, // escape + parser_csi, // csi + parser_osc, // osc + parser_dcs, // dcs + parser_apc, // apc + parser_pm, // pm + parser_sos, // sos + NULL // resize +}; + +static VTermStateFallbacks fallbacks = { + parser_control, // control + parser_csi, // csi + parser_osc, // osc + parser_dcs, // dcs + parser_apc, // dcs + parser_pm, // pm + parser_sos // sos +}; + +/* These callbacks are shared by State and Screen */ + +static int want_movecursor = 0; +static VTermPos state_pos; +static int movecursor(VTermPos pos, VTermPos oldpos UNUSED, int visible UNUSED, void *user UNUSED) +{ + state_pos = pos; + + if(want_movecursor) + printf("movecursor %d,%d\n", pos.row, pos.col); + + return 1; +} + +static int want_scrollrect = 0; +static int scrollrect(VTermRect rect, int downward, int rightward, void *user UNUSED) +{ + if(!want_scrollrect) + return 0; + + printf("scrollrect %d..%d,%d..%d => %+d,%+d\n", + rect.start_row, rect.end_row, rect.start_col, rect.end_col, + downward, rightward); + + return 1; +} + +static int want_moverect = 0; +static int moverect(VTermRect dest, VTermRect src, void *user UNUSED) +{ + if(!want_moverect) + return 0; + + printf("moverect %d..%d,%d..%d -> %d..%d,%d..%d\n", + src.start_row, src.end_row, src.start_col, src.end_col, + dest.start_row, dest.end_row, dest.start_col, dest.end_col); + + return 1; +} + +static int want_settermprop = 0; +static int settermprop(VTermProp prop, VTermValue *val, void *user UNUSED) +{ + VTermValueType type; + if(!want_settermprop) + return 1; + + type = vterm_get_prop_type(prop); + switch(type) { + case VTERM_VALUETYPE_BOOL: + printf("settermprop %d %s\n", prop, val->boolean ? "true" : "false"); + return 1; + case VTERM_VALUETYPE_INT: + printf("settermprop %d %d\n", prop, val->number); + return 1; + case VTERM_VALUETYPE_STRING: + printf("settermprop %d %s\"%.*s\"%s\n", prop, + val->string.initial ? "[" : "", val->string.len, val->string.str, val->string.final ? "]" : ""); + return 1; + case VTERM_VALUETYPE_COLOR: + printf("settermprop %d ", prop); + print_color(&val->color); + printf("\n"); + return 1; + + case VTERM_N_VALUETYPES: + return 0; + } + + return 0; +} + +// These callbacks are for State + +static int want_state_putglyph = 0; +static int state_putglyph(VTermGlyphInfo *info, VTermPos pos, void *user UNUSED) +{ + int i; + if(!want_state_putglyph) + return 1; + + printf("putglyph "); + for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++) + printf(i ? ",%x" : "%x", info->chars[i]); + printf(" %d %d,%d", info->width, pos.row, pos.col); + if(info->protected_cell) + printf(" prot"); + if(info->dwl) + printf(" dwl"); + if(info->dhl) + printf(" dhl-%s", info->dhl == 1 ? "top" : info->dhl == 2 ? "bottom" : "?" ); + printf("\n"); + + return 1; +} + +static int want_state_erase = 0; +static int state_erase(VTermRect rect, int selective, void *user UNUSED) +{ + if(!want_state_erase) + return 1; + + printf("erase %d..%d,%d..%d%s\n", + rect.start_row, rect.end_row, rect.start_col, rect.end_col, + selective ? " selective" : ""); + + return 1; +} + +static struct { + int bold; + int underline; + int italic; + int blink; + int reverse; + int conceal; + int strike; + int font; + int small; + int baseline; + VTermColor foreground; + VTermColor background; +} state_pen; +static int state_setpenattr(VTermAttr attr, VTermValue *val, void *user UNUSED) +{ + switch(attr) { + case VTERM_ATTR_BOLD: + state_pen.bold = val->boolean; + break; + case VTERM_ATTR_UNDERLINE: + state_pen.underline = val->number; + break; + case VTERM_ATTR_ITALIC: + state_pen.italic = val->boolean; + break; + case VTERM_ATTR_BLINK: + state_pen.blink = val->boolean; + break; + case VTERM_ATTR_REVERSE: + state_pen.reverse = val->boolean; + break; + case VTERM_ATTR_CONCEAL: + state_pen.conceal = val->boolean; + break; + case VTERM_ATTR_STRIKE: + state_pen.strike = val->boolean; + break; + case VTERM_ATTR_FONT: + state_pen.font = val->number; + break; + case VTERM_ATTR_SMALL: + state_pen.small = val->boolean; + break; + case VTERM_ATTR_BASELINE: + state_pen.baseline = val->number; + break; + case VTERM_ATTR_FOREGROUND: + state_pen.foreground = val->color; + break; + case VTERM_ATTR_BACKGROUND: + state_pen.background = val->color; + break; + + case VTERM_N_ATTRS: + return 0; + } + + return 1; +} + +static int state_setlineinfo(int row UNUSED, const VTermLineInfo *newinfo UNUSED, const VTermLineInfo *oldinfo UNUSED, void *user UNUSED) +{ + return 1; +} + +static int want_state_scrollback = 0; +static int state_sb_clear(void *user UNUSED) { + if(!want_state_scrollback) + return 1; + + printf("sb_clear\n"); + return 0; +} + +VTermStateCallbacks state_cbs = { + state_putglyph, // putglyph + movecursor, // movecursor + scrollrect, // scrollrect + moverect, // moverect + state_erase, // erase + NULL, // initpen + state_setpenattr, // setpenattr + settermprop, // settermprop + NULL, // bell + NULL, // resize + state_setlineinfo, // setlineinfo + state_sb_clear, // sb_clear +}; + +static int selection_set(VTermSelectionMask mask, VTermStringFragment frag, void *user UNUSED) +{ + printf("selection-set mask=%04X ", mask); + if(frag.initial) + printf("["); + printhex(frag.str, frag.len); + if(frag.final) + printf("]"); + printf("\n"); + + return 1; +} + +static int selection_query(VTermSelectionMask mask, void *user UNUSED) +{ + printf("selection-query mask=%04X\n", mask); + + return 1; +} + +VTermSelectionCallbacks selection_cbs = { + .set = selection_set, + .query = selection_query, +}; + +static int want_screen_damage = 0; +static int want_screen_damage_cells = 0; +static int screen_damage(VTermRect rect, void *user UNUSED) +{ + if(!want_screen_damage) + return 1; + + printf("damage %d..%d,%d..%d", + rect.start_row, rect.end_row, rect.start_col, rect.end_col); + + if(want_screen_damage_cells) { + int equals = FALSE; + int row; + int col; + + for(row = rect.start_row; row < rect.end_row; row++) { + int eol = rect.end_col; + while(eol > rect.start_col) { + VTermScreenCell cell; + VTermPos pos; + pos.row = row; + pos.col = eol-1; + vterm_screen_get_cell(screen, pos, &cell); + if(cell.chars[0]) + break; + + eol--; + } + + if(eol == rect.start_col) + break; + + if(!equals) + printf(" ="), equals = TRUE; + + printf(" %d<", row); + for(col = rect.start_col; col < eol; col++) { + VTermScreenCell cell; + VTermPos pos; + pos.row = row; + pos.col = col; + vterm_screen_get_cell(screen, pos, &cell); + printf(col == rect.start_col ? "%02X" : " %02X", cell.chars[0]); + } + printf(">"); + } + } + + printf("\n"); + + return 1; +} + +static int want_screen_scrollback = 0; +static int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user UNUSED) +{ + int eol; + int c; + + if(!want_screen_scrollback) + return 1; + + eol = cols; + while(eol && !cells[eol-1].chars[0]) + eol--; + + printf("sb_pushline %d =", cols); + for(c = 0; c < eol; c++) + printf(" %02X", cells[c].chars[0]); + printf("\n"); + + return 1; +} + +static int screen_sb_popline(int cols, VTermScreenCell *cells, void *user UNUSED) +{ + int col; + + if(!want_screen_scrollback) + return 0; + + // All lines of scrollback contain "ABCDE" + for(col = 0; col < cols; col++) { + if(col < 5) + cells[col].chars[0] = 'A' + col; + else + cells[col].chars[0] = 0; + + cells[col].width = 1; + } + + printf("sb_popline %d\n", cols); + return 1; +} + +static int screen_sb_clear(void *user UNUSED) +{ + if(!want_screen_scrollback) + return 1; + + printf("sb_clear\n"); + return 0; +} + +VTermScreenCallbacks screen_cbs = { + screen_damage, // damage + moverect, // moverect + movecursor, // movecursor + settermprop, // settermprop + NULL, // bell + NULL, // resize + screen_sb_pushline, // sb_pushline + screen_sb_popline, // sb_popline + screen_sb_clear, // sb_clear +}; + +int main(int argc UNUSED, char **argv UNUSED) +{ + char line[1024] = {0}; + int flag; + + int err; + + setvbuf(stdout, NULL, _IONBF, 0); + + while(fgets(line, sizeof line, stdin)) { + char *nl; + size_t outlen; + err = 0; + + if((nl = strchr(line, '\n'))) + *nl = '\0'; + + if(streq(line, "INIT")) { + if(!vt) + vt = vterm_new(25, 80); + + // Somehow this makes tests fail + // vterm_output_set_callback(vt, term_output, NULL); + } + + else if(streq(line, "WANTPARSER")) { + assert(vt); + vterm_parser_set_callbacks(vt, &parser_cbs, NULL); + } + + else if(strstartswith(line, "WANTSTATE") && (line[9] == '\0' || line[9] == ' ')) { + int i = 9; + int sense = 1; + assert(vt); + if(!state) { + state = vterm_obtain_state(vt); + vterm_state_set_callbacks(state, &state_cbs, NULL); + vterm_state_set_selection_callbacks(state, &selection_cbs, NULL, NULL, 1024); + vterm_state_set_bold_highbright(state, 1); + vterm_state_reset(state, 1); + } + + while(line[i] == ' ') + i++; + for( ; line[i]; i++) + switch(line[i]) { + case '+': + sense = 1; + break; + case '-': + sense = 0; + break; + case 'g': + want_state_putglyph = sense; + break; + case 's': + want_scrollrect = sense; + break; + case 'm': + want_moverect = sense; + break; + case 'e': + want_state_erase = sense; + break; + case 'p': + want_settermprop = sense; + break; + case 'f': + vterm_state_set_unrecognised_fallbacks(state, sense ? &fallbacks : NULL, NULL); + break; + case 'b': + want_state_scrollback = sense; + break; + default: + fprintf(stderr, "Unrecognised WANTSTATE flag '%c'\n", line[i]); + } + } + + else if(strstartswith(line, "WANTSCREEN") && (line[10] == '\0' || line[10] == ' ')) { + int i = 10; + int sense = 1; + assert(vt); + if(!screen) + screen = vterm_obtain_screen(vt); + vterm_screen_set_callbacks(screen, &screen_cbs, NULL); + + while(line[i] == ' ') + i++; + for( ; line[i]; i++) + switch(line[i]) { + case '-': + sense = 0; + break; + case 'a': + vterm_screen_enable_altscreen(screen, 1); + break; + case 'd': + want_screen_damage = sense; + break; + case 'D': + want_screen_damage = sense; + want_screen_damage_cells = sense; + break; + case 'm': + want_moverect = sense; + break; + case 'c': + want_movecursor = sense; + break; + case 'p': + want_settermprop = 1; + break; + case 'b': + want_screen_scrollback = sense; + break; + case 'r': + vterm_screen_enable_reflow(screen, sense); + break; + default: + fprintf(stderr, "Unrecognised WANTSCREEN flag '%c'\n", line[i]); + } + } + + else if(sscanf(line, "UTF8 %d", &flag)) { + vterm_set_utf8(vt, flag); + } + + else if(streq(line, "RESET")) { + if(state) { + vterm_state_reset(state, 1); + vterm_state_get_cursorpos(state, &state_pos); + } + if(screen) { + vterm_screen_reset(screen, 1); + } + } + + else if(strstartswith(line, "RESIZE ")) { + int rows, cols; + char *linep = line + 7; + while(linep[0] == ' ') + linep++; + sscanf(linep, "%d, %d", &rows, &cols); + vterm_set_size(vt, rows, cols); + } + + else if(strstartswith(line, "PUSH ")) { + char *bytes = line + 5; + size_t len = inplace_hex2bytes(bytes); + assert(len); + + size_t written = vterm_input_write(vt, bytes, len); + if(written < len) + fprintf(stderr, "! short write\n"); + } + + else if(streq(line, "WANTENCODING")) { + // This isn't really external API but it's hard to get this out any + // other way + encoding.enc = vterm_lookup_encoding(ENC_UTF8, 'u'); + if(encoding.enc->init) + (*encoding.enc->init)(encoding.enc, encoding.data); + } + + else if(strstartswith(line, "ENCIN ")) { + char *bytes = line + 6; + size_t len = inplace_hex2bytes(bytes); + assert(len); + + uint32_t cp[1024]; + int cpi = 0; + size_t pos = 0; + + (*encoding.enc->decode)(encoding.enc, encoding.data, + cp, &cpi, len, bytes, &pos, len); + + if(cpi > 0) { + int i; + printf("encout "); + for(i = 0; i < cpi; i++) { + printf(i ? ",%x" : "%x", cp[i]); + } + printf("\n"); + } + } + + else if(strstartswith(line, "INCHAR ")) { + char *linep = line + 7; + unsigned int c = 0; + VTermModifier mod; + while(linep[0] == ' ') + linep++; + mod = strpe_modifiers(&linep); + sscanf(linep, " %x", &c); + + vterm_keyboard_unichar(vt, c, mod); + } + + else if(strstartswith(line, "INKEY ")) { + VTermModifier mod; + VTermKey key; + char *linep = line + 6; + while(linep[0] == ' ') + linep++; + mod = strpe_modifiers(&linep); + while(linep[0] == ' ') + linep++; + key = strp_key(linep); + + vterm_keyboard_key(vt, key, mod); + } + + else if(strstartswith(line, "PASTE ")) { + char *linep = line + 6; + if(streq(linep, "START")) + vterm_keyboard_start_paste(vt); + else if(streq(linep, "END")) + vterm_keyboard_end_paste(vt); + else + goto abort_line; + } + + else if(strstartswith(line, "FOCUS ")) { + assert(state); + char *linep = line + 6; + if(streq(linep, "IN")) + vterm_state_focus_in(state); + else if(streq(linep, "OUT")) + vterm_state_focus_out(state); + else + goto abort_line; + } + + else if(strstartswith(line, "MOUSEMOVE ")) { + char *linep = line + 10; + int row, col, len; + VTermModifier mod; + while(linep[0] == ' ') + linep++; + sscanf(linep, "%d,%d%n", &row, &col, &len); + linep += len; + while(linep[0] == ' ') + linep++; + mod = strpe_modifiers(&linep); + vterm_mouse_move(vt, row, col, mod); + } + + else if(strstartswith(line, "MOUSEBTN ")) { + char *linep = line + 9; + char press; + int button, len; + VTermModifier mod; + while(linep[0] == ' ') + linep++; + sscanf(linep, "%c %d%n", &press, &button, &len); + linep += len; + while(linep[0] == ' ') + linep++; + mod = strpe_modifiers(&linep); + vterm_mouse_button(vt, button, (press == 'd' || press == 'D'), mod); + } + + else if(strstartswith(line, "SELECTION ")) { + char *linep = line + 10; + unsigned int mask; + int len; + VTermStringFragment frag = { 0 }; + sscanf(linep, "%x%n", &mask, &len); + linep += len; + while(linep[0] == ' ') + linep++; + if(linep[0] == '[') { + frag.initial = TRUE; + linep++; + while(linep[0] == ' ') + linep++; + } + frag.len = inplace_hex2bytes(linep); + frag.str = linep; + assert(frag.len); + + linep += frag.len * 2; + while(linep[0] == ' ') + linep++; + if(linep[0] == ']') { + frag.final = TRUE; + } + vterm_state_send_selection(state, mask, frag); + } + + else if(strstartswith(line, "DAMAGEMERGE ")) { + assert(screen); + char *linep = line + 12; + while(linep[0] == ' ') + linep++; + if(streq(linep, "CELL")) + vterm_screen_set_damage_merge(screen, VTERM_DAMAGE_CELL); + else if(streq(linep, "ROW")) + vterm_screen_set_damage_merge(screen, VTERM_DAMAGE_ROW); + else if(streq(linep, "SCREEN")) + vterm_screen_set_damage_merge(screen, VTERM_DAMAGE_SCREEN); + else if(streq(linep, "SCROLL")) + vterm_screen_set_damage_merge(screen, VTERM_DAMAGE_SCROLL); + } + + else if(strstartswith(line, "DAMAGEFLUSH")) { + assert(screen); + vterm_screen_flush_damage(screen); + } + + else if(line[0] == '?') { + if(streq(line, "?cursor")) { + assert(state); + VTermPos pos; + vterm_state_get_cursorpos(state, &pos); + if(pos.row != state_pos.row) + printf("! row mismatch: state=%d,%d event=%d,%d\n", + pos.row, pos.col, state_pos.row, state_pos.col); + else if(pos.col != state_pos.col) + printf("! col mismatch: state=%d,%d event=%d,%d\n", + pos.row, pos.col, state_pos.row, state_pos.col); + else + printf("%d,%d\n", state_pos.row, state_pos.col); + } + else if(strstartswith(line, "?pen ")) { + assert(state); + VTermValue val; + char *linep = line + 5; + while(linep[0] == ' ') + linep++; + +#define BOOLSTR(v) ((v) ? "on" : "off") + + if(streq(linep, "bold")) { + vterm_state_get_penattr(state, VTERM_ATTR_BOLD, &val); + if(val.boolean != state_pen.bold) + printf("! pen bold mismatch; state=%s, event=%s\n", + BOOLSTR(val.boolean), BOOLSTR(state_pen.bold)); + else + printf("%s\n", BOOLSTR(state_pen.bold)); + } + else if(streq(linep, "underline")) { + vterm_state_get_penattr(state, VTERM_ATTR_UNDERLINE, &val); + if(val.boolean != state_pen.underline) + printf("! pen underline mismatch; state=%d, event=%d\n", + val.boolean, state_pen.underline); + else + printf("%d\n", state_pen.underline); + } + else if(streq(linep, "italic")) { + vterm_state_get_penattr(state, VTERM_ATTR_ITALIC, &val); + if(val.boolean != state_pen.italic) + printf("! pen italic mismatch; state=%s, event=%s\n", + BOOLSTR(val.boolean), BOOLSTR(state_pen.italic)); + else + printf("%s\n", BOOLSTR(state_pen.italic)); + } + else if(streq(linep, "blink")) { + vterm_state_get_penattr(state, VTERM_ATTR_BLINK, &val); + if(val.boolean != state_pen.blink) + printf("! pen blink mismatch; state=%s, event=%s\n", + BOOLSTR(val.boolean), BOOLSTR(state_pen.blink)); + else + printf("%s\n", BOOLSTR(state_pen.blink)); + } + else if(streq(linep, "reverse")) { + vterm_state_get_penattr(state, VTERM_ATTR_REVERSE, &val); + if(val.boolean != state_pen.reverse) + printf("! pen reverse mismatch; state=%s, event=%s\n", + BOOLSTR(val.boolean), BOOLSTR(state_pen.reverse)); + else + printf("%s\n", BOOLSTR(state_pen.reverse)); + } + else if(streq(linep, "font")) { + vterm_state_get_penattr(state, VTERM_ATTR_FONT, &val); + if(val.boolean != state_pen.font) + printf("! pen font mismatch; state=%d, event=%d\n", + val.boolean, state_pen.font); + else + printf("%d\n", state_pen.font); + } + else if(streq(linep, "small")) { + vterm_state_get_penattr(state, VTERM_ATTR_SMALL, &val); + if(val.boolean != state_pen.small) + printf("! pen small mismatch; state=%s, event=%s\n", + BOOLSTR(val.boolean), BOOLSTR(state_pen.small)); + else + printf("%s\n", BOOLSTR(state_pen.small)); + } + else if(streq(linep, "baseline")) { + vterm_state_get_penattr(state, VTERM_ATTR_BASELINE, &val); + if(val.number != state_pen.baseline) + printf("! pen baseline mismatch: state=%d, event=%d\n", + val.number, state_pen.baseline); + else + printf("%s\n", state_pen.baseline == VTERM_BASELINE_RAISE ? "raise" + : state_pen.baseline == VTERM_BASELINE_LOWER ? "lower" + : "normal"); + } + else if(streq(linep, "foreground")) { + print_color(&state_pen.foreground); + printf("\n"); + } + else if(streq(linep, "background")) { + print_color(&state_pen.background); + printf("\n"); + } + else + printf("?\n"); + } + else if(strstartswith(line, "?lineinfo ")) { + assert(state); + char *linep = line + 10; + int row; + const VTermLineInfo *info; + while(linep[0] == ' ') + linep++; + if(sscanf(linep, "%d", &row) < 1) { + printf("! lineinfo unrecognised input\n"); + goto abort_line; + } + info = vterm_state_get_lineinfo(state, row); + if(info->doublewidth) + printf("dwl "); + if(info->doubleheight) + printf("dhl "); + if(info->continuation) + printf("cont "); + printf("\n"); + } + else if(strstartswith(line, "?screen_chars ")) { + assert(screen); + char *linep = line + 13; + VTermRect rect; + size_t len; + while(linep[0] == ' ') + linep++; + if(sscanf(linep, "%d,%d,%d,%d", &rect.start_row, &rect.start_col, &rect.end_row, &rect.end_col) == 4) + ; // fine + else if(sscanf(linep, "%d", &rect.start_row) == 1) { + rect.end_row = rect.start_row + 1; + rect.start_col = 0; + vterm_get_size(vt, NULL, &rect.end_col); + } + else { + printf("! screen_chars unrecognised input\n"); + goto abort_line; + } + len = vterm_screen_get_chars(screen, NULL, 0, rect); + if(len == (size_t)-1) + printf("! screen_chars error\n"); + else if(len == 0) + printf("\n"); + else { + uint32_t *chars = malloc(sizeof(uint32_t) * len); + size_t i; + vterm_screen_get_chars(screen, chars, len, rect); + for(i = 0; i < len; i++) { + printf("0x%02x%s", chars[i], i < len-1 ? "," : "\n"); + } + free(chars); + } + } + else if(strstartswith(line, "?screen_text ")) { + assert(screen); + char *linep = line + 12; + VTermRect rect; + size_t len; + while(linep[0] == ' ') + linep++; + if(sscanf(linep, "%d,%d,%d,%d", &rect.start_row, &rect.start_col, &rect.end_row, &rect.end_col) < 4) { + printf("! screen_text unrecognised input\n"); + goto abort_line; + } + len = vterm_screen_get_text(screen, NULL, 0, rect); + if(len == (size_t)-1) + printf("! screen_text error\n"); + else if(len == 0) + printf("\n"); + else { + // Put an overwrite guard at both ends of the buffer + unsigned char *buffer = malloc(len + 4); + unsigned char *text = buffer + 2; + text[-2] = 0x55; text[-1] = 0xAA; + text[len] = 0x55; text[len+1] = 0xAA; + + vterm_screen_get_text(screen, (char *)text, len, rect); + + if(text[-2] != 0x55 || text[-1] != 0xAA) + printf("! screen_get_text buffer overrun left [%02x,%02x]\n", text[-2], text[-1]); + else if(text[len] != 0x55 || text[len+1] != 0xAA) + printf("! screen_get_text buffer overrun right [%02x,%02x]\n", text[len], text[len+1]); + else + { + size_t i; + for(i = 0; i < len; i++) { + printf("0x%02x%s", text[i], i < len-1 ? "," : "\n"); + } + } + + free(buffer); + } + } + else if(strstartswith(line, "?screen_cell ")) { + assert(screen); + char *linep = line + 12; + int i; + VTermPos pos; + VTermScreenCell cell; + while(linep[0] == ' ') + linep++; + if(sscanf(linep, "%d,%d\n", &pos.row, &pos.col) < 2) { + printf("! screen_cell unrecognised input\n"); + goto abort_line; + } + if(!vterm_screen_get_cell(screen, pos, &cell)) + goto abort_line; + printf("{"); + for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell.chars[i]; i++) { + printf("%s0x%x", i ? "," : "", cell.chars[i]); + } + printf("} width=%d attrs={", cell.width); + if(cell.attrs.bold) printf("B"); + if(cell.attrs.underline) printf("U%d", cell.attrs.underline); + if(cell.attrs.italic) printf("I"); + if(cell.attrs.blink) printf("K"); + if(cell.attrs.reverse) printf("R"); + if(cell.attrs.font) printf("F%d", cell.attrs.font); + if(cell.attrs.small) printf("S"); + if(cell.attrs.baseline) printf( + cell.attrs.baseline == VTERM_BASELINE_RAISE ? "^" : + "_"); + printf("} "); + if(cell.attrs.dwl) printf("dwl "); + if(cell.attrs.dhl) printf("dhl-%s ", cell.attrs.dhl == 2 ? "bottom" : "top"); + printf("fg="); + vterm_screen_convert_color_to_rgb(screen, &cell.fg); + print_color(&cell.fg); + printf(" bg="); + vterm_screen_convert_color_to_rgb(screen, &cell.bg); + print_color(&cell.bg); + printf("\n"); + } + else if(strstartswith(line, "?screen_eol ")) { + assert(screen); + VTermPos pos; + char *linep = line + 12; + while(linep[0] == ' ') + linep++; + if(sscanf(linep, "%d,%d\n", &pos.row, &pos.col) < 2) { + printf("! screen_eol unrecognised input\n"); + goto abort_line; + } + printf("%d\n", vterm_screen_is_eol(screen, pos)); + } + else if(strstartswith(line, "?screen_attrs_extent ")) { + assert(screen); + VTermPos pos; + VTermRect rect; + char *linep = line + 21; + while(linep[0] == ' ') + linep++; + if(sscanf(linep, "%d,%d\n", &pos.row, &pos.col) < 2) { + printf("! screen_attrs_extent unrecognised input\n"); + goto abort_line; + } + rect.start_col = 0; + rect.end_col = -1; + if(!vterm_screen_get_attrs_extent(screen, &rect, pos, ~0)) { + printf("! screen_attrs_extent failed\n"); + goto abort_line; + } + printf("%d,%d-%d,%d\n", rect.start_row, rect.start_col, rect.end_row, rect.end_col); + } + else + printf("?\n"); + + memset(line, 0, sizeof line); + continue; + } + + else + abort_line: err = 1; + + outlen = vterm_output_get_buffer_current(vt); + if(outlen > 0) { + char outbuff[1024]; + vterm_output_read(vt, outbuff, outlen); + + term_output(outbuff, outlen, NULL); + } + + printf(err ? "?\n" : "DONE\n"); + } + + vterm_free(vt); + + return 0; +} diff --git a/src/libvterm/t/run-test.pl b/src/libvterm/t/run-test.pl new file mode 100644 index 0000000..3440465 --- /dev/null +++ b/src/libvterm/t/run-test.pl @@ -0,0 +1,233 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Getopt::Long; +use IO::Handle; +use IPC::Open2 qw( open2 ); +use POSIX qw( WIFEXITED WEXITSTATUS WIFSIGNALED WTERMSIG ); + +my $VALGRIND = 0; +my $EXECUTABLE = "t/harness"; +GetOptions( + 'valgrind|v+' => \$VALGRIND, + 'executable|e=s' => \$EXECUTABLE, + 'fail-early|F' => \(my $FAIL_EARLY), +) or exit 1; + +my ( $hin, $hout, $hpid ); +{ + my @command = $EXECUTABLE; + unshift @command, "valgrind", "--tool=memcheck", "--leak-check=yes", "--num-callers=25", "--log-file=valgrind.out", "--error-exitcode=126" if $VALGRIND; + + $hpid = open2 $hout, $hin, @command or die "Cannot open2 harness - $!"; +} + +my $exitcode = 0; + +my $command; +my @expect; + +my $linenum = 0; + +sub do_onetest +{ + $hin->print( "$command\n" ); + undef $command; + + my $fail_printed = 0; + + while( my $outline = <$hout> ) { + last if $outline eq "DONE\n" or $outline eq "?\n"; + + chomp $outline; + + if( !@expect ) { + print "# line $linenum: Test failed\n" unless $fail_printed++; + print "# expected nothing more\n" . + "# Actual: $outline\n"; + next; + } + + my $expectation = shift @expect; + + next if $expectation eq $outline; + + print "# line $linenum: Test failed\n" unless $fail_printed++; + print "# Expected: $expectation\n" . + "# Actual: $outline\n"; + } + + if( @expect ) { + print "# line $linenum: Test failed\n" unless $fail_printed++; + print "# Expected: $_\n" . + "# didn't happen\n" for @expect; + } + + $exitcode = 1 if $fail_printed; + exit $exitcode if $exitcode and $FAIL_EARLY; +} + +sub do_line +{ + my ( $line ) = @_; + + if( $line =~ m/^!(.*)/ ) { + do_onetest if defined $command; + print "> $1\n"; + } + + # Commands have capitals + elsif( $line =~ m/^([A-Z]+)/ ) { + # Some convenience formatting + if( $line =~ m/^(PUSH|ENCIN) (.*)$/ ) { + # we're evil + my $string = eval($2); + $line = "$1 " . unpack "H*", $string; + } + elsif( $line =~ m/^(SELECTION \d+) +(\[?)(.*?)(\]?)$/ ) { + # we're evil + my $string = eval($3); + $line = "$1 $2 " . unpack( "H*", $string ) . " $4"; + } + + do_onetest if defined $command; + + $command = $line; + undef @expect; + } + # Expectations have lowercase + elsif( $line =~ m/^([a-z]+)/ ) { + # Convenience formatting + if( $line =~ m/^(text|encout) (.*)$/ ) { + $line = "$1 " . join ",", map sprintf("%x", $_), eval($2); + } + elsif( $line =~ m/^(output) (.*)$/ ) { + $line = "$1 " . join ",", map sprintf("%x", $_), unpack "C*", eval($2); + } + elsif( $line =~ m/^control (.*)$/ ) { + $line = sprintf "control %02x", eval($1); + } + elsif( $line =~ m/^csi (\S+) (.*)$/ ) { + $line = sprintf "csi %02x %s", eval($1), $2; # TODO + } + elsif( $line =~ m/^(osc) (\[\d+)? *(.*?)(\]?)$/ ) { + my ( $cmd, $initial, $data, $final ) = ( $1, $2, $3, $4 ); + $initial //= ""; + $initial .= ";" if $initial =~ m/\d+/; + + $line = "$cmd $initial" . join( "", map sprintf("%02x", $_), unpack "C*", length $data ? eval($data) : "" ) . "$final"; + } + elsif( $line =~ m/^(escape|dcs|apc|pm|sos) (\[?)(.*?)(\]?)$/ ) { + $line = "$1 $2" . join( "", map sprintf("%02x", $_), unpack "C*", length $3 ? eval($3) : "" ) . "$4"; + } + elsif( $line =~ m/^putglyph (\S+) (.*)$/ ) { + $line = "putglyph " . join( ",", map sprintf("%x", $_), eval($1) ) . " $2"; + } + elsif( $line =~ m/^(?:movecursor|scrollrect|moverect|erase|damage|sb_pushline|sb_popline|sb_clear|settermprop|setmousefunc|selection-query) ?/ ) { + # no conversion + } + elsif( $line =~ m/^(selection-set) (.*?) (\[?)(.*?)(\]?)$/ ) { + $line = "$1 $2 $3" . join( "", map sprintf("%02x", $_), unpack "C*", eval($4) ) . "$5"; + } + else { + warn "Unrecognised test expectation '$line'\n"; + } + + push @expect, $line; + } + # ?screen_row assertion is emulated here + elsif( $line =~ s/^\?screen_row\s+(\d+)\s*=\s*// ) { + my $row = $1; + my $want; + + if( $line =~ m/^"/ ) { + $want = eval($line); + } + else { + # Turn 0xDD,0xDD,... directly into bytes + $want = pack "C*", map { hex } split m/,/, $line; + } + + do_onetest if defined $command; + + $hin->print( "\?screen_chars $row\n" ); + my $response = <$hout>; + chomp $response; + + $response = pack "C*", map { hex } split m/,/, $response; + if( $response ne $want ) { + print "# line $linenum: Assert ?screen_row $row failed:\n" . + "# Expected: $want\n" . + "# Actual: $response\n"; + $exitcode = 1; + exit $exitcode if $exitcode and $FAIL_EARLY; + } + } + # Assertions start with '?' + elsif( $line =~ s/^\?([a-z]+.*?=)\s*// ) { + do_onetest if defined $command; + + my ( $assertion ) = $1 =~ m/^(.*)\s+=/; + my $expectation = $line; + + $hin->print( "\?$assertion\n" ); + my $response = <$hout>; defined $response or wait, die "Test harness failed - $?\n"; + chomp $response; $response =~ s/^\s+|\s+$//g; + + # Some convenience formatting + if( $assertion =~ m/^screen_chars/ and $expectation =~ m/^"/ ) { + $expectation = join ",", map sprintf("0x%02x", ord $_), split m//, eval($expectation); + } + + if( $response ne $expectation ) { + print "# line $linenum: Assert $assertion failed:\n" . + "# Expected: $expectation\n" . + "# Actual: $response\n"; + $exitcode = 1; + exit $exitcode if $exitcode and $FAIL_EARLY; + } + } + # Test controls start with '$' + elsif( $line =~ s/\$SEQ\s+(\d+)\s+(\d+):\s*// ) { + my ( $low, $high ) = ( $1, $2 ); + foreach my $val ( $low .. $high ) { + ( my $inner = $line ) =~ s/\\#/$val/g; + do_line( $inner ); + } + } + elsif( $line =~ s/\$REP\s+(\d+):\s*// ) { + my $count = $1; + do_line( $line ) for 1 .. $count; + } + else { + die "Unrecognised TEST line $line\n"; + } +} + +open my $test, "<", $ARGV[0] or die "Cannot open test script $ARGV[0] - $!"; + +while( my $line = <$test> ) { + $linenum++; + $line =~ s/^\s+//; + chomp $line; + + next if $line =~ m/^(?:#|$)/; + last if $line eq "__END__"; + + do_line( $line ); +} + +do_onetest if defined $command; + +close $hin; +close $hout; + +waitpid $hpid, 0; +if( $? ) { + printf STDERR "Harness exited %d\n", WEXITSTATUS($?) if WIFEXITED($?); + printf STDERR "Harness exit signal %d\n", WTERMSIG($?) if WIFSIGNALED($?); + $exitcode = WIFEXITED($?) ? WEXITSTATUS($?) : 125; +} + +exit $exitcode; |