main 5d6fa26

syncwait_explicit_test.go RawHistoryFolder

1package wayland
2
3import (
4	"testing"
5	"time"
6
7	"github.com/stretchr/testify/mock"
8	"golang.org/x/sys/unix"
9)
10
11// explicitWaiter is a waiter whose explicit waits register with a mocked
12// display-loop source; the callback and fd are captured.
13type explicitWaiter struct {
14	sw    *syncWaiter
15	src   *mockacquireSource
16	rm    *mockacquireRemover
17	paced int
18	fd    int
19	fire  func()
20	point syncPoint
21}
22
23func newExplicitWaiter(t *testing.T, addErr error) *explicitWaiter {
24	t.Helper()
25	e := &explicitWaiter{src: newMockacquireSource(t), rm: newMockacquireRemover(t)}
26	dev := newMocksyncobjDevice(t)
27	dev.EXPECT().signalled(uint32(7), uint64(4)).Return(false, nil).Once()
28	dev.EXPECT().eventfd(uint32(7), uint64(4), mock.Anything).Return(nil).Once()
29	e.point = syncPoint{tl: &timeline{dev: dev, handle: 7}, point: 4}
30	sw, err := newSyncWaiter(func() { t.Error("explicit wait posted a tick") })
31	if err != nil {
32		t.Fatal(err)
33	}
34	t.Cleanup(func() { sw.discard(); sw.close() })
35	sw.src, sw.pace = e.src, func() { e.paced++ }
36	e.sw = sw
37	e.src.EXPECT().add(mock.Anything, mock.Anything).RunAndReturn(func(fd int, f func()) (acquireRemover, error) {
38		e.fd, e.fire = fd, f
39		if addErr != nil {
40			return nil, addErr
41		}
42		return e.rm, nil
43	}).Once()
44	return e
45}
46
47func fdClosed(fd int) bool {
48	_, err := unix.FcntlInt(uintptr(fd), unix.F_GETFD, 0)
49	return err == unix.EBADF
50}
51
52// A fired eventfd removes the source, closes the fd and wakes the pacer
53// inline, never through a posted tick.
54func TestExplicitWaitFiresOnDisplayLoop(t *testing.T) {
55	e := newExplicitWaiter(t, nil)
56	w, err := e.sw.watch(e.point)
57	if err != nil || e.sw.fired(w) {
58		t.Fatalf("watch: %v fired=%v", err, e.sw.fired(w))
59	}
60	e.rm.EXPECT().Remove().Once()
61	one := [8]byte{1}
62	if _, err := unix.Write(e.fd, one[:]); err != nil {
63		t.Fatal(err)
64	}
65	fd := e.fd
66	e.fire()
67	if !e.sw.fired(w) || w.failed || e.paced != 1 || !fdClosed(fd) {
68		t.Fatalf("fired=%v failed=%v paced=%d closed=%v", e.sw.fired(w), w.failed, e.paced, fdClosed(fd))
69	}
70	e.sw.cancel(w) // applySync cancels after the fire: nothing more to release
71}
72
73// Cancelling before the fire removes the source, then closes the fd.
74func TestExplicitWaitCancelRemovesThenCloses(t *testing.T) {
75	e := newExplicitWaiter(t, nil)
76	w, err := e.sw.watch(e.point)
77	if err != nil {
78		t.Fatal(err)
79	}
80	e.rm.EXPECT().Remove().RunAndReturn(func() {
81		if fdClosed(e.fd) {
82			t.Error("fd closed before Remove")
83		}
84	}).Once()
85	e.sw.cancel(w)
86	if !e.sw.fired(w) || !fdClosed(e.fd) || e.paced != 0 {
87		t.Fatal("cancel left the wait registered")
88	}
89	e.sw.cancel(w)
90}
91
92func TestExplicitWaitAddFailureFails(t *testing.T) {
93	e := newExplicitWaiter(t, unix.ENOMEM)
94	w, err := e.sw.watch(e.point)
95	if err == nil || !e.sw.fired(w) || !w.failed || !fdClosed(e.fd) {
96		t.Fatalf("add failure: err=%v", err)
97	}
98}
99
100// A callback with nothing to read keeps the wait registered.
101func TestExplicitWaitSpuriousCallbackKeepsWaiting(t *testing.T) {
102	e := newExplicitWaiter(t, nil)
103	w, err := e.sw.watch(e.point)
104	if err != nil {
105		t.Fatal(err)
106	}
107	e.fire()
108	if e.sw.fired(w) || e.paced != 0 || fdClosed(e.fd) {
109		t.Fatal("spurious callback ended the wait")
110	}
111	e.rm.EXPECT().Remove().Once()
112	e.sw.cancel(w)
113}
114
115// Shutdown releases waits still registered, Remove before Close.
116func TestExplicitWaitReleasePending(t *testing.T) {
117	e := newExplicitWaiter(t, nil)
118	if _, err := e.sw.watch(e.point); err != nil {
119		t.Fatal(err)
120	}
121	e.rm.EXPECT().Remove().Once()
122	e.sw.releasePending()
123	if !fdClosed(e.fd) {
124		t.Fatal("pending wait leaked its eventfd")
125	}
126}
127
128// End to end: a queued commit stays queued until the callback runs, then
129// is applied by that same tick.
130func TestSyncobjCommitAppliedByAcquireCallback(t *testing.T) {
131	h := newSyncHarness(t)
132	h.releases = map[uint32]*syncReleaseProxy{}
133	src, rm := newMockacquireSource(t), newMockacquireRemover(t)
134	var fire func()
135	src.EXPECT().add(mock.Anything, mock.Anything).RunAndReturn(func(_ int, f func()) (acquireRemover, error) {
136		fire = f
137		return rm, nil
138	}).Once()
139	rm.EXPECT().Remove().Maybe()
140	h.s.display.Do(func() { h.s.syncWait.src = src })
141	a := h.dmabuf()
142	h.commit(a, 1, 2)
143	if _, ok := h.content(50 * time.Millisecond); ok {
144		t.Fatal("content before the acquire point fired")
145	}
146	h.fire(1) // the kernel signals: efd readable (display loop is mocked, so nothing reads it)
147	var applied, posted bool
148	h.s.display.Do(func() {
149		fire()
150		// Same tick: the commit left the queue without posting a pace tick.
151		applied = len(h.s.windows[h.win].xdg.surface.queue) == 0
152		posted = h.s.paceSignal.Load()
153	})
154	if !applied || posted {
155		t.Fatalf("acquire callback: applied=%v posted tick=%v, want applied inline", applied, posted)
156	}
157	got, ok := h.content(2 * time.Second)
158	if !ok || got.Acquire == nil {
159		t.Fatalf("content %+v", got)
160	}
161}

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 23

Tags 10

↑↓ moveenter openesc close