v0.7.0 f00b1b1

display_test.go RawHistoryFolder

1package server
2
3import (
4	"context"
5	"golang.org/x/sys/unix"
6	"os"
7	"testing"
8	"time"
9	"unsafe"
10)
11
12func TestNoHandlerClosesFDs(t *testing.T) {
13	for _, sig := range []string{"2?oh", "12uhu"} {
14		fds := []int{0, 0}
15		if err := unix.Pipe(fds); err != nil {
16			t.Fatal(err)
17		}
18		defer unix.Close(fds[1])
19		args := make([]Arg, argCount(sig))
20		i := 0
21		for _, ch := range sig {
22			if ch == '?' || (ch >= '0' && ch <= '9') {
23				continue
24			}
25			if ch == 'h' {
26				args[i] = Fd(fds[0])
27			}
28			i++
29		}
30		iface := &Interface{Name: "test_fd", Version: 1, Requests: []Message{{Name: "fd", Signature: sig}}}
31		if err := NewInterfaces(iface); err != nil {
32			t.Fatal(err)
33		}
34		closeRequestFDs(iface.Requests[0].fdPositions, unsafe.Pointer(&args[0]))
35		if _, err := unix.FcntlInt(uintptr(fds[0]), unix.F_GETFD, 0); err != unix.EBADF {
36			t.Fatalf("signature %s: fd not closed: %v", sig, err)
37		}
38	}
39}
40
41// Objects libwayland created itself (wl_registry) come back as bare handles.
42func TestArgResourceUntracked(t *testing.T) {
43	if Arg(0).Resource() != nil {
44		t.Fatal("null object is not nil")
45	}
46	r := Arg(0x1234).Resource()
47	if r == nil || r.c != 0x1234 || r.handler != nil || !r.bare || !r.Alive() {
48		t.Fatalf("untracked object %+v", r)
49	}
50}
51
52func TestAckGlobalRemoveNilResources(t *testing.T) {
53	if err := load(); err != nil {
54		t.Skip(err)
55	}
56	// A dead or missing resource is ignored, whatever the library version.
57	supported := AckGlobalRemove(nil, nil, 1)
58	if supported != (symFixesAckGlobalRemove != 0) {
59		t.Fatalf("supported %v with symbol %#x", supported, symFixesAckGlobalRemove)
60	}
61}
62
63func TestDoStopped(t *testing.T) {
64	d, err := NewDisplay()
65	if err != nil {
66		t.Fatal(err)
67	}
68	ctx, cancel := context.WithCancel(context.Background())
69	returned := make(chan error, 1)
70	go func() { returned <- d.Run(ctx) }()
71	ran := false
72	if !d.Do(func() { ran = true }) || !ran {
73		t.Fatal("call did not run")
74	}
75	cancel()
76	select {
77	case err := <-returned:
78		if err != nil {
79			t.Fatal(err)
80		}
81	case <-time.After(3 * time.Second):
82		t.Fatal("Run did not return")
83	}
84	select {
85	case <-d.Stopped():
86	default:
87		t.Fatal("Stopped not closed")
88	}
89	result := make(chan bool, 1)
90	go func() { result <- d.Do(func() { t.Error("call ran after stop") }) }()
91	select {
92	case ok := <-result:
93		if ok {
94			t.Fatal("call confirmed after stop")
95		}
96	case <-time.After(100 * time.Millisecond):
97		t.Fatal("Do blocked after stop")
98	}
99}
100
101func TestDoBlockedDuringCancellation(t *testing.T) {
102	d, err := NewDisplay()
103	if err != nil {
104		t.Fatal(err)
105	}
106	ctx, cancel := context.WithCancel(context.Background())
107	returned := make(chan error, 1)
108	go func() { returned <- d.Run(ctx) }()
109	entered := make(chan struct{})
110	release := make(chan struct{})
111	first := make(chan bool, 1)
112	go func() { first <- d.Do(func() { close(entered); <-release }) }()
113	select {
114	case <-entered:
115	case <-time.After(3 * time.Second):
116		t.Fatal("first call not started")
117	}
118	result := make(chan bool, 1)
119	go func() { result <- d.Do(func() {}) }()
120	cancel()
121	close(release)
122	select {
123	case <-first:
124	case <-time.After(3 * time.Second):
125		t.Fatal("first call blocked")
126	}
127	select {
128	case err := <-returned:
129		if err != nil {
130			t.Fatal(err)
131		}
132	case <-time.After(3 * time.Second):
133		t.Fatal("Run blocked")
134	}
135	select {
136	case <-result:
137	case <-time.After(100 * time.Millisecond):
138		t.Fatal("blocked Do leaked")
139	}
140}
141
142func TestCloseBeforeRun(t *testing.T) {
143	d, err := NewDisplay()
144	if err != nil {
145		t.Fatal(err)
146	}
147	fd, err := unix.Socket(unix.AF_UNIX, unix.SOCK_STREAM, 0)
148	if err != nil {
149		t.Fatal(err)
150	}
151	defer unix.Close(fd)
152	if err := unix.Bind(fd, &unix.SockaddrUnix{Name: "\x00purego-close-test"}); err != nil {
153		t.Fatal(err)
154	}
155	if err := unix.Listen(fd, 1); err != nil {
156		t.Fatal(err)
157	}
158	if err := d.AddSocketFD(fd); err != nil {
159		t.Fatal(err)
160	}
161	d.Close()
162	d.Close()
163}
164
165// Do must not wait for the dispatch timeout: a compositor sends one call per
166// pointer event, at up to 1000 per second.
167func TestDoLatency(t *testing.T) {
168	d, err := NewDisplay()
169	if err != nil {
170		t.Fatal(err)
171	}
172	ctx, cancel := context.WithCancel(context.Background())
173	returned := make(chan error, 1)
174	go func() { returned <- d.Run(ctx) }()
175	defer func() { cancel(); <-returned }()
176	start := time.Now()
177	for range 1000 {
178		if !d.Do(func() {}) {
179			t.Fatal("Do failed")
180		}
181	}
182	if elapsed := time.Since(start); elapsed > 500*time.Millisecond {
183		t.Fatalf("1000 calls took %v", elapsed)
184	}
185}
186
187func TestCloseReleasesFDs(t *testing.T) {
188	count := func() int {
189		es, err := os.ReadDir("/proc/self/fd")
190		if err != nil {
191			t.Skip(err)
192		}
193		return len(es)
194	}
195	d, err := NewDisplay()
196	if err != nil {
197		t.Fatal(err)
198	}
199	d.Close() // first display loads the library; measure the next ones
200	before := count()
201	for range 3 {
202		d, err := NewDisplay()
203		if err != nil {
204			t.Fatal(err)
205		}
206		d.Close()
207	}
208	if after := count(); after != before {
209		t.Fatalf("fd leak: %d -> %d", before, after)
210	}
211}

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 14

↑↓ moveenter openesc close