syncwait_test.go RawHistoryFolder

1package wayland
2
3import (
4	"bytes"
5	"context"
6	"math"
7	"os"
8	"testing"
9	"time"
10
11	"github.com/bnema/neferwl/internal/ports"
12	"github.com/bnema/zerowrap"
13)
14
15func TestSyncWaitAlreadySignalled(t *testing.T) {
16	dev := newMocksyncobjDevice(t)
17	dev.EXPECT().signalled(uint32(7), uint64(4)).Return(true, nil).Once()
18	sw, err := newSyncWaiter(func() {})
19	if err != nil {
20		t.Fatal(err)
21	}
22	defer sw.pipeR.Close()
23	defer sw.close()
24	w, err := sw.watch(syncPoint{tl: &timeline{dev: dev, handle: 7}, point: 4})
25	if err != nil || !sw.fired(w) {
26		t.Fatalf("wait: %v, ready: %v", err, sw.fired(w))
27	}
28}
29
30// An invalid poll result is an error, never permission to publish.
31func TestImplicitPollErrorRejectedAndWarnOnce(t *testing.T) {
32	sw, err := newSyncWaiter(func() {})
33	if err != nil {
34		t.Fatal(err)
35	}
36	defer sw.pipeR.Close()
37	defer sw.close()
38	var output bytes.Buffer
39	sw.log = zerowrap.New(zerowrap.Config{Output: &output})
40	// An fd number no process can own polls as POLLNVAL; its finalizer
41	// cannot close a reused descriptor.
42	b := &ports.DMABuf{Planes: []ports.DMABufPlane{{File: os.NewFile(uintptr(math.MaxInt32), "invalid")}}}
43	var waits [4]*syncWait
44	for range 2 {
45		if err := sw.watchImplicit(b, &waits); err == nil || waits[0] != nil {
46			t.Fatalf("poll error should reject without registering wait: %v %+v", err, waits)
47		}
48	}
49	if n := bytes.Count(output.Bytes(), []byte("implicit fence poll error")); n != 1 {
50		t.Fatalf("poll warnings %d: %s", n, output.String())
51	}
52}
53
54// POLLHUP with no POLLIN on a watched fd must wake the surface too.
55func TestImplicitPollHangupWakes(t *testing.T) {
56	sw, err := newSyncWaiter(func() {})
57	if err != nil {
58		t.Fatal(err)
59	}
60	ctx, cancel := context.WithCancel(context.Background())
61	done := make(chan struct{})
62	go func() { sw.run(ctx); close(done) }()
63	defer func() { cancel(); sw.close(); <-done }()
64	r, wr, err := os.Pipe()
65	if err != nil {
66		t.Fatal(err)
67	}
68	defer r.Close()
69	b := &ports.DMABuf{Planes: []ports.DMABufPlane{{File: r}}}
70	var waits [4]*syncWait
71	if err := sw.watchImplicit(b, &waits); err != nil || waits[0] == nil {
72		t.Fatalf("watch: %v", err)
73	}
74	wr.Close()
75	deadline := time.Now().Add(time.Second)
76	for !sw.fired(waits[0]) && time.Now().Before(deadline) {
77		time.Sleep(time.Millisecond)
78	}
79	if !sw.fired(waits[0]) || !waits[0].failed {
80		t.Fatal("hangup must wake waiter as failed, not ready to publish")
81	}
82}
83
84func TestImplicitWaitAndDrop(t *testing.T) {
85	sw, err := newSyncWaiter(func() {})
86	if err != nil {
87		t.Fatal(err)
88	}
89	ctx, cancel := context.WithCancel(context.Background())
90	done := make(chan struct{})
91	go func() { sw.run(ctx); close(done) }()
92	defer func() { cancel(); sw.close(); <-done }()
93	r, wr, err := os.Pipe()
94	if err != nil {
95		t.Fatal(err)
96	}
97	defer r.Close()
98	defer wr.Close()
99	b := &ports.DMABuf{Planes: []ports.DMABufPlane{{File: r}}}
100	var waits [4]*syncWait
101	err = sw.watchImplicit(b, &waits)
102	if err != nil || waits[0] == nil || sw.fired(waits[0]) {
103		t.Fatalf("not ready: %v", err)
104	}
105	sw.cancel(waits[0])
106	waits = [4]*syncWait{}
107	err = sw.watchImplicit(b, &waits)
108	if err != nil || waits[0] == nil || sw.fired(waits[0]) {
109		t.Fatalf("not ready: %v", err)
110	}
111	if _, err := wr.Write([]byte{1}); err != nil {
112		t.Fatal(err)
113	}
114	deadline := time.Now().Add(time.Second)
115	for !sw.fired(waits[0]) && time.Now().Before(deadline) {
116		time.Sleep(time.Millisecond)
117	}
118	if !sw.fired(waits[0]) {
119		t.Fatal("readable buffer never became ready")
120	}
121	// A newly attached readable buffer is ready without a poll goroutine hop.
122	waits = [4]*syncWait{}
123	err = sw.watchImplicit(b, &waits)
124	if err != nil || waits[0] != nil {
125		t.Fatalf("ready: %v, waits: %+v", err, waits)
126	}
127}

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