package main import ( "os" "path/filepath" "strconv" "strings" "testing" ) // aMachine gives findSession a /proc and an X socket directory of the test's own. func aMachine(t *testing.T, env map[string]string) (proc, sockets string) { t.Helper() root := t.TempDir() proc, sockets = filepath.Join(root, "proc"), filepath.Join(root, "x11") for _, d := range []string{proc, sockets} { if err := os.MkdirAll(d, 0o755); err != nil { t.Fatal(err) } } wasProc, wasX, wasEnv := procRoot, x11Sockets, getenv procRoot, x11Sockets = proc, sockets getenv = func(k string) string { return env[k] } t.Cleanup(func() { procRoot, x11Sockets, getenv = wasProc, wasX, wasEnv }) return proc, sockets } func aProcess(t *testing.T, proc string, pid int, comm string, env ...string) { t.Helper() dir := filepath.Join(proc, strconv.Itoa(pid)) if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatal(err) } _ = os.WriteFile(filepath.Join(dir, "comm"), []byte(comm+"\n"), 0o644) _ = os.WriteFile(filepath.Join(dir, "environ"), []byte(strings.Join(env, "\x00")+"\x00"), 0o644) } func aSocket(t *testing.T, dir, name string) { t.Helper() if err := os.WriteFile(filepath.Join(dir, name), nil, 0o644); err != nil { t.Fatal(err) } } func TestSessionTheWindowManagersDisplayAndCookieAreTheSessions(t *testing.T) { proc, sockets := aMachine(t, map[string]string{"MESH_OPERATOR_HOME": "/home/op"}) aSocket(t, sockets, "X1") aProcess(t, proc, 3, "bash", "DISPLAY=:9", "XAUTHORITY=/stale") aProcess(t, proc, 4, "kitty", "DISPLAY=:1", "XAUTHORITY=/home/op/.Xauthority") aProcess(t, proc, 5, "i3", "DISPLAY=:1.0", "XAUTHORITY=/home/op/.Xauthority") aProcess(t, proc, 6, "sshd", "PATH=/bin") s, err := findSession() if err != nil { t.Fatal(err) } if s.Display != ":1.0" || s.XAuthority != "/home/op/.Xauthority" || !strings.Contains(s.FoundBy, "i3") { t.Fatalf("%+v: a display without a socket (:9) is skipped, and the window manager's is preferred", s) } if got := strings.Join(s.Env(), " "); got != "DISPLAY=:1.0 XAUTHORITY=/home/op/.Xauthority" { t.Fatalf("env %s", got) } } func TestSessionTheOnlySocketWithTheHomesCookieIsTheFallback(t *testing.T) { home := t.TempDir() _ = os.WriteFile(filepath.Join(home, ".Xauthority"), []byte("c"), 0o600) _, sockets := aMachine(t, map[string]string{"MESH_OPERATOR_HOME": home}) aSocket(t, sockets, "X0") s, err := findSession() if err != nil || s.Display != ":0" || s.XAuthority != filepath.Join(home, ".Xauthority") || s.FoundBy != "socket" { t.Fatalf("%+v %v", s, err) } } func TestSessionNoSessionIsSaidNotGuessed(t *testing.T) { _, sockets := aMachine(t, map[string]string{}) if _, err := findSession(); err == nil || !strings.Contains(err.Error(), "no graphical session") { t.Fatalf("none: %v", err) } aSocket(t, sockets, "X0") aSocket(t, sockets, "X1") if _, err := findSession(); err == nil || !strings.Contains(err.Error(), "2 X sockets") { t.Fatalf("two: %v", err) } } func TestSessionTheProcessesOwnDisplayComesFirst(t *testing.T) { _, sockets := aMachine(t, map[string]string{"DISPLAY": ":2", "XAUTHORITY": "/a"}) aSocket(t, sockets, "X2") s, err := findSession() if err != nil || s.Display != ":2" || s.FoundBy != "environment" { t.Fatalf("%+v %v", s, err) } }