1package vt
2
3import (
4 "testing"
5)
6
7// TestWideCharSurvivesRotatedScroll proves a wide-character pair travels intact
8// with its line when a full-width scroll rotates line offsets rather than
9// copying cells.
10func TestWideCharSurvivesRotatedScroll(t *testing.T) {
11 tests := []struct {
12 name string
13 run func(t *testing.T)
14 }{
15 {
16 name: "wide pair moves up one row on newline scroll",
17 run: func(t *testing.T) {
18 s := NewScreen(4, 3)
19 // Place a wide rune (世, width 2) on the bottom row, then scroll up.
20 s.Write([]byte("\x1b[3;1H世"))
21 assertCell(t, s, 0, 2, '世')
22 assertContinuation(t, s, 1, 2)
23 // Newline at the bottom row scrolls the region up by one.
24 s.Write([]byte("\n"))
25 // The wide pair is now on row 1, intact; bottom row blank.
26 assertCell(t, s, 0, 1, '世')
27 assertContinuation(t, s, 1, 1)
28 assertBlank(t, s, 0, 2)
29 assertBlank(t, s, 1, 2)
30 if err := s.frame.CheckInvariants(); err != nil {
31 t.Fatalf("invariants: %v", err)
32 }
33 },
34 },
35 {
36 name: "wide pair moves down on reverse index",
37 run: func(t *testing.T) {
38 s := NewScreen(4, 3)
39 s.Write([]byte("\x1b[1;1H世")) // top row
40 assertCell(t, s, 0, 0, '世')
41 assertContinuation(t, s, 1, 0)
42 s.Write([]byte("\x1b[1;1H\x1bM")) // cursor top, reverse index -> scroll down
43 assertCell(t, s, 0, 1, '世')
44 assertContinuation(t, s, 1, 1)
45 assertBlank(t, s, 0, 0)
46 if err := s.frame.CheckInvariants(); err != nil {
47 t.Fatalf("invariants: %v", err)
48 }
49 },
50 },
51 }
52 for _, tt := range tests {
53 t.Run(tt.name, tt.run)
54 }
55}
56
57// TestLineOffsetInvariantAfterVTSequences drives interleaved writes and scrolls
58// through the VT layer and asserts the frame's line-offset stays a valid
59// permutation.
60func TestLineOffsetInvariantAfterVTSequences(t *testing.T) {
61 s := NewScreen(10, 6)
62 seqs := [][]byte{
63 []byte("line0\r\nline1\r\nline2\r\nline3\r\nline4\r\nline5"),
64 []byte("\x1b[2;5r"), // scroll region rows 2..5
65 []byte("\x1b[5;1Hmore\n"), // scroll within region
66 []byte("\x1b[1;1H\x1bM"), // reverse index at top
67 []byte("\x1b[3S"), // scroll up 3
68 []byte("\x1b[2T"), // scroll down 2
69 []byte("\x1b[r"), // reset region
70 []byte("\x1b[6;1Hbottom\n"),
71 }
72 for i, seq := range seqs {
73 s.Write(seq)
74 s.ClearDamage()
75 if err := s.frame.CheckInvariants(); err != nil {
76 t.Fatalf("after seq %d: invariants broken: %v", i, err)
77 }
78 }
79}