detached bc06f3f

test(vt): cover ECH row damage

commit
bc06f3f2793f898db7dba800b18c81f964b347ab
author
brice · · 2026-08-03 07:46 UTC
parent
a6c1ffe
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
1 file · +15 -9
M pkg/vt/screen_csi_test.go +15 -9
@@ -614,6 +614,7 @@ func TestEraseCharacters(t *testing.T) {
614614 tests := []struct {
615615 name string
616616 width int
617+ row int
617618 initial string
618619 seq string
619620 wantRow string
@@ -623,38 +624,42 @@ func TestEraseCharacters(t *testing.T) {
623624 {
624625 name: "explicit count",
625626 width: 50,
627+ row: 1,
626628 initial: ` // scroll-method "on-button-down"`,
627- seq: "\x1b[1;19Hbutton 273\x1b[13X",
629+ seq: "\x1b[19Gbutton 273\x1b[13X",
628630 wantRow: " // scroll-button 273",
629631 wantCol: 28,
630632 wantDamage: []renderer.Damage{
631- {Kind: renderer.DamageText, X: 18, Y: 0, Width: 10, Height: 1, Count: 1},
632- {Kind: renderer.DamageClear, X: 28, Y: 0, Width: 13, Height: 1, Count: 1},
633+ {Kind: renderer.DamageText, X: 18, Y: 1, Width: 10, Height: 1, Count: 1},
634+ {Kind: renderer.DamageClear, X: 28, Y: 1, Width: 13, Height: 1, Count: 1},
633635 },
634636 },
635637 {
636638 name: "omitted count erases exactly one cell",
637639 width: 6,
640+ row: 1,
638641 initial: "abcdef",
639- seq: "\x1b[1;3H\x1b[X",
642+ seq: "\x1b[3G\x1b[X",
640643 wantRow: "ab def",
641644 wantCol: 2,
642- wantDamage: []renderer.Damage{{Kind: renderer.DamageClear, X: 2, Y: 0, Width: 1, Height: 1, Count: 1}},
645+ wantDamage: []renderer.Damage{{Kind: renderer.DamageClear, X: 2, Y: 1, Width: 1, Height: 1, Count: 1}},
643646 },
644647 {
645648 name: "maximum count clips to screen edge",
646649 width: 6,
650+ row: 1,
647651 initial: "abcdef",
648- seq: "\x1b[1;4H\x1b[" + maxInt + "X",
652+ seq: "\x1b[4G\x1b[" + maxInt + "X",
649653 wantRow: "abc",
650654 wantCol: 3,
651- wantDamage: []renderer.Damage{{Kind: renderer.DamageClear, X: 3, Y: 0, Width: 3, Height: 1, Count: 1}},
655+ wantDamage: []renderer.Damage{{Kind: renderer.DamageClear, X: 3, Y: 1, Width: 3, Height: 1, Count: 1}},
652656 },
653657 }
654658
655659 for _, tt := range tests {
656660 t.Run(tt.name, func(t *testing.T) {
657- s := NewScreen(tt.width, 1)
661+ s := NewScreen(tt.width, 2)
662+ s.Write([]byte("\x1b[2;1H"))
658663 s.Write([]byte(tt.initial))
659664 s.ClearDamage()
660665 s.Write([]byte(tt.seq))
@@ -664,8 +669,9 @@ func TestEraseCharacters(t *testing.T) {
664669 if x < len(tt.wantRow) {
665670 want = rune(tt.wantRow[x])
666671 }
667- require.Equal(t, want, cellAt(s, x, 0).Rune, "cell(%d,0)", x)
672+ require.Equal(t, want, cellAt(s, x, tt.row).Rune, "cell(%d,%d)", x, tt.row)
668673 }
674+ require.Equal(t, tt.row, s.Row)
669675 require.Equal(t, tt.wantCol, s.Col)
670676 require.Equal(t, tt.wantDamage, s.Damage())
671677 })
@@ -614,6 +614,7 @@ func TestEraseCharacters(t *testing.T) {
614 tests := []struct {614 tests := []struct {
615 name string615 name string
616 width int616 width int
617+ row int
617 initial string618 initial string
618 seq string619 seq string
619 wantRow string620 wantRow string
@@ -623,38 +624,42 @@ func TestEraseCharacters(t *testing.T) {
623 {624 {
624 name: "explicit count",625 name: "explicit count",
625 width: 50,626 width: 50,
627+ row: 1,
626 initial: ` // scroll-method "on-button-down"`,628 initial: ` // scroll-method "on-button-down"`,
627- seq: "\x1b[1;19Hbutton 273\x1b[13X",629+ seq: "\x1b[19Gbutton 273\x1b[13X",
628 wantRow: " // scroll-button 273",630 wantRow: " // scroll-button 273",
629 wantCol: 28,631 wantCol: 28,
630 wantDamage: []renderer.Damage{632 wantDamage: []renderer.Damage{
631- {Kind: renderer.DamageText, X: 18, Y: 0, Width: 10, Height: 1, Count: 1},633+ {Kind: renderer.DamageText, X: 18, Y: 1, Width: 10, Height: 1, Count: 1},
632- {Kind: renderer.DamageClear, X: 28, Y: 0, Width: 13, Height: 1, Count: 1},634+ {Kind: renderer.DamageClear, X: 28, Y: 1, Width: 13, Height: 1, Count: 1},
633 },635 },
634 },636 },
635 {637 {
636 name: "omitted count erases exactly one cell",638 name: "omitted count erases exactly one cell",
637 width: 6,639 width: 6,
640+ row: 1,
638 initial: "abcdef",641 initial: "abcdef",
639- seq: "\x1b[1;3H\x1b[X",642+ seq: "\x1b[3G\x1b[X",
640 wantRow: "ab def",643 wantRow: "ab def",
641 wantCol: 2,644 wantCol: 2,
642- wantDamage: []renderer.Damage{{Kind: renderer.DamageClear, X: 2, Y: 0, Width: 1, Height: 1, Count: 1}},645+ wantDamage: []renderer.Damage{{Kind: renderer.DamageClear, X: 2, Y: 1, Width: 1, Height: 1, Count: 1}},
643 },646 },
644 {647 {
645 name: "maximum count clips to screen edge",648 name: "maximum count clips to screen edge",
646 width: 6,649 width: 6,
650+ row: 1,
647 initial: "abcdef",651 initial: "abcdef",
648- seq: "\x1b[1;4H\x1b[" + maxInt + "X",652+ seq: "\x1b[4G\x1b[" + maxInt + "X",
649 wantRow: "abc",653 wantRow: "abc",
650 wantCol: 3,654 wantCol: 3,
651- wantDamage: []renderer.Damage{{Kind: renderer.DamageClear, X: 3, Y: 0, Width: 3, Height: 1, Count: 1}},655+ wantDamage: []renderer.Damage{{Kind: renderer.DamageClear, X: 3, Y: 1, Width: 3, Height: 1, Count: 1}},
652 },656 },
653 }657 }
654 658
655 for _, tt := range tests {659 for _, tt := range tests {
656 t.Run(tt.name, func(t *testing.T) {660 t.Run(tt.name, func(t *testing.T) {
657- s := NewScreen(tt.width, 1)661+ s := NewScreen(tt.width, 2)
662+ s.Write([]byte("\x1b[2;1H"))
658 s.Write([]byte(tt.initial))663 s.Write([]byte(tt.initial))
659 s.ClearDamage()664 s.ClearDamage()
660 s.Write([]byte(tt.seq))665 s.Write([]byte(tt.seq))
@@ -664,8 +669,9 @@ func TestEraseCharacters(t *testing.T) {
664 if x < len(tt.wantRow) {669 if x < len(tt.wantRow) {
665 want = rune(tt.wantRow[x])670 want = rune(tt.wantRow[x])
666 }671 }
667- require.Equal(t, want, cellAt(s, x, 0).Rune, "cell(%d,0)", x)672+ require.Equal(t, want, cellAt(s, x, tt.row).Rune, "cell(%d,%d)", x, tt.row)
668 }673 }
674+ require.Equal(t, tt.row, s.Row)
669 require.Equal(t, tt.wantCol, s.Col)675 require.Equal(t, tt.wantCol, s.Col)
670 require.Equal(t, tt.wantDamage, s.Damage())676 require.Equal(t, tt.wantDamage, s.Damage())
671 })677 })

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 13

↑↓ moveenter openesc close