1package daemon
2
3import (
4 "fmt"
5 "os/exec"
6 "strconv"
7 "testing"
8
9 vt "github.com/bnema/vev-vt"
10 renderer "github.com/bnema/vev-vt/ansi"
11 "github.com/stretchr/testify/require"
12)
13
14// ncurses xterm-direct reserves 0..7 for ANSI colors and encodes larger
15// indices as packed RGB, with an empty colorspace field in colon-form SGR.
16func TestXtermDirectColorContract(t *testing.T) {
17 for _, value := range []int{0, 1, 7, 8, 0x123456, 0xffffff} {
18 t.Run(strconv.Itoa(value), func(t *testing.T) {
19 fg, bg := xtermDirectSetters(value)
20 assertXtermDirectColors(t, fg+bg, value)
21 })
22 }
23}
24
25func TestInstalledXtermDirectColorContract(t *testing.T) {
26 if _, err := exec.LookPath("tput"); err != nil {
27 t.Skip("tput is not installed")
28 }
29 colors, err := exec.CommandContext(t.Context(), "tput", "-T", "xterm-direct", "colors").Output()
30 if err != nil {
31 t.Skip("xterm-direct is not installed")
32 }
33 require.Equal(t, "16777216\n", string(colors))
34 for _, value := range []int{0, 7, 8, 0x123456, 0xffffff} {
35 t.Run(strconv.Itoa(value), func(t *testing.T) {
36 fg, err := exec.CommandContext(t.Context(), "tput", "-T", "xterm-direct", "setaf", strconv.Itoa(value)).Output()
37 require.NoError(t, err)
38 bg, err := exec.CommandContext(t.Context(), "tput", "-T", "xterm-direct", "setab", strconv.Itoa(value)).Output()
39 require.NoError(t, err)
40 assertXtermDirectColors(t, string(fg)+string(bg), value)
41 })
42 }
43}
44
45func xtermDirectSetters(value int) (string, string) {
46 if value < 8 {
47 return fmt.Sprintf("\x1b[%dm", 30+value), fmt.Sprintf("\x1b[%dm", 40+value)
48 }
49 components := fmt.Sprintf("2::%d:%d:%d", value>>16, value>>8&255, value&255)
50 return "\x1b[38:" + components + "m", "\x1b[48:" + components + "m"
51}
52
53func assertXtermDirectColors(t *testing.T, setters string, value int) {
54 t.Helper()
55 screen := vt.NewScreen(3, 1)
56 screen.Write([]byte(setters + "X\x1b[39;49mY"))
57 cell := screen.Cell(0, 0)
58 if value < 8 {
59 require.False(t, cell.Style.HasForegroundRGB)
60 require.False(t, cell.Style.HasBackgroundRGB)
61 require.EqualValues(t, value, cell.Style.Foreground)
62 require.EqualValues(t, value, cell.Style.Background)
63 } else {
64 want := renderer.RGB{R: uint8(value >> 16), G: uint8(value >> 8), B: uint8(value)}
65 require.True(t, cell.Style.HasForegroundRGB)
66 require.True(t, cell.Style.HasBackgroundRGB)
67 require.Equal(t, want, cell.Style.ForegroundRGB)
68 require.Equal(t, want, cell.Style.BackgroundRGB)
69 }
70 reset := screen.Cell(1, 0).Style
71 require.False(t, reset.HasForegroundRGB)
72 require.False(t, reset.HasBackgroundRGB)
73 require.EqualValues(t, -1, reset.Foreground)
74 require.EqualValues(t, -1, reset.Background)
75
76 // Exercise the actual attachment output path, not just the VT parser.
77 frame := renderer.NewFrame(3, 1)
78 frame.Set(0, 0, cell)
79 frame.Set(1, 0, screen.Cell(1, 0))
80 prepared, err := newOutputStateStream().prepare(frame, nil, true)
81 require.NoError(t, err)
82 display := vt.NewScreen(3, 1)
83 display.Write(prepared.data)
84 require.Equal(t, cell, display.Cell(0, 0))
85}