v0.4.0 b6be3a8

frame_lifecycle_test.go RawHistoryFolder

1package drm
2
3import (
4	"sync/atomic"
5	"testing"
6	"time"
7)
8
9// All transitions below use values only: no event reader, timer, clock or
10// KMS mock is needed to prove the serial and deadline rules.
11func TestFrameLifecycleStaleFlipAndRecovery(t *testing.T) {
12	var serials atomic.Uint64
13	l := frameLifecycle{serials: &serials}
14	start := time.Unix(100, 0)
15	old := l.userData(userFrame)
16	l.begin(pendingFrame{frame: true, queued: 7}, start)
17	l.endPending() // successful recovery modeset invalidates the old event
18	current := l.userData(userFrame)
19	l.begin(pendingFrame{frame: true, queued: 8}, start.Add(time.Millisecond))
20	if _, _, ok := l.flip(old); ok || !l.pending {
21		t.Fatal("old event completed new commit")
22	}
23	f, _, ok := l.flip(current)
24	if !ok || l.pending || !f.frame || f.queued != 8 {
25		t.Fatalf("new event: ok=%v pending=%v frame=%+v", ok, l.pending, f)
26	}
27}
28
29func TestFrameLifecycleBusyRetryAndLimit(t *testing.T) {
30	var serials atomic.Uint64
31	l := frameLifecycle{serials: &serials, stuckAfter: 200 * time.Millisecond}
32	start := time.Unix(100, 0)
33	if !l.busy(start) || l.stuckAt != start.Add(busyRetry) {
34		t.Fatal("initial EBUSY did not arm retry")
35	}
36	if got := l.timeout(start.Add(busyRetry-time.Nanosecond), true); got != timeoutEarly {
37		t.Fatalf("premature deadline: %d", got)
38	}
39	if got := l.timeout(start.Add(busyRetry), true); got != timeoutRetry || l.pending {
40		t.Fatalf("first retry: %d pending %v", got, l.pending)
41	}
42	l.busy(start.Add(busyRetry))
43	if _, _, ok := l.flip(999 << userKindBits); !ok || l.pending {
44		t.Fatal("CRTC event did not unblock EBUSY")
45	}
46	for i := 3; i < 6; i++ {
47		l.busy(start.Add(time.Duration(i) * busyRetry))
48		if got := l.timeout(start.Add(time.Duration(i+1)*busyRetry), true); got != timeoutRetry {
49			t.Fatalf("busy retry %d: %d", i, got)
50		}
51	}
52	l.busy(start.Add(6 * busyRetry))
53	if got := l.timeout(start.Add(7*busyRetry), true); got != timeoutBusy {
54		t.Fatalf("long EBUSY did not request modeset: %d", got)
55	}
56	if !l.busy(start.Add(time.Second)) {
57		t.Fatal("later refusal did not start a new busy run")
58	}
59	if got := l.timeout(start.Add(time.Second+busyRetry), true); got != timeoutRetry {
60		t.Fatalf("new busy run: %d", got)
61	}
62}
63
64func TestFrameLifecycleFenceAndMissingEvent(t *testing.T) {
65	var serials atomic.Uint64
66	l := frameLifecycle{serials: &serials, stuckAfter: 100 * time.Millisecond}
67	start := time.Unix(100, 0)
68	user := l.userData(userFrame)
69	l.begin(pendingFrame{frame: true}, start)
70	if got := l.timeout(start.Add(100*time.Millisecond), false); got != timeoutFence || !l.pending || l.stuckAt != start.Add(200*time.Millisecond) {
71		t.Fatalf("unsignalled fence: %d pending %v deadline %v", got, l.pending, l.stuckAt)
72	}
73	if got := l.timeout(start.Add(200*time.Millisecond), false); got != timeoutEarly || !l.pending {
74		t.Fatalf("repeated fence wait: %d", got)
75	}
76	if got := l.timeout(start.Add(300*time.Millisecond), true); got != timeoutMissing || l.pending {
77		t.Fatalf("missing event after fence: %d pending %v", got, l.pending)
78	}
79	if _, _, ok := l.flip(user); ok {
80		t.Fatal("late event completed abandoned commit")
81	}
82}
83
84func TestFrameLifecycleTransitionsAllocations(t *testing.T) {
85	var serials atomic.Uint64
86	l := frameLifecycle{serials: &serials}
87	start := time.Unix(100, 0)
88	if allocs := testing.AllocsPerRun(100, func() {
89		user := l.userData(userFrame)
90		l.begin(pendingFrame{frame: true}, start)
91		l.flip(user)
92		l.busy(start)
93		l.timeout(start.Add(busyRetry), true)
94	}); allocs != 0 {
95		t.Fatalf("frame lifecycle: %.1f allocs, want 0", allocs)
96	}
97}

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 25

Tags 10

↑↓ moveenter openesc close