1package drm
2
3import (
4 "maps"
5 "sync/atomic"
6 "testing"
7
8 "github.com/stretchr/testify/mock"
9 "golang.org/x/sys/unix"
10)
11
12func TestInactiveOnCloseRequiresSuccessfulValidatedDisable(t *testing.T) {
13 for _, stage := range []string{"success", "commit-failure", "query-failure", "partial-primary", "partial-cursor", "partial-overlay", "partial-stray", "missing-active"} {
14 t.Run(stage, func(t *testing.T) {
15 var failures []error
16 if stage == "commit-failure" {
17 failures = []error{unix.EACCES}
18 }
19 o, k, commits := testOutput(t, failures...)
20 var state atomic.Uint64
21 securityGate(t, o, &state) // production gate, even unlocked: disable
22 o.off = true // stale cached state is never terminal evidence
23 o.inactiveOnClose = true // reset any earlier result at entry
24 o.saved = modeCrtc{crtcID: tCrtc, fbID: 123, modeValid: 1, mode: o.mode}
25 o.overlay = &plane{id: tOverlay, props: planeProps}
26 stray := &plane{id: 53, props: planeProps}
27 o.stray = []*plane{o.overlay, stray}
28 var readErr error
29 if stage == "query-failure" {
30 readErr = unix.EACCES
31 }
32 k.EXPECT().objProps(uint32(53), uint32(objPlane)).Return(map[string][2]uint64{"CRTC_ID": {pCrtcID, tCrtc}}, readErr).Once()
33 partial := func(p *plane) { p.props = maps.Clone(planeProps); p.props["FB_ID"] = 0 }
34 switch stage {
35 case "partial-primary":
36 partial(o.primary)
37 case "partial-cursor":
38 partial(o.cursor.plane)
39 case "partial-overlay":
40 partial(o.overlay)
41 case "partial-stray":
42 partial(stray)
43 case "missing-active":
44 o.crtcProps = maps.Clone(o.crtcProps)
45 o.crtcProps["ACTIVE"] = 0
46 }
47 k.EXPECT().rmFB(mock.Anything).Return(nil)
48 done := make(chan struct{})
49 go func() { o.Close(); close(done) }()
50 <-done // accessor is only read after owner-completion synchronization
51 if got := o.InactiveOnClose(); got != (stage == "success") {
52 t.Fatalf("inactiveOnClose=%v stage=%s", got, stage)
53 }
54 if len(*commits) != 1 {
55 t.Fatalf("commits %d", len(*commits))
56 }
57 if stage == "success" {
58 checkProtectedDisable(t, (*commits)[0], tPrimary, tCursor, tOverlay, 53)
59 }
60 })
61 }
62}
63
64func TestInactiveOnCloseSavedRestoreNeverCertifiesRemoval(t *testing.T) {
65 o, k, _ := testOutput(t)
66 o.saved = modeCrtc{crtcID: tCrtc, fbID: 123, modeValid: 1, mode: o.mode}
67 k.EXPECT().createBlob(mock.Anything).Return(99, nil).Once()
68 k.EXPECT().destroyBlob(uint32(99)).Return(nil).Once()
69 k.EXPECT().rmFB(mock.Anything).Return(nil)
70 o.Close()
71 if o.InactiveOnClose() {
72 t.Fatal("legacy saved desktop restoration certified inactivity")
73 }
74}