v0.1.0 47aaa0f

wait_test.go RawHistoryFolder

1//go:build linux
2
3package syncobj
4
5import (
6	"errors"
7	"os"
8	"strings"
9	"testing"
10	"time"
11
12	"golang.org/x/sys/unix"
13)
14
15func TestWaitResultMapping(t *testing.T) {
16	if ok, err := waitResult(0); !ok || err != nil {
17		t.Fatalf("success: got %v, %v", ok, err)
18	}
19	for _, e := range []unix.Errno{unix.ETIME, unix.EBUSY} {
20		if ok, err := waitResult(e); ok || err != nil {
21			t.Fatalf("%v: want (false,nil), got %v, %v", e, ok, err)
22		}
23	}
24	for _, e := range []unix.Errno{unix.EINTR, unix.EBADF, unix.EINVAL, unix.ENOENT, unix.ENOTTY, unix.EFAULT} {
25		ok, err := waitResult(e)
26		if ok || err == nil {
27			t.Fatalf("%v: want error, got %v, %v", e, ok, err)
28		}
29		if !errors.Is(err, e) {
30			t.Errorf("%v: error does not wrap errno: %v", e, err)
31		}
32		if !strings.Contains(err.Error(), "DRM syncobj ioctl 0xc03064ca") {
33			t.Errorf("%v: lost request context: %v", e, err)
34		}
35	}
36}
37
38func TestWaitResultIdleDoesNotAllocate(t *testing.T) {
39	for _, e := range []unix.Errno{unix.ETIME, unix.EBUSY} {
40		if n := testing.AllocsPerRun(1000, func() { _, _ = waitResult(e) }); n != 0 {
41			t.Errorf("%v: %v allocs per idle result, want 0", e, n)
42		}
43	}
44}
45
46func TestWaitPointBadFDKeepsRealError(t *testing.T) {
47	for _, fd := range []int{-1, 1 << 30} {
48		n := &Node{fd: fd}
49		ok, err := n.WaitPoint(1, 1, 20*time.Millisecond)
50		if ok || !errors.Is(err, unix.EBADF) {
51			t.Fatalf("fd %d: want (false, EBADF), got %v, %v", fd, ok, err)
52		}
53		if !strings.Contains(err.Error(), "DRM syncobj ioctl") {
54			t.Errorf("fd %d: lost context: %v", fd, err)
55		}
56	}
57	// Closed Node (fd -1 after Close) must also surface EBADF.
58	n := &Node{fd: -1}
59	if _, err := n.Signaled(1, 1); !errors.Is(err, unix.EBADF) {
60		t.Fatalf("Signaled on closed node: %v", err)
61	}
62}
63
64func TestWaitPointNonDRMFDIsNotSwallowed(t *testing.T) {
65	fd, err := unix.Eventfd(0, unix.EFD_CLOEXEC)
66	if err != nil {
67		t.Fatal(err)
68	}
69	defer unix.Close(fd)
70	n := &Node{fd: fd}
71	if ok, err := n.WaitPoint(1, 1, 0); ok || !errors.Is(err, unix.ENOTTY) {
72		t.Fatalf("want (false, ENOTTY), got %v, %v", ok, err)
73	}
74}
75
76func TestWaitPointTimeoutBounds(t *testing.T) {
77	n := &Node{fd: -1}
78	for _, d := range []time.Duration{-1, time.Second + 1} {
79		if _, err := n.WaitPoint(1, 1, d); err == nil || errors.Is(err, unix.EBADF) {
80			t.Errorf("timeout %s: want bounds error before ioctl, got %v", d, err)
81		}
82	}
83	// Boundaries stay accepted (fail later at the ioctl with EBADF).
84	for _, d := range []time.Duration{0, time.Second} {
85		if _, err := n.WaitPoint(1, 1, d); !errors.Is(err, unix.EBADF) {
86			t.Errorf("timeout %s: want EBADF, got %v", d, err)
87		}
88	}
89}
90
91// openBenchNode opts in to a real render node, like the other hardware tests.
92func openBenchNode(tb testing.TB) *Node {
93	path := os.Getenv("NEFERGUI_RENDER_NODE")
94	if path == "" {
95		tb.Skip("set NEFERGUI_RENDER_NODE=/dev/dri/renderD... for real idle-wait measurement")
96	}
97	n, err := Open(path)
98	if err != nil {
99		tb.Fatal(err)
100	}
101	tb.Cleanup(func() { _ = n.Close() })
102	return n
103}
104
105func TestWaitPointIdleTimeoutRealNodeNoAllocs(t *testing.T) {
106	n := openBenchNode(t)
107	h, err := n.Create()
108	if err != nil {
109		t.Fatal(err)
110	}
111	defer n.Destroy(h)
112	ready, err := n.WaitPoint(h, 1, 0)
113	if ready || err != nil {
114		t.Fatalf("idle unsignaled point: got %v, %v", ready, err)
115	}
116	if a := testing.AllocsPerRun(200, func() { _, _ = n.WaitPoint(h, 1, 0) }); a != 0 {
117		t.Errorf("idle WaitPoint allocs/op = %v, want 0", a)
118	}
119}
120
121func BenchmarkWaitResultIdle(b *testing.B) {
122	b.ReportAllocs()
123	for i := 0; i < b.N; i++ {
124		_, _ = waitResult(unix.ETIME)
125	}
126}
127
128// BenchmarkWaitPointIdle measures the presentation waiter's idle path on a
129// real render node: an unsignaled point that times out (ETIME/EBUSY).
130func BenchmarkWaitPointIdle(b *testing.B) {
131	n := openBenchNode(b)
132	h, err := n.Create()
133	if err != nil {
134		b.Fatal(err)
135	}
136	defer n.Destroy(h)
137	b.ReportAllocs()
138	b.ResetTimer()
139	for i := 0; i < b.N; i++ {
140		if ready, err := n.WaitPoint(h, 1, 20*time.Millisecond); ready || err != nil {
141			b.Fatalf("got %v, %v", ready, err)
142		}
143	}
144}

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 12

↑↓ moveenter openesc close