AtlatestRepositorysigil-vt
1
;;; Conformance suite for (sigil vt) — the native VT core.2
;;;3
;;; A faithful port of slate's test/term-test.sgl (the emulator's behavior IS4
;;; the spec). Each fixture feeds a byte/escape sequence into a fresh emulator5
;;; and asserts the resulting grid text, cursor, attrs, or mode flags. Covers6
;;; the MUST tier: C0, CSI cursor/erase/insert/delete/scroll-region, SGR7
;;; 16/256/truecolor (+ colon sub-params), alt-screen, autowrap (incl. the8
;;; deferred-wrap edge), tabs, DECSC/DECRC, origin mode, DECALN, OSC title,9
;;; DECSCUSR, bracketed paste, DSR/DA replies, scrollback, resize, incremental10
;;; UTF-8 across chunk boundaries, the ground-state bulk-run fast path, the11
;;; trust boundary (hostile input), plus the native additions (events, mouse12
;;; flags, row-runs).14
(import (sigil core)15
(sigil test)16
(sigil vt))18
;; ---- helpers --------------------------------------------------------------19
(define ESC "\x1b;")20
(define (csi . parts) (apply string-append ESC "[" parts))22
(define (vt* cols rows . feeds)23
(let ((t (vt-make cols rows)))24
(for-each (lambda (s) (vt-feed! t s)) feeds)25
t))27
(define (rtrim s)28
(let loop ((i (string-length s)))29
(cond ((= i 0) "")30
((char=? (string-ref s (- i 1)) #\space) (loop (- i 1)))31
(else (substring s 0 i)))))32
(define (rows-of t)33
(let loop ((i (- (vt-rows t) 1)) (acc '()))34
(if (< i 0) acc (loop (- i 1) (cons (rtrim (vt-row-text t i)) acc)))))35
(define (cursor-of t) (list (vt-cursor-row t) (vt-cursor-col t)))36
(define (cell-at t row col)37
(let ((c (vector-ref (vt-row-cells t row) col)))38
(list (string (integer->char (vt-cell-ch c)))39
(vt-cell-attr c) (vt-cell-fg c) (vt-cell-bg c))))41
;; ==========================================================================42
;; plain text, C0, wrapping43
;; ==========================================================================44
(test-group "print / C0 / wrap"45
(test "print: text lands on row 0"46
(let ((t (vt* 10 3 "hello")))47
(assert-equal (list "hello" "" "") (rows-of t))48
(assert-equal (list 0 5) (cursor-of t))))49
(test "CRLF: second line"50
(let ((t (vt* 10 3 "ab\r\ncd")))51
(assert-equal (list "ab" "cd" "") (rows-of t))52
(assert-equal (list 1 2) (cursor-of t))))53
(test "CR overprint"54
(assert-equal (list "Xbc" "" "") (rows-of (vt* 10 3 "abc\rX"))))55
(test "BS then overprint"56
(assert-equal (list "aX" "" "") (rows-of (vt* 10 3 "ab\x08;X"))))57
(test "autowrap wraps"58
(let ((t (vt* 10 3 "0123456789AB")))59
(assert-equal (list "0123456789" "AB" "") (rows-of t))60
(assert-equal (list 1 2) (cursor-of t))))61
(test "pending wrap: cursor stays on last col, CR cancels"62
(let ((t (vt* 10 3 "0123456789")))63
(assert-equal (list 0 9) (cursor-of t))64
(vt-feed! t "\rX")65
(assert-equal (list "X123456789" "" "") (rows-of t))))66
(test "DECAWM off: no wrap"67
(assert-equal (list "012345678B" "" "")68
(rows-of (vt* 10 3 (csi "?7l") "0123456789AB"))))69
(test "LF at bottom scrolls; evicted row to scrollback"70
(let ((t (vt* 5 2 "aa\r\nbb\r\ncc")))71
(assert-equal (list "bb" "cc") (rows-of t))72
(assert-equal 1 (vt-scrollback-count t))73
(assert-true (= (vt-cell-ch (vector-ref (vt-scrollback-row t 0) 0)) 97))))74
(test "tab to col 8"75
(assert-equal (list "a b" "") (rows-of (vt* 20 2 "a\tb")))))77
;; ==========================================================================78
;; cursor movement79
;; ==========================================================================80
(test-group "cursor movement"81
(test "CUP 3;4"82
(assert-equal (list "" "" " X" "" "") (rows-of (vt* 10 5 (csi "3;4H") "X"))))83
(test "CUU + CUB"84
(assert-equal (list "" " X" "" "" "")85
(rows-of (vt* 10 5 (csi "3;4H") (csi "A") (csi "2D") "X"))))86
(test "CUP clamps"87
(let ((t (vt* 10 5 "abc" (csi "10;20H") "Z")))88
(assert-equal (list "abc" "" "" "" " Z") (rows-of t))89
(assert-equal (list 4 9) (cursor-of t))))90
(test "CUD + CUF"91
(assert-equal (list 4 4)92
(cursor-of (vt* 10 5 (csi "2;2H") (csi "3B") (csi "2C") "X"))))93
(test "CHA column"94
(assert-equal (list "hi X" "" "" "" "") (rows-of (vt* 10 5 "hi" (csi "5G") "X"))))95
(test "VPA row keeps col"96
(assert-equal (list "hi" "" " X" "" "") (rows-of (vt* 10 5 "hi" (csi "3d") "X")))))98
;; ==========================================================================99
;; erase / insert / delete100
;; ==========================================================================101
(test-group "erase / insert / delete"102
(test "EL 0: erase to right"103
(assert-equal (list "abc" "" "") (rows-of (vt* 10 3 "abcdef" (csi "4G") (csi "K")))))104
(test "EL 1: erase to left (incl cursor)"105
(assert-equal (list " ef" "" "") (rows-of (vt* 10 3 "abcdef" (csi "4G") (csi "1K")))))106
(test "EL 2: whole line"107
(assert-equal (list "" "" "") (rows-of (vt* 10 3 "abcdef" (csi "2K")))))108
(test "ED 0: erase below"109
(assert-equal (list "aaaaaa" "bb" "")110
(rows-of (vt* 6 3 "aaaaaa\r\nbbbbbb\r\ncccccc" (csi "2;3H") (csi "J")))))111
(test "ED 1: erase above"112
(assert-equal (list "" " bbb" "cccccc")113
(rows-of (vt* 6 3 "aaaaaa\r\nbbbbbb\r\ncccccc" (csi "2;3H") (csi "1J")))))114
(test "ED 2: erase all + cursor left on last col"115
(let ((t (vt* 6 3 "aaaaaa\r\nbbbbbb" (csi "2J"))))116
(assert-equal (list "" "" "") (rows-of t))117
(assert-equal (list 1 5) (cursor-of t))))118
(test "ICH inserts blanks (then overtyped)"119
(assert-equal (list "abXYcdef" "" "")120
(rows-of (vt* 10 3 "abcdef" (csi "3G") (csi "2@") "XY"))))121
(test "DCH deletes chars"122
(assert-equal (list "abef" "" "") (rows-of (vt* 10 3 "abcdef" (csi "3G") (csi "2P")))))123
(test "ECH erases chars in place"124
(assert-equal (list "ab ef" "" "") (rows-of (vt* 10 3 "abcdef" (csi "3G") (csi "2X")))))125
(test "IL inserts a line"126
(assert-equal (list "a" "" "b" "c")127
(rows-of (vt* 5 4 "a\r\nb\r\nc\r\nd" (csi "2;1H") (csi "L")))))128
(test "DL deletes a line"129
(assert-equal (list "a" "c" "d" "")130
(rows-of (vt* 5 4 "a\r\nb\r\nc\r\nd" (csi "2;1H") (csi "M"))))))132
;; ==========================================================================133
;; scroll region134
;; ==========================================================================135
(test-group "scroll region"136
(test "DECSTBM: region scrolls, top intact"137
(assert-equal (list "top" "l2" "l3" "l4" "")138
(rows-of (vt* 5 5 "top" (csi "2;4r") (csi "2;1H") "l1\r\nl2\r\nl3\r\nl4"))))139
(test "SU in region"140
(assert-equal (list "a" "d" "" "" "e")141
(rows-of (vt* 5 5 "a\r\nb\r\nc\r\nd\r\ne" (csi "2;4r") (csi "2S")))))142
(test "SD in region"143
(assert-equal (list "a" "" "b" "c" "e")144
(rows-of (vt* 5 5 "a\r\nb\r\nc\r\nd\r\ne" (csi "2;4r") (csi "1T")))))145
(test "RI at region top"146
(assert-equal (list "a" "" "b" "c" "")147
(rows-of (vt* 5 5 "a\r\nb\r\nc" (csi "2;4r") (csi "2;1H") ESC "M"))))148
(test "non-top region scroll: no scrollback"149
(assert-equal 0 (vt-scrollback-count150
(vt* 5 5 "x\r\ny" (csi "2;4r") (csi "2;1H") "1\r\n2\r\n3\r\n4"))))151
(test "DECOM: home is region top"152
(assert-equal (list "" "X" "" "" "")153
(rows-of (vt* 10 5 (csi "2;4r") (csi "?6h") (csi "1;1H") "X")))))155
;; ==========================================================================156
;; SGR157
;; ==========================================================================158
(test-group "SGR"159
(test "SGR bold red + reset"160
(let ((t (vt* 10 2 (csi "1;31m") "R" (csi "0m") "p")))161
(assert-equal (list "R" vt-attr-bold 1 -1) (cell-at t 0 0))162
(assert-equal (list "p" 0 -1 -1) (cell-at t 0 1))))163
(test "SGR italic+underline+inverse"164
(assert-equal (list "x" (+ vt-attr-italic vt-attr-underline vt-attr-inverse) -1 -1)165
(cell-at (vt* 10 2 (csi "3;4;7m") "x") 0 0)))166
(test "SGR 256-color fg/bg"167
(assert-equal (list "c" 0 196 22)168
(cell-at (vt* 10 2 (csi "38;5;196m") (csi "48;5;22m") "c") 0 0)))169
(test "SGR truecolor fg"170
(assert-equal (list "t" 0 (+ #x1000000 (* 255 65536) (* 128 256) 0) -1)171
(cell-at (vt* 10 2 (csi "38;2;255;128;0m") "t") 0 0)))172
(test "SGR colon syntax"173
(assert-equal (list "Q" 0 99 -1) (cell-at (vt* 10 2 ESC "[38:5:99mQ") 0 0)))174
(test "SGR colon colorspace form + following param"175
(assert-equal (list "W" vt-attr-underline (+ #x1000000 (* 255 65536) (* 128 256) 0) -1)176
(cell-at (vt* 10 2 ESC "[38:2::255:128:0;4mW") 0 0)))177
(test "SGR colon truecolor (no colorspace)"178
(assert-equal (list "V" 0 (+ #x1000000 (* 10 65536) (* 20 256) 30) -1)179
(cell-at (vt* 10 2 ESC "[38:2:10:20:30mV") 0 0)))180
(test "SGR bright fg + 39 default"181
(let ((t (vt* 10 2 (csi "91m") "b" (csi "39m") "d")))182
(assert-equal (list "b" 0 9 -1) (cell-at t 0 0))183
(assert-equal (list "d" 0 -1 -1) (cell-at t 0 1))))184
(test "SGR 22 clears bold, keeps color"185
(assert-equal (list "b" 0 1 -1)186
(cell-at (vt* 10 2 (csi "1;31m") "a" (csi "22m") "b") 0 1)))187
(test "BCE: ED fills with cur bg"188
(assert-equal 19 (vt-cell-bg (vector-ref (vt-row-cells (vt* 4 2 (csi "48;5;19m") (csi "2J")) 1) 3))))189
(test "palette 16 = cube 0,0,0" (assert-equal 0 (vt-color-256->rgb 16)))190
(test "palette 196 = red" (assert-equal #xff0000 (vt-color-256->rgb 196)))191
(test "palette 231 = white" (assert-equal #xffffff (vt-color-256->rgb 231)))192
(test "palette 244 gray" (assert-equal #x808080 (vt-color-256->rgb 244))))194
;; ==========================================================================195
;; alt screen196
;; ==========================================================================197
(test-group "alt screen"198
(test "1049: alt starts cleared / restores main + cursor"199
(let ((t (vt* 10 3 "main" (csi "?1049h") (csi "1;1H") "ALT")))200
(assert-equal (list "ALT" "" "") (rows-of t))201
(assert-true (vt-alt? t))202
(vt-feed! t (csi "?1049l"))203
(assert-equal (list "main" "" "") (rows-of t))204
(assert-equal (list 0 4) (cursor-of t))205
(assert-true (not (vt-alt? t)))))206
(test "alt: no scrollback"207
(assert-equal 0 (vt-scrollback-count (vt* 5 2 (csi "?1049h") "a\r\nb\r\nc\r\nd")))))209
;; ==========================================================================210
;; DECSC/DECRC, DECALN, RIS211
;; ==========================================================================212
(test-group "DECSC/DECRC, DECALN, RIS"213
(test "DECSC/DECRC restores pos + SGR"214
(let ((t (vt* 10 3 (csi "31m") (csi "2;3H") ESC "7" (csi "0m") (csi "1;1H") ESC "8" "X")))215
(assert-equal (list "" " X" "") (rows-of t))216
(assert-equal (list "X" 0 1 -1) (cell-at t 1 2))))217
(test "DECALN fills E"218
(assert-equal (list "EEE" "EEE") (rows-of (vt* 3 2 ESC "#8"))))219
(test "RIS clears + resets SGR"220
(let ((t (vt* 5 2 "hi" (csi "31m") ESC "c" "x")))221
(assert-equal (list "x" "") (rows-of t))222
(assert-equal (list "x" 0 -1 -1) (cell-at t 0 0)))))224
;; ==========================================================================225
;; OSC title, DECSCUSR, bracketed paste, modes, events226
;; ==========================================================================227
(test-group "OSC / modes / events"228
(test "OSC 0 BEL: title"229
(let ((t (vt* 10 2 ESC "]0;my title\x07;" "x")))230
(assert-equal "my title" (vt-title t))231
(assert-equal (list "x" "") (rows-of t))))232
(test "OSC 2 ST: title"233
(let ((t (vt* 10 2 ESC "]2;st title" ESC "\\" "y")))234
(assert-equal "st title" (vt-title t))235
(assert-equal (list "y" "") (rows-of t))))236
(test "OSC 52 (clipboard) not in title, queued as event"237
(let ((t (vt* 10 2 ESC "]52;c;aGVsbG8=\x07;" "z")))238
(assert-equal "" (vt-title t))239
(assert-equal (list "z" "") (rows-of t))240
(let ((evs (vt-take-events! t)))241
(assert-true (memv 'clipboard (map (lambda (e) (dict-ref e type: #f)) evs))))))242
(test "DECSCUSR style" (assert-equal 4 (vt-cursor-style (vt* 10 2 (csi "4 q")))))243
(test "bracketed paste on" (assert-true (vt-bracketed-paste? (vt* 10 2 (csi "?2004h")))))244
(test "bracketed paste off"245
(assert-true (not (vt-bracketed-paste? (vt* 10 2 (csi "?2004h") (csi "?2004l"))))))246
(test "cursor hidden" (assert-true (not (vt-cursor-visible? (vt* 10 2 (csi "?25l"))))))247
(test "app cursor mode" (assert-true (vt-app-cursor? (vt* 10 2 (csi "?1h")))))248
(test "IRM: chars shift right"249
(assert-equal (list "aXYbc" "") (rows-of (vt* 10 2 "abc" (csi "2G") (csi "4h") "XY"))))250
(test "mouse-mode flags parsed"251
(assert-equal (+ vt-mouse-1002 vt-mouse-1006)252
(vt-mouse-flags (vt* 10 2 (csi "?1002h") (csi "?1006h")))))253
(test "mouse-mode flags cleared"254
(assert-equal vt-mouse-1006255
(vt-mouse-flags (vt* 10 2 (csi "?1002h") (csi "?1006h") (csi "?1002l"))))))257
;; ==========================================================================258
;; replies (DSR / DA)259
;; ==========================================================================260
(test-group "replies"261
(test "DSR 6: cursor report + drain"262
(let ((t (vt* 10 5 (csi "3;4H") (csi "6n"))))263
(assert-equal (string-append ESC "[3;4R") (vt-take-output! t))264
(assert-equal "" (vt-take-output! t))))265
(test "DSR 5: status ok"266
(assert-equal (string-append ESC "[0n") (vt-take-output! (vt* 10 5 (csi "5n")))))267
(test "DA reply"268
(assert-equal (string-append ESC "[?6c") (vt-take-output! (vt* 10 5 (csi "c"))))))270
;; ==========================================================================271
;; the trust boundary272
;; ==========================================================================273
(test-group "trust boundary"274
(test "huge params clamp"275
(let ((t (vt* 10 2 ESC "[999999999999H" "ok")))276
(assert-equal (list 1 2) (cursor-of t))277
(assert-equal (list "" "ok") (rows-of t))))278
(test "truncated SGR ignored"279
(assert-equal (list "x" 0 -1 -1) (cell-at (vt* 10 2 ESC "[38;2m" "x") 0 0)))280
(test "DCS swallowed"281
(assert-equal (list "ok" "") (rows-of (vt* 10 2 ESC "P malicious dcs payload " ESC "\\" "ok"))))282
(test "CHT clamps"283
(assert-equal (list 0 9) (cursor-of (vt* 10 2 ESC "[999999999I" "x"))))284
(test "unknown modes/charsets swallowed"285
(assert-equal (list "ok" "") (rows-of (vt* 10 2 ESC "[?9999h" ESC "[<5m" ESC "(0" "ok"))))286
(test "escape split across chunks"287
(let ((t (vt-make 10 2)))288
(vt-feed! t ESC) (vt-feed! t "[3") (vt-feed! t "1mX")289
(assert-equal (list "X" 0 1 -1) (cell-at t 0 0)))))291
;; ==========================================================================292
;; incremental UTF-8 (byte feed)293
;; ==========================================================================294
(test-group "incremental UTF-8"295
(test "utf-8 across chunks"296
(let ((t (vt-make 10 2)))297
(vt-feed-bytes! t (list 97 195))298
(vt-feed-bytes! t (list 169 32 226 134))299
(vt-feed-bytes! t (list 146))300
(assert-equal "aé → " (vt-row-text t 0))))301
(test "invalid utf-8 -> U+FFFD, stream recovers"302
(let ((t (vt-make 10 2)))303
(vt-feed-bytes! t (list 195 195 169))304
(assert-true (and (= (vt-cell-ch (vector-ref (vt-row-cells t 0) 0)) 65533)305
(= (vt-cell-ch (vector-ref (vt-row-cells t 0) 1)) 233)))))306
(test "surrogate bytes -> U+FFFD"307
(let ((t (vt-make 10 2)))308
(vt-feed-bytes! t (list 237 160 128))309
(assert-equal 65533 (vt-cell-ch (vector-ref (vt-row-cells t 0) 0)))))310
(test "past-U+10FFFF -> U+FFFD"311
(let ((t (vt-make 10 2)))312
(vt-feed-bytes! t (list 244 144 128 128))313
(assert-equal 65533 (vt-cell-ch (vector-ref (vt-row-cells t 0) 0)))))314
(test "overlong encoding -> U+FFFD"315
(let ((t (vt-make 10 2)))316
(vt-feed-bytes! t (list 224 128 168))317
(assert-equal 65533 (vt-cell-ch (vector-ref (vt-row-cells t 0) 0)))))318
(test "DL never scrollbacks"319
(let ((t (vt* 5 3 "a\r\nb\r\nc" (csi "1;1H") (csi "M"))))320
(assert-equal 0 (vt-scrollback-count t))321
(assert-equal (list "b" "c" "") (rows-of t)))))323
;; ==========================================================================324
;; damage tracking325
;; ==========================================================================326
(test-group "damage"327
(test "damage: one row / drained"328
(let ((t (vt-make 10 3)))329
(vt-take-damage! t)330
(vt-feed! t "x")331
(let ((d (vt-take-damage! t)))332
(assert-equal (list 0) (dict-ref d rows: '()))333
(assert-true (not (dict-ref d all?: #f))))334
(assert-equal '() (dict-ref (vt-take-damage! t) rows: '()))))335
(test "ED marks all its rows"336
(let ((t (vt-make 10 3)))337
(vt-take-damage! t)338
(vt-feed! t (csi "2J"))339
(assert-equal (list 0 1 2) (dict-ref (vt-take-damage! t) rows: '()))))340
(test "scroll damages the region"341
(let ((t (vt-make 5 2)))342
(vt-take-damage! t)343
(vt-feed! t "a\r\nb\r\nc")344
(let ((d (vt-take-damage! t)))345
(assert-true (or (dict-ref d all?: #f)346
(equal? (dict-ref d rows: '()) (list 0 1))))))))348
;; ==========================================================================349
;; resize350
;; ==========================================================================351
(test-group "resize"352
(test "narrower truncates / wider pads / cols updated"353
(let ((t (vt* 10 4 "aa\r\nbb")))354
(vt-resize! t 5 4)355
(assert-equal (list "aa" "bb" "" "") (rows-of t))356
(vt-resize! t 20 4)357
(assert-equal (list "aa" "bb" "" "") (rows-of t))358
(assert-equal 20 (vt-cols t))))359
(test "shrink drops blank bottom rows (no scrollback)"360
(let ((t (vt* 10 5 "aa\r\nbb")))361
(vt-resize! t 10 3)362
(assert-equal (list "aa" "bb" "") (rows-of t))363
(assert-equal 0 (vt-scrollback-count t))))364
(test "shrink onto content keeps cursor rows, evicts to scrollback"365
(let ((t (vt* 10 4 "a\r\nb\r\nc\r\nd")))366
(vt-resize! t 10 2)367
(assert-equal (list "c" "d") (rows-of t))368
(assert-equal 2 (vt-scrollback-count t))369
(assert-equal (list 1 1) (cursor-of t))))370
(test "grow pulls back out of scrollback"371
(let ((t (vt* 10 2 "a\r\nb\r\nc")))372
(assert-equal 1 (vt-scrollback-count t))373
(vt-resize! t 10 4)374
(assert-equal (list "a" "b" "c" "") (rows-of t))375
(assert-equal 0 (vt-scrollback-count t))))376
(test "post-resize feed is sane"377
(let ((t (vt* 10 4 (csi "2;3r") "x")))378
(vt-resize! t 8 3)379
(vt-feed! t (string-append (csi "3;1H") "\n\n"))380
(assert-true (>= (vt-rows t) 3)))))382
;; ==========================================================================383
;; the ground-state bulk-run fast path384
;; ==========================================================================385
(test-group "bulk-run fast path"386
(test "run wraps across the margin"387
(let ((t (vt* 5 3 "abcdefgh")))388
(assert-equal (list "abcde" "fgh" "") (rows-of t))389
(assert-equal (list 1 3) (cursor-of t))))390
(test "run deferred wrap parks / fires next char"391
(let ((t (vt* 5 3 "abcde")))392
(assert-equal (list 0 4) (cursor-of t))393
(vt-feed! t "f")394
(assert-equal (list "abcde" "f" "") (rows-of t))))395
(test "run no-autowrap overwrites last col"396
(let ((t (vt* 5 3 (csi "?7l") "abcdefgh")))397
(assert-equal (list "abcdh" "" "") (rows-of t))398
(assert-equal (list 0 4) (cursor-of t))))399
(test "esc splits runs, text intact"400
(assert-equal (list "abcdef" "" "")401
(rows-of (vt* 10 3 (string-append "ab" (csi "31m") "cd" (csi "0m") "ef")))))402
(test "insert mode shifts, not overwrites"403
(assert-equal (list "XYabc" "" "")404
(rows-of (vt* 10 3 "abc" (csi "1;1H") (csi "4h") "XY"))))405
(test "mixed unicode intact"406
(assert-equal (list "aλbμc" "" "") (rows-of (vt* 10 3 "aλbμc")))))408
;; ==========================================================================409
;; row-runs (the render seam) — new native primitive410
;; ==========================================================================411
(test-group "row-runs"412
(test "plain run merges to one, right-trimmed"413
(let* ((t (vt* 10 2 "hello"))414
(runs (vt-row-runs t 0)))415
(assert-equal 1 (length runs))416
(assert-equal "hello" (vector-ref (car runs) 0))417
(assert-equal 0 (vector-ref (car runs) 1))))418
(test "style change splits runs"419
(let* ((t (vt* 10 2 "ab" (csi "31m") "cd"))420
(runs (vt-row-runs t 0)))421
(assert-equal 2 (length runs))422
(assert-equal "ab" (vector-ref (car runs) 0))423
(assert-equal "cd" (vector-ref (cadr runs) 0))424
(assert-equal 1 (vector-ref (cadr runs) 2))))425
(test "cursor-col forces a break with cursor? flag"426
(let* ((t (vt* 10 2 "hello"))427
(runs (vt-row-runs t 0 1)))428
;; "h" | "e"(cursor) | "llo"429
(assert-equal 3 (length runs))430
(assert-true (vector-ref (cadr runs) 4))431
(assert-equal "e" (vector-ref (cadr runs) 0))))432
(test "blank row -> no runs"433
(assert-equal '() (vt-row-runs (vt-make 10 2) 1)))435
;; ---- t-d4c7: a history row keeps the width it was PUSHED at -------------436
;; Ring rows are allocated at the cols in effect when they scrolled off and437
;; are never re-widthed (xterm no-rewrap). Reading one at t->cols after a438
;; WIDENING resize runs off the end of the allocation: a heap over-read whose439
;; garbage got rendered into the terminal (DoS via integer->char, plus440
;; disclosure of adjacent heap). These call the REAL readers — the fuzz441
;; harness cannot, since VT_FUZZ compiles the Sigil glue out.442
(test "scrollback row keeps its push width (no over-read on widen)"443
(let ((t (vt-make 40 3)))444
(vt-feed! t "aaaa\r\nbbbb\r\ncccc\r\ndddd\r\neeee\r\n")445
(assert-true (> (vt-scrollback-count t) 0))446
(vt-resize! t 132 3) ; widen; history stays 40 wide447
(assert-equal 132 (vt-cols t))448
;; the row is returned at ITS width, not the grid's449
(assert-equal 40 (vector-length (vt-scrollback-row t 0)))450
;; and every cell is a real codepoint, not heap garbage451
(let* ((row (vt-scrollback-row t 0))452
(n (vector-length row)))453
(let loop ((i 0))454
(when (< i n)455
(let ((cp (vt-cell-ch (vector-ref row i))))456
(assert-true (and (>= cp 0) (<= cp 1114111))))457
(loop (+ i 1)))))))459
(test "scrollback runs after widen stay in-bounds"460
(let ((t (vt-make 40 3)))461
(vt-feed! t "hello\r\nworld\r\nagain\r\nmore1\r\nmore2\r\n")462
(vt-resize! t 132 3)463
(let ((runs (vt-scrollback-runs t 0)))464
(assert-true (pair? runs))465
;; TOTAL rendered width must not exceed the row's real width. Asserting466
;; per-RUN length instead would silently pass on the bug: over-read467
;; garbage has erratic attrs, so it splits into many SHORT runs that are468
;; each under the limit while the row as a whole runs far over.469
(let loop ((rs runs) (total 0))470
(if (null? rs)471
(assert-true (<= total 40))472
(loop (cdr rs) (+ total (string-length (vector-ref (car rs) 0)))))))))474
;; The resize PULL had the same root cause with a different guess (ocols, not475
;; t->cols) — only wrong after TWO resizes, when ocols is neither the push476
;; width nor the new width.477
(test "resize pull uses the row's push width, not ocols"478
(let ((t (vt-make 40 3)))479
(vt-feed! t "aaaa\r\nbbbb\r\ncccc\r\ndddd\r\neeee\r\n")480
(vt-resize! t 80 3) ; ocols becomes 80...481
(vt-resize! t 132 8) ; ...but history rows are 40 wide482
(assert-equal 132 (vt-cols t))483
;; pulled-back rows must be real content, not garbage484
(let loop ((r 0))485
(when (< r 8)486
(let* ((row (vt-row-cells t r))487
(n (vector-length row)))488
(let loop2 ((i 0))489
(when (< i n)490
(let ((cp (vt-cell-ch (vector-ref row i))))491
(assert-true (and (>= cp 0) (<= cp 1114111))))492
(loop2 (+ i 1)))))493
(loop (+ r 1)))))))