1package xturbo
2
3import (
4 "bytes"
5 "encoding/binary"
6 "errors"
7 "os"
8 "path/filepath"
9 "testing"
10)
11
12type authEntry struct {
13 family uint16
14 addr, number, name string
15 data []byte
16}
17
18func encodeAuth(entries ...authEntry) []byte {
19 var b []byte
20 str := func(s []byte) {
21 b = binary.BigEndian.AppendUint16(b, uint16(len(s)))
22 b = append(b, s...)
23 }
24 for _, e := range entries {
25 b = binary.BigEndian.AppendUint16(b, e.family)
26 str([]byte(e.addr))
27 str([]byte(e.number))
28 str([]byte(e.name))
29 str(e.data)
30 }
31 return b
32}
33
34func TestReadCookie(t *testing.T) {
35 cookie := []byte("0123456789abcdef")
36 other := []byte("fedcba9876543210")
37 local := func(host, num string, data []byte) authEntry {
38 return authEntry{familyLocal, host, num, authMagicCookie, data}
39 }
40 for _, tc := range []struct {
41 name string
42 file []byte
43 want []byte
44 wantErr error
45 }{
46 {"match", encodeAuth(local("arch", "7", cookie)), cookie, nil},
47 {"first match wins", encodeAuth(local("other", "7", other), local("arch", "3", other), local("arch", "7", cookie), local("arch", "7", other)), cookie, nil},
48 {"wild family", encodeAuth(authEntry{familyWild, "", "7", authMagicCookie, cookie}), cookie, nil},
49 {"empty number matches any", encodeAuth(local("arch", "", cookie)), cookie, nil},
50 {"other host", encodeAuth(local("box", "7", cookie)), nil, nil},
51 {"other display", encodeAuth(local("arch", "70", cookie)), nil, nil},
52 {"other family", encodeAuth(authEntry{0, "arch", "7", authMagicCookie, cookie}), nil, nil},
53 {"other protocol", encodeAuth(authEntry{familyLocal, "arch", "7", "XDM-AUTHORIZATION-1", cookie}), nil, nil},
54 {"empty file", nil, nil, nil},
55 {"truncated family", []byte{1}, nil, ErrMalformed},
56 {"truncated field", encodeAuth(local("arch", "7", cookie))[:30], nil, ErrMalformed},
57 {"missing fields", encodeAuth(local("arch", "7", cookie))[:10], nil, ErrMalformed},
58 } {
59 t.Run(tc.name, func(t *testing.T) {
60 path := filepath.Join(t.TempDir(), "Xauthority")
61 if err := os.WriteFile(path, tc.file, 0o600); err != nil {
62 t.Fatal(err)
63 }
64 got, err := readCookie(path, "arch", 7)
65 if !errors.Is(err, tc.wantErr) || (tc.wantErr == nil && err != nil) || !bytes.Equal(got, tc.want) {
66 t.Fatalf("got %q, %v; want %q, %v", got, err, tc.want, tc.wantErr)
67 }
68 })
69 }
70 if _, err := readCookie(filepath.Join(t.TempDir(), "missing"), "arch", 7); !errors.Is(err, os.ErrNotExist) {
71 t.Fatal(err)
72 }
73}
74
75func TestXauthorityPath(t *testing.T) {
76 t.Setenv("XAUTHORITY", "/x/auth")
77 if got := xauthorityPath(); got != "/x/auth" {
78 t.Fatal(got)
79 }
80 t.Setenv("XAUTHORITY", "")
81 t.Setenv("HOME", "/home/u")
82 if got := xauthorityPath(); got != "/home/u/.Xauthority" {
83 t.Fatal(got)
84 }
85}
86
87func TestParseDisplay(t *testing.T) {
88 for _, tc := range []struct {
89 in string
90 want int
91 ok bool
92 }{
93 {":0", 0, true},
94 {":12", 12, true},
95 {":3.1", 3, true},
96 {"unix:5", 5, true},
97 {"unix:5.0", 5, true},
98 {"", 0, false},
99 {"0", 0, false},
100 {":", 0, false},
101 {":x", 0, false},
102 {":1.", 0, false},
103 {":1.x", 0, false},
104 {":-1", 0, false},
105 {"localhost:0", 0, false},
106 {"host::0", 0, false},
107 {"::1:0", 0, false},
108 } {
109 got, err := parseDisplay(tc.in)
110 if (err == nil) != tc.ok || got != tc.want {
111 t.Errorf("%q: %d, %v", tc.in, got, err)
112 }
113 }
114}
115
116func TestDialNoServer(t *testing.T) {
117 // Display numbers above any real server: both abstract and path fail.
118 if _, err := Dial(":2147483000"); err == nil {
119 t.Fatal("Dial succeeded without a server")
120 }
121 if _, err := Dial("tcp/host:0"); err == nil {
122 t.Fatal("remote DISPLAY accepted")
123 }
124}