v0.4.0 b6be3a8

power_test.go RawHistoryFolder

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

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