package app import ( "context" "syscall" "testing" "github.com/hjbdev/patterm/internal/preset" ) func TestSwitchProjectPreservesProjectProcessTrees(t *testing.T) { t.Setenv("XDG_DATA_HOME", t.TempDir()) t.Setenv("XDG_CONFIG_HOME", t.TempDir()) ctx, cancel := context.WithCancel(context.Background()) defer cancel() reg := newProjectRegistry(preset.Set{}, defaultSettings(), nil, 80, 24) defer reg.Shutdown() projectA, err := reg.Open(ctx, t.TempDir()) if err != nil { t.Fatalf("open project A: %v", err) } projectB, err := reg.Open(ctx, t.TempDir()) if err != nil { t.Fatalf("open project B: %v", err) } a, err := projectA.Session.Spawn(SpawnSpec{ Kind: KindCommand, Argv: []string{"sh", "-c", "trap 'exit 0' TERM; while :; do sleep 1; done"}, Name: "a-loop", }, 80, 24) if err != nil { t.Fatalf("spawn project A command: %v", err) } b, err := projectB.Session.Spawn(SpawnSpec{ Kind: KindCommand, Argv: []string{"sh", "-c", "trap 'exit 0' TERM; while :; do sleep 1; done"}, Name: "b-loop", }, 80, 24) if err != nil { t.Fatalf("spawn project B command: %v", err) } t.Cleanup(func() { _ = projectA.Session.Kill(a.ID, syscall.SIGTERM) _ = projectB.Session.Kill(b.ID, syscall.SIGTERM) }) waitUntilLive(t, a) waitUntilLive(t, b) st := &uiState{ registry: reg, project: projectA, sess: projectA.Session, launcher: projectA.Launcher, pads: projectA.Pads, trust: projectA.Trust, timers: projectA.Host.timers, chromeWake: make(chan struct{}, 1), view: ClientView{ ID: "test", ProjectKey: projectA.Key, ProjectName: projectA.Name, Cols: 80, Rows: 24, }, } st.focusChildLocked(a) projectA.Session.Subscribe(st) st.switchProject(projectB) if st.view.ProjectKey != projectB.Key { t.Fatalf("view project key = %q, want %q", st.view.ProjectKey, projectB.Key) } if st.sess != projectB.Session { t.Fatalf("ui session did not move to project B") } if projectA.Session.FindChild(a.ID) == nil { t.Fatalf("project A child disappeared after switch") } if projectB.Session.FindChild(b.ID) == nil { t.Fatalf("project B child disappeared after switch") } if !a.IsLive() { t.Fatalf("project A child stopped after switch") } if !b.IsLive() { t.Fatalf("project B child stopped after switch") } st.switchProject(projectA) if st.view.ProjectKey != projectA.Key { t.Fatalf("view project key after switching back = %q, want %q", st.view.ProjectKey, projectA.Key) } if projectA.Session.FindChild(a.ID) == nil || projectB.Session.FindChild(b.ID) == nil { t.Fatalf("switching back should preserve both project process trees") } }