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:
6
go.mod
6
go.mod
@@ -1,3 +1,9 @@
|
|||||||
module github.com/pieMusic/vmux
|
module github.com/pieMusic/vmux
|
||||||
|
|
||||||
go 1.25
|
go 1.25
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc // indirect
|
||||||
|
github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046 // indirect
|
||||||
|
go.i3wm.org/i3/v4 v4.24.0 // indirect
|
||||||
|
)
|
||||||
|
|||||||
6
go.sum
Normal file
6
go.sum
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc h1:7D+Bh06CRPCJO3gr2F7h1sriovOZ8BMhca2Rg85c2nk=
|
||||||
|
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046 h1:O/r2Sj+8QcMF7V5IcmiE2sMFV2q3J47BEirxbXJAdzA=
|
||||||
|
github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k=
|
||||||
|
go.i3wm.org/i3/v4 v4.24.0 h1:sBVc+EwxO1UMG7SqYGdGmS4XkMagyHA2y2tcs548fTw=
|
||||||
|
go.i3wm.org/i3/v4 v4.24.0/go.mod h1:Sdg8TVasZI6E7pc6aV7jxwzN4sl/8MUUs5W2+iyvXyo=
|
||||||
32
workspace.go
Normal file
32
workspace.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
i3 "go.i3wm.org/i3/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
// X11PIDResolver abstrait la lecture de _NET_WM_PID pour testabilite.
|
||||||
|
type X11PIDResolver interface {
|
||||||
|
GetPID(windowID int64) (int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// I3TreeProvider abstrait i3.GetTree() pour testabilite.
|
||||||
|
type I3TreeProvider interface {
|
||||||
|
GetTree() (i3.Tree, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadPPID lit le PPid depuis /proc/PID/status. procDir injectable pour les tests.
|
||||||
|
func ReadPPID(procDir string, pid int) (int, error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolveWorkspace remonte la chaine PPID depuis claudePID jusqu'a trouver
|
||||||
|
// un PID connu dans terminalWorkspaces. Max 20 niveaux.
|
||||||
|
func ResolveWorkspace(procDir string, claudePID int, terminalWorkspaces map[int]string) string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildTerminalWorkspaceMap construit la map terminalPID -> workspaceName.
|
||||||
|
// Utilise i3 GetTree + X11 _NET_WM_PID.
|
||||||
|
func BuildTerminalWorkspaceMap(tree I3TreeProvider, x11 X11PIDResolver) (map[int]string, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
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