security_run_test.go RawHistoryFolder

1package drm
2
3import (
4	"context"
5	"os"
6	"sync/atomic"
7	"testing"
8	"testing/synctest"
9	"time"
10
11	portsmocks "github.com/bnema/neferwl/internal/mocks/ports"
12	"github.com/bnema/neferwl/internal/ports"
13	"github.com/stretchr/testify/mock"
14)
15
16func TestSecurityRunWakePendingResumeAndOff(t *testing.T) {
17	synctest.Test(t, securityRunWakePendingResumeAndOff)
18}
19
20func securityRunWakePendingResumeAndOff(t *testing.T) {
21	o, k, commits, mu := testOutputMu(t)
22	o.fbs = [2]uint32{} // the first setup of a run: no image yet
23	o.cursor, o.tearing = nil, false
24	var state atomic.Uint64
25	securityGate(t, o, &state)
26	events := make(chan ports.SecurityBackendEvent, 8)
27	o.SecurityEvents = events
28	wake := make(chan ports.SecurityState, 1)
29	o.SecurityChanges = wake
30	active := make(chan bool, 2)
31	flips := make(chan flipEvent, 2)
32	o.flipped = flips
33	scenes := make(chan ports.Scene, 2)
34	r := portsmocks.NewMockRenderer(t)
35	buf := func() ports.DMABuf {
36		f, w := protectionFence(t, false)
37		w.Close()
38		return ports.DMABuf{Planes: []ports.DMABufPlane{{File: f}}}
39	}
40	r.EXPECT().SetHDR(float64(0)).Return().Maybe()
41	r.EXPECT().ExportTargets(2, mock.Anything, false).Return([]ports.DMABuf{buf(), buf()}, nil).Once()
42	r.EXPECT().UseTarget(mock.Anything).Return()
43	black := make(chan struct{}, 8)
44	desktop := make(chan struct{}, 2)
45	r.EXPECT().Render(mock.Anything, mock.Anything).RunAndReturn(func(s ports.Scene, _ map[ports.WindowID]ports.SurfaceContent) (*os.File, error) {
46		if s.Seq == 99 {
47			desktop <- struct{}{}
48		} else {
49			black <- struct{}{}
50		}
51		return nil, nil
52	})
53	r.EXPECT().Close().Return().Once()
54	k.EXPECT().addFB(mock.Anything, uint32(fourccXRGB)).Return(70, nil)
55	k.EXPECT().createBlob(mock.Anything).Return(99, nil)
56	k.EXPECT().destroyBlob(mock.Anything).Return(nil).Maybe()
57	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
58	defer cancel()
59	done := make(chan error, 1)
60	go func() {
61		done <- o.Run(ctx, func(int, int) (ports.Renderer, error) { return r, nil }, nil, active, scenes, nil, nil, nil, discardSink(t), nil, nil)
62	}()
63	scenes <- ports.Scene{Seq: 99, Scale: 1}
64	select {
65	case <-desktop:
66	case <-ctx.Done():
67		t.Fatal("no desktop frame")
68	}
69	var frame commitRec
70	waitFor(t, func() bool {
71		mu.Lock()
72		defer mu.Unlock()
73		for _, c := range *commits {
74			if c.user&3 == userFrame {
75				frame = c
76				return true
77			}
78		}
79		return false
80	})
81	state.Store(3)
82	wake <- ports.SecurityState{} // stale payload: read gate, not payload
83	synctest.Wait()
84	select {
85	case ev := <-events:
86		t.Fatalf("proof while old frame pending: %+v", ev)
87	default:
88	}
89	flips <- eventOf(frame)
90	proof := func(kind ports.ProtectionKind) {
91		t.Helper()
92		for {
93			select {
94			case ev := <-events:
95				if p, ok := ev.(ports.SecurityOutputProof); ok {
96					if p.Proof != (ports.OutputProtection{Generation: 1, Instance: 77, Kind: kind}) {
97						t.Fatalf("proof %+v", p)
98					}
99					return
100				}
101			case <-ctx.Done():
102				t.Fatal("missing security proof")
103			}
104		}
105	}
106	proof(ports.ProtectionProtectedFrame)
107	scenes <- ports.Scene{Seq: 99} // held desktop scene cannot be replayed
108	synctest.Wait()
109	select {
110	case <-desktop:
111		t.Fatal("stale desktop rendered under protection")
112	default:
113	}
114	active <- false
115	select {
116	case ev := <-events:
117		if _, ok := ev.(ports.SecurityOutputInvalidated); !ok {
118			t.Fatalf("disable did not invalidate before resume: %+v", ev)
119		}
120	case <-ctx.Done():
121		t.Fatal("missing disable invalidation")
122	}
123	active <- true
124	select {
125	case ev := <-events:
126		if _, ok := ev.(ports.SecurityOutputInvalidated); !ok {
127			t.Fatalf("resume proof preceded invalidation: %+v", ev)
128		}
129	case <-ctx.Done():
130		t.Fatal("missing resume invalidation")
131	}
132	proof(ports.ProtectionProtectedFrame) // reacquire requires a fresh black transaction
133	scenes <- ports.Scene{Security: ports.SecurityState{Generation: 1, Protected: true}, Off: true}
134	proof(ports.ProtectionInactiveOutput)
135	cancel()
136	if err := <-done; err != nil {
137		t.Fatal(err)
138	}
139}

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