summaryrefslogtreecommitdiffstats
path: root/src/libvterm/t
diff options
context:
space:
mode:
Diffstat (limited to 'src/libvterm/t')
-rw-r--r--src/libvterm/t/02parser.test200
-rw-r--r--src/libvterm/t/03encoding_utf8.test122
-rw-r--r--src/libvterm/t/10state_putglyph.test55
-rw-r--r--src/libvterm/t/11state_movecursor.test224
-rw-r--r--src/libvterm/t/12state_scroll.test150
-rw-r--r--src/libvterm/t/13state_edit.test300
-rw-r--r--src/libvterm/t/14state_encoding.test105
-rw-r--r--src/libvterm/t/15state_mode.test86
-rw-r--r--src/libvterm/t/16state_resize.test48
-rw-r--r--src/libvterm/t/17state_mouse.test172
-rw-r--r--src/libvterm/t/18state_termprops.test36
-rw-r--r--src/libvterm/t/20state_wrapping.test69
-rw-r--r--src/libvterm/t/21state_tabstops.test60
-rw-r--r--src/libvterm/t/22state_save.test64
-rw-r--r--src/libvterm/t/25state_input.test132
-rw-r--r--src/libvterm/t/26state_query.test62
-rw-r--r--src/libvterm/t/27state_reset.test32
-rw-r--r--src/libvterm/t/28state_dbl_wh.test61
-rw-r--r--src/libvterm/t/29state_fallback.test19
-rw-r--r--src/libvterm/t/30pen.test106
-rw-r--r--src/libvterm/t/40screen_ascii.test69
-rw-r--r--src/libvterm/t/41screen_unicode.test47
-rw-r--r--src/libvterm/t/42screen_damage.test155
-rw-r--r--src/libvterm/t/43screen_resize.test90
-rw-r--r--src/libvterm/t/44screen_pen.test55
-rw-r--r--src/libvterm/t/45screen_protect.test16
-rw-r--r--src/libvterm/t/46screen_extent.test11
-rw-r--r--src/libvterm/t/47screen_dbl_wh.test32
-rw-r--r--src/libvterm/t/48screen_termprops.test17
-rw-r--r--src/libvterm/t/90vttest_01-movement-1.test87
-rw-r--r--src/libvterm/t/90vttest_01-movement-2.test40
-rw-r--r--src/libvterm/t/90vttest_01-movement-3.test21
-rw-r--r--src/libvterm/t/90vttest_01-movement-4.test36
-rw-r--r--src/libvterm/t/90vttest_02-screen-1.test18
-rw-r--r--src/libvterm/t/90vttest_02-screen-2.test29
-rw-r--r--src/libvterm/t/90vttest_02-screen-3.test16
-rw-r--r--src/libvterm/t/90vttest_02-screen-4.test17
-rw-r--r--src/libvterm/t/92lp1640917.test13
-rw-r--r--src/libvterm/t/harness.c929
-rw-r--r--src/libvterm/t/run-test.pl196
40 files changed, 3997 insertions, 0 deletions
diff --git a/src/libvterm/t/02parser.test b/src/libvterm/t/02parser.test
new file mode 100644
index 0000000000..66d487dad9
--- /dev/null
+++ b/src/libvterm/t/02parser.test
@@ -0,0 +1,200 @@
+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 "\x9f"
+ control 0x9f
+
+!C1 7bit
+PUSH "\e\x43"
+ control 0x83
+
+PUSH "\e\x5f"
+ control 0x9f
+
+!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"
+
+!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"
+ control 10
+ osc "2;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"
+
+!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"
+ control 10
+ dcs "Bye"
+
+!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 0000000000..7ee16ac641
--- /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 0000000000..5665bce1dc
--- /dev/null
+++ b/src/libvterm/t/10state_putglyph.test
@@ -0,0 +1,55 @@
+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 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 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
+
+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 0000000000..c1d72b2a61
--- /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 Horizonal 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 0000000000..ca305d499b
--- /dev/null
+++ b/src/libvterm/t/12state_scroll.test
@@ -0,0 +1,150 @@
+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[10H"
+PUSH "\e[9;10r"
+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
diff --git a/src/libvterm/t/13state_edit.test b/src/libvterm/t/13state_edit.test
new file mode 100644
index 0000000000..b435655e8b
--- /dev/null
+++ b/src/libvterm/t/13state_edit.test
@@ -0,0 +1,300 @@
+INIT
+UTF8 1
+WANTSTATE se
+
+!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
+
+!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@"