main 602baa7

dial_test.go RawHistoryFolder

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}

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 1

Tags 1

↑↓ moveenter openesc close