83 lines
2.0 KiB
Go
83 lines
2.0 KiB
Go
package app
|
|
|
|
import (
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestParentExitKillsDescendants verifies that when a parent process
|
|
// exits, every still-live process that was spawned under it is signaled
|
|
// and dies, recursively through the tree.
|
|
func TestParentExitKillsDescendants(t *testing.T) {
|
|
sess := NewSession(t.TempDir(), "test")
|
|
|
|
// Use a tiny pause-then-trap shell so the children survive long
|
|
// enough for the parent to die first; SIGTERM should terminate them.
|
|
sleepArgv := []string{"sh", "-c", "trap 'exit 0' TERM; sleep 30"}
|
|
|
|
parent, err := sess.Spawn(SpawnSpec{
|
|
Kind: KindTerminal,
|
|
Argv: []string{"sh", "-c", "sleep 30"},
|
|
}, 80, 24)
|
|
if err != nil {
|
|
t.Fatalf("spawn parent: %v", err)
|
|
}
|
|
|
|
childA, err := sess.Spawn(SpawnSpec{
|
|
Kind: KindCommand,
|
|
Argv: sleepArgv,
|
|
ParentID: parent.ID,
|
|
}, 80, 24)
|
|
if err != nil {
|
|
t.Fatalf("spawn childA: %v", err)
|
|
}
|
|
grandchild, err := sess.Spawn(SpawnSpec{
|
|
Kind: KindCommand,
|
|
Argv: sleepArgv,
|
|
ParentID: childA.ID,
|
|
}, 80, 24)
|
|
if err != nil {
|
|
t.Fatalf("spawn grandchild: %v", err)
|
|
}
|
|
|
|
// Wait for everyone to be running.
|
|
waitUntilLive(t, parent)
|
|
waitUntilLive(t, childA)
|
|
waitUntilLive(t, grandchild)
|
|
|
|
// Kill the parent. Its reapChild should cascade to childA, whose
|
|
// reapChild should in turn cascade to grandchild.
|
|
if err := parent.signal(syscall.SIGTERM); err != nil {
|
|
t.Fatalf("signal parent: %v", err)
|
|
}
|
|
|
|
waitUntilNotLive(t, parent)
|
|
waitUntilNotLive(t, childA)
|
|
waitUntilNotLive(t, grandchild)
|
|
}
|
|
|
|
func waitUntilLive(t *testing.T, c *Child) {
|
|
t.Helper()
|
|
deadline := time.Now().Add(5 * time.Second)
|
|
for time.Now().Before(deadline) {
|
|
if c.IsLive() {
|
|
return
|
|
}
|
|
time.Sleep(20 * time.Millisecond)
|
|
}
|
|
t.Fatalf("child %s never went live", c.ID)
|
|
}
|
|
|
|
func waitUntilNotLive(t *testing.T, c *Child) {
|
|
t.Helper()
|
|
deadline := time.Now().Add(5 * time.Second)
|
|
for time.Now().Before(deadline) {
|
|
if !c.IsLive() {
|
|
return
|
|
}
|
|
time.Sleep(20 * time.Millisecond)
|
|
}
|
|
t.Fatalf("child %s still live after parent died (status=%s)", c.ID, c.Status())
|
|
}
|