test(02-02): add failing tests for PPID chain walk + workspace resolution
- TestReadPPID, TestReadPPIDMissing - TestResolveWorkspace, TestResolveWorkspaceNotFound, TestResolveWorkspaceMaxDepth - TestBuildTerminalWorkspaceMapUnit - Add go.i3wm.org/i3/v4 dependency
This commit is contained in:
169
workspace_test.go
Normal file
169
workspace_test.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
i3 "go.i3wm.org/i3/v4"
|
||||
)
|
||||
|
||||
// --- Helpers ---
|
||||
|
||||
func writeFakeStatus(t *testing.T, procDir string, pid, ppid int) {
|
||||
t.Helper()
|
||||
pidDir := filepath.Join(procDir, fmt.Sprintf("%d", pid))
|
||||
if err := os.MkdirAll(pidDir, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
content := fmt.Sprintf("Name:\tprocess\nPPid:\t%d\nUid:\t1000\n", ppid)
|
||||
if err := os.WriteFile(filepath.Join(pidDir, "status"), []byte(content), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Mock types ---
|
||||
|
||||
type mockI3TreeProvider struct {
|
||||
tree i3.Tree
|
||||
err error
|
||||
}
|
||||
|
||||
func (m *mockI3TreeProvider) GetTree() (i3.Tree, error) {
|
||||
return m.tree, m.err
|
||||
}
|
||||
|
||||
type mockX11PIDResolver struct {
|
||||
pids map[int64]int // windowID -> PID
|
||||
}
|
||||
|
||||
func (m *mockX11PIDResolver) GetPID(windowID int64) (int, error) {
|
||||
pid, ok := m.pids[windowID]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("no PID for window %d", windowID)
|
||||
}
|
||||
return pid, nil
|
||||
}
|
||||
|
||||
// --- ReadPPID tests ---
|
||||
|
||||
func TestReadPPID(t *testing.T) {
|
||||
procDir := t.TempDir()
|
||||
writeFakeStatus(t, procDir, 100, 50)
|
||||
|
||||
ppid, err := ReadPPID(procDir, 100)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if ppid != 50 {
|
||||
t.Errorf("ReadPPID = %d, want 50", ppid)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadPPIDMissing(t *testing.T) {
|
||||
procDir := t.TempDir()
|
||||
|
||||
_, err := ReadPPID(procDir, 999)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for missing PID, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
// --- ResolveWorkspace tests ---
|
||||
|
||||
func TestResolveWorkspace(t *testing.T) {
|
||||
procDir := t.TempDir()
|
||||
// Chain: 100 -> 50 -> 10
|
||||
writeFakeStatus(t, procDir, 100, 50)
|
||||
writeFakeStatus(t, procDir, 50, 10)
|
||||
writeFakeStatus(t, procDir, 10, 1)
|
||||
|
||||
terminalMap := map[int]string{10: "workspace 3"}
|
||||
|
||||
ws := ResolveWorkspace(procDir, 100, terminalMap)
|
||||
if ws != "workspace 3" {
|
||||
t.Errorf("ResolveWorkspace = %q, want %q", ws, "workspace 3")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveWorkspaceNotFound(t *testing.T) {
|
||||
procDir := t.TempDir()
|
||||
writeFakeStatus(t, procDir, 100, 50)
|
||||
writeFakeStatus(t, procDir, 50, 1)
|
||||
writeFakeStatus(t, procDir, 1, 0)
|
||||
|
||||
terminalMap := map[int]string{999: "workspace 5"}
|
||||
|
||||
ws := ResolveWorkspace(procDir, 100, terminalMap)
|
||||
if ws != "" {
|
||||
t.Errorf("ResolveWorkspace = %q, want empty", ws)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveWorkspaceMaxDepth(t *testing.T) {
|
||||
procDir := t.TempDir()
|
||||
// Chain of 25 PIDs: 100 -> 99 -> 98 -> ... -> 76
|
||||
for pid := 100; pid > 75; pid-- {
|
||||
writeFakeStatus(t, procDir, pid, pid-1)
|
||||
}
|
||||
// PID 75 is in the terminal map but depth > 20
|
||||
terminalMap := map[int]string{75: "workspace 1"}
|
||||
|
||||
ws := ResolveWorkspace(procDir, 100, terminalMap)
|
||||
if ws != "" {
|
||||
t.Errorf("ResolveWorkspace should return empty after max depth, got %q", ws)
|
||||
}
|
||||
}
|
||||
|
||||
// --- BuildTerminalWorkspaceMap tests ---
|
||||
|
||||
func TestBuildTerminalWorkspaceMapUnit(t *testing.T) {
|
||||
// Mock i3 tree with 2 workspaces, each containing a window
|
||||
tree := i3.Tree{
|
||||
Root: &i3.Node{
|
||||
Type: i3.Root,
|
||||
Nodes: []*i3.Node{
|
||||
{
|
||||
Type: i3.OutputNode,
|
||||
Name: "eDP-1",
|
||||
Nodes: []*i3.Node{
|
||||
{
|
||||
Type: i3.WorkspaceNode,
|
||||
Name: "1",
|
||||
Nodes: []*i3.Node{
|
||||
{Type: i3.Con, Window: 12345},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: i3.WorkspaceNode,
|
||||
Name: "3",
|
||||
Nodes: []*i3.Node{
|
||||
{Type: i3.Con, Window: 67890},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
x11 := &mockX11PIDResolver{
|
||||
pids: map[int64]int{
|
||||
12345: 1000,
|
||||
67890: 2000,
|
||||
},
|
||||
}
|
||||
|
||||
result, err := BuildTerminalWorkspaceMap(&mockI3TreeProvider{tree: tree}, x11)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if result[1000] != "1" {
|
||||
t.Errorf("PID 1000 workspace = %q, want %q", result[1000], "1")
|
||||
}
|
||||
if result[2000] != "3" {
|
||||
t.Errorf("PID 2000 workspace = %q, want %q", result[2000], "3")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user