1package xkb
2
3import (
4 "testing"
5
6 "github.com/bnema/neferwl/internal/ports"
7)
8
9func secureKey(t *testing.T, k *Keymap, code uint32, down bool, state ports.SecurityState) ports.KeyEvent {
10 t.Helper()
11 ev, deliver, err := k.KeySecure(code, down, 123, state)
12 if err != nil || !deliver {
13 t.Fatalf("key %d down=%v: deliver=%v err=%v", code, down, deliver, err)
14 }
15 return ev
16}
17
18func suppressedKey(t *testing.T, k *Keymap, code uint32, down bool, state ports.SecurityState) {
19 t.Helper()
20 ev, deliver, err := k.KeySecure(code, down, 123, state)
21 if err != nil || deliver || ev != (ports.KeyEvent{}) {
22 t.Fatalf("inherited key %d down=%v leaked: %+v deliver=%v err=%v", code, down, ev, deliver, err)
23 }
24}
25
26func TestSecurityEpochQuarantinesHeldKeysAcrossAcquireAndRelease(t *testing.T) {
27 k := newTest(t, "us")
28 initial := ports.SecurityState{}
29 locked := ports.SecurityState{Generation: 1, Protected: true}
30 unlocked := ports.SecurityState{Generation: 2}
31 for _, code := range []uint32{125, 42, 29, 56, 30} {
32 secureKey(t, k, code, true, initial)
33 }
34 for _, code := range []uint32{125, 42, 29, 56, 30} {
35 suppressedKey(t, k, code, true, locked) // repeats cannot reintroduce held modifiers
36 }
37 if ev := secureKey(t, k, 16, true, locked); ev.Keysym != "q" || ev.Mods != 0 || ev.State != (ports.ModState{}) {
38 t.Fatalf("password inherited native state: %+v", ev)
39 }
40 secureKey(t, k, 16, false, locked)
41 secureKey(t, k, 54, true, locked) // password-era right Shift
42 if ev := secureKey(t, k, 16, true, unlocked); ev.Keysym != "q" || ev.Mods != 0 || ev.State != (ports.ModState{}) {
43 t.Fatalf("desktop inherited password/native state: %+v", ev)
44 }
45 secureKey(t, k, 16, false, unlocked)
46 for _, code := range []uint32{30, 56, 29, 42, 125, 54} {
47 suppressedKey(t, k, code, false, unlocked)
48 }
49 secureKey(t, k, 125, true, unlocked)
50 if ev := secureKey(t, k, 16, true, unlocked); ev.Mods&ports.ModSuper == 0 || ev.State.Depressed == 0 {
51 t.Fatalf("fresh desktop Super broken: %+v", ev)
52 }
53}
54
55func TestSecurityEpochResetsLocksButFreshCapsAndNumLockWork(t *testing.T) {
56 k := newTest(t, "us")
57 initial := ports.SecurityState{}
58 locked := ports.SecurityState{Generation: 1, Protected: true}
59 for _, code := range []uint32{58, 69} {
60 secureKey(t, k, code, true, initial)
61 secureKey(t, k, code, false, initial)
62 }
63 if ev := secureKey(t, k, 30, true, initial); ev.Keysym != "A" || ev.State.Locked == 0 {
64 t.Fatalf("baseline caps: %+v", ev)
65 }
66 secureKey(t, k, 30, false, initial)
67 if ev := secureKey(t, k, 30, true, locked); ev.Keysym != "a" || ev.State.Locked != 0 {
68 t.Fatalf("old locks crossed epoch: %+v", ev)
69 }
70 secureKey(t, k, 30, false, locked)
71 secureKey(t, k, 58, true, locked)
72 secureKey(t, k, 58, false, locked)
73 if ev := secureKey(t, k, 30, true, locked); ev.Keysym != "A" || ev.State.Locked == 0 {
74 t.Fatalf("fresh protected caps broken: %+v", ev)
75 }
76 secureKey(t, k, 30, false, locked)
77 secureKey(t, k, 69, true, locked)
78 secureKey(t, k, 69, false, locked)
79 if ev := secureKey(t, k, 79, true, locked); ev.Keysym != "KP_1" || ev.State.Locked == 0 {
80 t.Fatalf("fresh protected numlock broken: %+v", ev)
81 }
82}
83
84func TestSecurityEpochUsesActualRemappedModifiers(t *testing.T) {
85 k, err := New(RMLVO{Layout: "us", Options: "ctrl:swapcaps"})
86 if err != nil {
87 t.Fatal(err)
88 }
89 defer k.Close()
90 initial, locked := ports.SecurityState{}, ports.SecurityState{Generation: 1, Protected: true}
91 secureKey(t, k, 58, true, initial) // physical Caps is Control in this map
92 if ev := secureKey(t, k, 16, true, locked); ev.Mods != 0 || ev.State != (ports.ModState{}) {
93 t.Fatalf("remapped inherited Control leaked: %+v", ev)
94 }
95 secureKey(t, k, 16, false, locked)
96 suppressedKey(t, k, 58, false, locked)
97 secureKey(t, k, 58, true, locked)
98 if ev := secureKey(t, k, 16, true, locked); ev.Mods&ports.ModCtrl == 0 || ev.State.Depressed == 0 {
99 t.Fatalf("fresh remapped Control broken: %+v", ev)
100 }
101 secureKey(t, k, 16, false, locked)
102 secureKey(t, k, 58, false, locked)
103 secureKey(t, k, 29, true, locked) // physical Control is Caps in this map
104 secureKey(t, k, 29, false, locked)
105 if ev := secureKey(t, k, 30, true, locked); ev.Keysym != "A" || ev.State.Locked == 0 {
106 t.Fatalf("fresh remapped Caps broken: %+v", ev)
107 }
108}
109
110func TestSecurityEpochAltGrAndAltReleaseOrder(t *testing.T) {
111 for _, releases := range [][]uint32{{100, 56}, {56, 100}} {
112 k := newTest(t, "fr")
113 locked := ports.SecurityState{Generation: 1, Protected: true}
114 secureKey(t, k, 100, true, locked) // ISO_Level3_Shift, not guessed Alt
115 secureKey(t, k, 56, true, locked)
116 if ev := secureKey(t, k, 11, true, locked); ev.Keysym != "at" || ev.Mods&ports.ModAlt == 0 || ev.State.Depressed == 0 {
117 t.Fatalf("fresh AltGr+Alt not mapped by XKB: %+v", ev)
118 }
119 secureKey(t, k, 11, false, locked)
120 for _, code := range releases {
121 secureKey(t, k, code, false, locked)
122 }
123 if ev := secureKey(t, k, 16, true, locked); ev.Keysym != "a" || ev.Mods != 0 || ev.State.Depressed != 0 {
124 t.Fatalf("release order left modifiers: %+v", ev)
125 }
126 }
127}
128
129func TestKeymapReplacementRetainsQuarantine(t *testing.T) {
130 previous := newTest(t, "us")
131 locked := ports.SecurityState{Generation: 1, Protected: true}
132 secureKey(t, previous, 125, true, locked)
133 next := newTest(t, "fr")
134 next.QuarantineFrom(previous)
135 suppressedKey(t, next, 125, true, locked)
136 if ev := secureKey(t, next, 16, true, locked); ev.Keysym != "a" || ev.Mods != 0 || ev.State.Depressed != 0 {
137 t.Fatalf("replacement replayed old held state: %+v", ev)
138 }
139 suppressedKey(t, next, 125, false, locked)
140 secureKey(t, next, 125, true, locked)
141 if ev := secureKey(t, next, 30, true, locked); ev.Mods&ports.ModSuper == 0 {
142 t.Fatalf("fresh key after replacement broken: %+v", ev)
143 }
144}
145
146func TestSecurityStateChangeResetsAltGrAndAltWithoutMaskInference(t *testing.T) {
147 k := newTest(t, "fr")
148 initial := ports.SecurityState{}
149 locked := ports.SecurityState{Generation: 1, Protected: true}
150 secureKey(t, k, 100, true, initial)
151 secureKey(t, k, 56, true, initial)
152 if ev := secureKey(t, k, 11, true, locked); ev.Keysym != "agrave" || ev.Mods != 0 || ev.State != (ports.ModState{}) {
153 t.Fatalf("inherited AltGr/Alt crossed acquisition: %+v", ev)
154 }
155 secureKey(t, k, 11, false, locked)
156 suppressedKey(t, k, 56, false, locked)
157 suppressedKey(t, k, 100, false, locked)
158 secureKey(t, k, 100, true, locked)
159 // Full state comparison is conservative even if a caller changes the
160 // protection bit without advancing the generation.
161 changed := locked
162 changed.Protected = false
163 if ev := secureKey(t, k, 11, true, changed); ev.Keysym != "agrave" || ev.Mods != 0 || ev.State != (ports.ModState{}) {
164 t.Fatalf("state change missed native reset: %+v", ev)
165 }
166 suppressedKey(t, k, 100, false, changed)
167}