main f2d166b

power_test.go RawHistoryFolder

1package drm
2
3import (
4	"context"
5	"os"
6	"testing"
7	"testing/synctest"
8
9	portsmocks "github.com/bnema/neferwl/internal/mocks/ports"
10	"github.com/bnema/neferwl/internal/ports"
11	"github.com/stretchr/testify/mock"
12)
13
14// A scene with Off turns the CRTC inactive and draws nothing; the next
15// scene without Off modesets (ACTIVE=1) and draws a frame.
16func TestRunOutputPower(t *testing.T) { synctest.Test(t, runOutputPower) }
17
18func runOutputPower(t *testing.T) {
19	o, k, commits, commitMu := testOutputMu(t)
20	o.tearing, o.cursor = false, nil
21	flips := make(chan flipEvent, 1)
22	o.flipped = flips
23	r := portsmocks.NewMockRenderer(t)
24	buf := func() ports.DMABuf {
25		f, w, _ := os.Pipe()
26		w.Close()
27		return ports.DMABuf{Planes: []ports.DMABufPlane{{File: f}}}
28	}
29	r.EXPECT().SetHDR(float64(0)).Return().Maybe()
30	r.EXPECT().ExportTargets(2, mock.Anything, false).Return([]ports.DMABuf{buf(), buf()}, nil).Once()
31	r.EXPECT().UseTarget(mock.Anything).Return()
32	renders := make(chan struct{}, 8)
33	r.EXPECT().Render(mock.Anything, mock.Anything).RunAndReturn(func(ports.Scene, map[ports.WindowID]ports.SurfaceContent) (*os.File, error) {
34		renders <- struct{}{}
35		return nil, nil
36	})
37	r.EXPECT().Close().Return().Once()
38	k.EXPECT().addFB(mock.Anything, uint32(fourccXRGB)).Return(70, nil).Twice()
39	k.EXPECT().createBlob(mock.Anything).Return(99, nil)
40	k.EXPECT().destroyBlob(mock.Anything).Return(nil).Maybe()
41	k.EXPECT().rmFB(mock.Anything).Return(nil).Maybe()
42	snapshot := func() []commitRec {
43		commitMu.Lock()
44		defer commitMu.Unlock()
45		return append([]commitRec(nil), *commits...)
46	}
47	active := func(c commitRec) (uint64, bool) { return c.req.value(tCrtc, pActive) }
48	scenes := make(chan ports.Scene, 1)
49	vt := make(chan bool)
50	ctx, cancel := context.WithCancel(context.Background())
51	done := make(chan error, 1)
52	go func() {
53		done <- o.Run(ctx, func(int, int) (ports.Renderer, error) { return r, nil }, nil, vt, scenes, nil, nil, nil, discardSink(t), nil, nil)
54	}()
55	on := ports.Scene{OutputWidth: 200, OutputHeight: 100, Scale: 1}
56	scenes <- on
57	<-renders
58	var frame commitRec
59	waitFor(t, func() bool {
60		for _, c := range snapshot() {
61			if c.user&3 == userFrame {
62				frame = c
63				return true
64			}
65		}
66		return false
67	})
68	flips <- eventOf(frame)
69
70	off := on
71	off.Off = true
72	scenes <- off
73	waitFor(t, func() bool {
74		cs := snapshot()
75		v, ok := active(cs[len(cs)-1])
76		return ok && v == 0
77	})
78	n := len(snapshot())
79	// Scenes while off draw nothing (renders before the power off drained).
80	for len(renders) > 0 {
81		<-renders
82	}
83	scenes <- off
84	synctest.Wait()
85	select {
86	case <-renders:
87		t.Fatal("rendered while off")
88	default:
89	}
90	if len(snapshot()) != n {
91		t.Fatal("committed while off")
92	}
93
94	// A VT switch back modesets while off: the CRTC stays inactive.
95	vt <- false
96	vt <- true
97	waitFor(t, func() bool { return len(snapshot()) > n })
98	synctest.Wait()
99	for _, c := range snapshot()[n:] {
100		if v, ok := active(c); ok && v != 0 && c.flags&atomicTestOnly == 0 {
101			t.Fatal("VT resume turned the display on")
102		}
103	}
104	n = len(snapshot())
105
106	scenes <- on
107	<-renders
108	waitFor(t, func() bool {
109		for _, c := range snapshot()[n:] {
110			if v, ok := active(c); ok && v == 1 {
111				return true
112			}
113		}
114		return false
115	})
116	cancel()
117	if err := <-done; err != nil {
118		t.Fatal(err)
119	}
120}
121
122// An output started off (it reconnected while turned off) never activates
123// its CRTC before a scene turns it on; its tearing probe, refused by an
124// inactive CRTC, waits until then.
125func TestRunOutputStartOff(t *testing.T) { synctest.Test(t, runOutputStartOff) }
126
127func runOutputStartOff(t *testing.T) {
128	o, k, commits, commitMu := testOutputMu(t)
129	o.tearing, o.cursor = true, nil
130	o.StartOff = true
131	ready := make(chan error, 1)
132	o.ready = ready
133	r := portsmocks.NewMockRenderer(t)
134	buf := func() ports.DMABuf {
135		f, w, _ := os.Pipe()
136		w.Close()
137		return ports.DMABuf{Planes: []ports.DMABufPlane{{File: f}}}
138	}
139	r.EXPECT().SetHDR(float64(0)).Return().Maybe()
140	r.EXPECT().ExportTargets(2, mock.Anything, false).Return([]ports.DMABuf{buf(), buf()}, nil).Once()
141	r.EXPECT().UseTarget(mock.Anything).Return().Maybe()
142	renders := make(chan struct{}, 8)
143	r.EXPECT().Render(mock.Anything, mock.Anything).RunAndReturn(func(ports.Scene, map[ports.WindowID]ports.SurfaceContent) (*os.File, error) {
144		renders <- struct{}{}
145		return nil, nil
146	}).Maybe()
147	r.EXPECT().Close().Return().Once()
148	k.EXPECT().addFB(mock.Anything, uint32(fourccXRGB)).Return(70, nil).Twice()
149	k.EXPECT().createBlob(mock.Anything).Return(99, nil)
150	k.EXPECT().destroyBlob(mock.Anything).Return(nil).Maybe()
151	k.EXPECT().rmFB(mock.Anything).Return(nil).Maybe()
152	snapshot := func() []commitRec {
153		commitMu.Lock()
154		defer commitMu.Unlock()
155		return append([]commitRec(nil), *commits...)
156	}
157	lit := func(cs []commitRec) bool {
158		for _, c := range cs {
159			if v, ok := c.req.value(tCrtc, pActive); ok && v != 0 && c.flags&atomicTestOnly == 0 {
160				return true
161			}
162		}
163		return false
164	}
165	probed := func(cs []commitRec) bool {
166		for _, c := range cs {
167			if c.flags == atomicTestOnly|flipAsyncFlag {
168				return true
169			}
170		}
171		return false
172	}
173	scenes := make(chan ports.Scene, 1)
174	ctx, cancel := context.WithCancel(context.Background())
175	done := make(chan error, 1)
176	go func() {
177		done <- o.Run(ctx, func(int, int) (ports.Renderer, error) { return r, nil }, nil, make(chan bool), scenes, nil, nil, nil, discardSink(t), nil, nil)
178	}()
179	if err := <-ready; err != nil {
180		t.Fatal(err)
181	}
182	for len(renders) > 0 { // the images are cleared at setup
183		<-renders
184	}
185	off := ports.Scene{OutputWidth: 200, OutputHeight: 100, Scale: 1, Off: true}
186	scenes <- off
187	synctest.Wait()
188	if lit(snapshot()) {
189		t.Fatal("a display started off lit up")
190	}
191	if probed(snapshot()) {
192		t.Fatal("tearing probed while off")
193	}
194	if len(renders) > 0 {
195		t.Fatal("rendered while off")
196	}
197	on := off
198	on.Off = false
199	scenes <- on
200	waitFor(t, func() bool { return lit(snapshot()) && probed(snapshot()) })
201	cancel()
202	if err := <-done; err != nil {
203		t.Fatal(err)
204	}
205}

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