rofi, clipmenu, feh, i3status-rust and the laptop's model module wrote files into i3's config.d, naming no dependency on the window manager. They now contribute their lines; i3 places them under a line naming each module, and config.d is the operator's alone. The catalogue-wide test composes the contributions as the controller does and checks the whole with i3 -C.
114 lines
3.8 KiB
Go
114 lines
3.8 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)
|
|
// 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)
|
|
}
|
|
}
|