The laptop's model module owned triggerhappy's trigger file and service, although the daemon is a general piece others have keys for. triggerhappy now owns the daemon, reads only the mesh's file, runs every trigger as the account, and the model module contributes its vendor keys.
107 lines
3.4 KiB
Go
107 lines
3.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func put(t *testing.T, root, p, content string) {
|
|
t.Helper()
|
|
full := filepath.Join(root, p)
|
|
if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(full, []byte(content), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// The module's shipped triggers and i3 lines, read back as keys: what each runs and where.
|
|
func TestKeysListsTriggersI3LinesAndFirmwareKeys(t *testing.T) {
|
|
f := newFake(t)
|
|
m := readManifest(t)
|
|
content := map[string]string{}
|
|
for _, r := range m.Resources {
|
|
if c, ok := r["content"].(string); ok {
|
|
content[r["id"].(string)] = c
|
|
}
|
|
}
|
|
put(t, f.root, TriggerDir+"/mesh.conf", "# asus-zephyrus-g14\n"+m.triggers())
|
|
home := "/home/operator"
|
|
t.Setenv("MESH_OPERATOR_HOME", home)
|
|
put(t, f.root, home+"/"+I3Fragments[0], content["i3-vendor-keys"])
|
|
put(t, f.root, home+"/"+I3Fragments[1], content["i3-model"])
|
|
|
|
r := f.machine().Keys()
|
|
got := map[string]Key{}
|
|
for _, k := range r.Keys {
|
|
got[k.Key+"|"+k.When] = k
|
|
}
|
|
if k := got["KEY_F21|pressed"]; !strings.Contains(k.Runs, "zephyrus-touchpad reset") || k.Physical == "" {
|
|
t.Errorf("touchpad key: %+v", k)
|
|
}
|
|
if k := got["KEY_PROG1|pressed"]; !strings.Contains(k.Runs, "zephyrus-media play-pause") {
|
|
t.Errorf("media key: %+v", k)
|
|
}
|
|
if k := got["$mod+Shift+s|released"]; !strings.Contains(k.Runs, "screenshot") || !strings.Contains(k.Physical, "Fn+F6") {
|
|
t.Errorf("screenshot key: %+v", k)
|
|
}
|
|
if k := got["$mod+p|pressed"]; !strings.Contains(k.Runs, "zephyrus-display order") {
|
|
t.Errorf("display key: %+v", k)
|
|
}
|
|
if k := got["(session start)|at login"]; k.Runs == "" {
|
|
t.Errorf("no session-start line read")
|
|
}
|
|
if k := got["Fn+F2 / Fn+F3|"]; k.From != "firmware" {
|
|
t.Errorf("firmware keys missing: %+v", r.Keys)
|
|
}
|
|
if len(r.Warnings) != 0 || len(r.NotRead) != 0 {
|
|
t.Errorf("warnings %v, not read %v", r.Warnings, r.NotRead)
|
|
}
|
|
if _, err := json.Marshal(r); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// A key bound in two trigger files fires twice, which is said.
|
|
func TestKeysWarnsAboutAKeyInTwoTriggerFiles(t *testing.T) {
|
|
f := newFake(t)
|
|
put(t, f.root, TriggerDir+"/asus-g14.conf", "KEY_F21\t1\t/x reset\n")
|
|
put(t, f.root, TriggerDir+"/old.conf", "KEY_F21\t1\t/y reset\n")
|
|
t.Setenv("MESH_OPERATOR_HOME", "")
|
|
r := f.machine().Keys()
|
|
if len(r.Warnings) != 1 || !strings.Contains(r.Warnings[0], "KEY_F21") || len(r.NotRead) != 1 {
|
|
t.Errorf("warnings %v, not read %v", r.Warnings, r.NotRead)
|
|
}
|
|
}
|
|
|
|
// The check names what still calls as-user, and whether triggerhappy runs as the account.
|
|
func TestCheckFindsAsUserAndTriggerhappysAccount(t *testing.T) {
|
|
f := newFake(t)
|
|
put(t, f.root, "/etc/udev/rules.d/91-monitor-hotplug.rules", `RUN+="/x/scripts/as-user setsid wizard"`)
|
|
put(t, f.root, "/proc/4242/comm", "thd\n")
|
|
put(t, f.root, "/proc/4242/status", "Name:\tthd\nUid:\t0\t0\t0\t0\n")
|
|
was := operatorUID
|
|
operatorUID = func() int { return 1000 }
|
|
defer func() { operatorUID = was }()
|
|
t.Setenv("MESH_OPERATOR_HOME", "")
|
|
r := f.machine().Check(context.Background(), nil)
|
|
by := map[string]Check{}
|
|
for _, c := range r.Checks {
|
|
by[c.Name] = c
|
|
}
|
|
if c := by["no as-user"]; c.OK || !strings.Contains(c.Detail, "91-monitor-hotplug.rules") {
|
|
t.Errorf("as-user: %+v", c)
|
|
}
|
|
if c := by["triggerhappy as the account"]; c.OK || !strings.Contains(c.Detail, "uid 0") {
|
|
t.Errorf("triggerhappy: %+v", c)
|
|
}
|
|
if c := by["touchpad after resume"]; c.OK {
|
|
t.Errorf("resume: %+v", c)
|
|
}
|
|
}
|