163 lines
4.4 KiB
Go
163 lines
4.4 KiB
Go
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")
|
|
}
|
|
}
|
|
|
|
func TestProjectRegistryScratchpadsRouteByCallerProject(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-caller",
|
|
}, 80, 24)
|
|
if err != nil {
|
|
t.Fatalf("spawn project A caller: %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-caller",
|
|
}, 80, 24)
|
|
if err != nil {
|
|
t.Fatalf("spawn project B caller: %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)
|
|
|
|
if _, err := reg.ScratchpadWrite(a.ID, "note.md", "project A", ""); err != nil {
|
|
t.Fatalf("write project A scratchpad: %v", err)
|
|
}
|
|
if _, err := reg.ScratchpadWrite(b.ID, "note.md", "project B", ""); err != nil {
|
|
t.Fatalf("write project B scratchpad: %v", err)
|
|
}
|
|
|
|
gotA, _, err := reg.ScratchpadRead(a.ID, "note.md")
|
|
if err != nil {
|
|
t.Fatalf("read project A scratchpad: %v", err)
|
|
}
|
|
gotB, _, err := reg.ScratchpadRead(b.ID, "note.md")
|
|
if err != nil {
|
|
t.Fatalf("read project B scratchpad: %v", err)
|
|
}
|
|
if gotA != "project A" || gotB != "project B" {
|
|
t.Fatalf("scratchpad routing leaked between projects: A=%q B=%q", gotA, gotB)
|
|
}
|
|
}
|