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) // The i3 module's file as the controller composes it: other modules' lines around this one's. var own string for _, c := range m.Contributions { if c.Seat == "node-display-session" { own += c.Content } } put(t, f.root, home+"/"+I3Config, "set $mod Mod4\n# rofi\nbindsym $mod+d exec rofi\n# "+ModuleName+"\n"+own+ "# triggerhappy-not-here\nbindsym $mod+Shift+z exec other\n#########################################\ninclude x\n") 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) } }