mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
test(ioscompanion): cover stopProcess, restart, and failed bring-up supervision
This commit is contained in:
1 parent
338a232ba2
commit
8c38a727e0
2 files changed
+159
-2
No files matched your search
@@ -39,8 +39,8 @@ const startupTimeout = 30 * time.Second
|
||||
const runnerStartupTimeout = 120 * time.Second
|
||||
|
||||
// shutdownGrace bounds how long the companion child gets to exit after SIGTERM
|
||||
// before it is killed.
|
||||
const shutdownGrace = 15 * time.Second
|
||||
// before it is killed. A variable so the kill-escalation test can shrink it.
|
||||
var shutdownGrace = 15 * time.Second
|
||||
|
||||
// longPressHoldMilliseconds is how long LongPress holds the finger down.
|
||||
const longPressHoldMilliseconds = 600
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
package ioscompanion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/priyanshujain/sanderling/internal/driver/ioscompanion/transport"
|
||||
)
|
||||
|
||||
// These tests cover the process-supervision logic (stopProcess, bringUp,
|
||||
// respawnAndRedial) with real child processes and seamed transports, so the
|
||||
// default suite exercises it without a simulator.
|
||||
|
||||
func TestStopProcessReapsChildOnSigterm(t *testing.T) {
|
||||
child := exec.Command("sleep", "30")
|
||||
if err := child.Start(); err != nil {
|
||||
t.Fatalf("start child: %v", err)
|
||||
}
|
||||
start := time.Now()
|
||||
stopProcess(child)
|
||||
if elapsed := time.Since(start); elapsed > 5*time.Second {
|
||||
t.Fatalf("stopProcess took %v; SIGTERM on sleep should reap promptly", elapsed)
|
||||
}
|
||||
if child.ProcessState == nil {
|
||||
t.Fatal("child not reaped: ProcessState is nil")
|
||||
}
|
||||
if err := child.Process.Signal(syscall.Signal(0)); err == nil {
|
||||
t.Fatal("child still signalable after stopProcess")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStopProcessEscalatesToKillWhenSigtermIgnored(t *testing.T) {
|
||||
previousGrace := shutdownGrace
|
||||
shutdownGrace = 200 * time.Millisecond
|
||||
defer func() { shutdownGrace = previousGrace }()
|
||||
|
||||
child := exec.Command("sh", "-c", `trap "" TERM; sleep 30`)
|
||||
if err := child.Start(); err != nil {
|
||||
t.Fatalf("start child: %v", err)
|
||||
}
|
||||
// Give the shell a beat to install its TERM trap.
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
stopProcess(child)
|
||||
if child.ProcessState == nil {
|
||||
t.Fatal("child not reaped: ProcessState is nil")
|
||||
}
|
||||
if child.ProcessState.Success() {
|
||||
t.Fatal("child should have died by signal, not exited cleanly")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStopProcessHandlesNilChild(t *testing.T) {
|
||||
stopProcess(nil)
|
||||
stopProcess(&exec.Cmd{})
|
||||
}
|
||||
|
||||
// TestRespawnAndRedialReplacesTransportAndChild drives the real restart path
|
||||
// through the seams: the old transport must be closed, a fresh child spawned,
|
||||
// and the fresh transport installed.
|
||||
func TestRespawnAndRedialReplacesTransportAndChild(t *testing.T) {
|
||||
t.Setenv("SANDERLING_SIMULATOR_COMPANION", "legacy")
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer listener.Close()
|
||||
go func() {
|
||||
for {
|
||||
connection, acceptErr := listener.Accept()
|
||||
if acceptErr != nil {
|
||||
return
|
||||
}
|
||||
_ = connection.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
spawns := 0
|
||||
dials := []*fakeCompanion{}
|
||||
options := Options{
|
||||
UniqueDeviceIdentifier: "FAKE-UDID",
|
||||
pickAddress: func() (string, error) { return listener.Addr().String(), nil },
|
||||
spawnChild: func(context.Context, string) (*exec.Cmd, error) {
|
||||
spawns++
|
||||
return &exec.Cmd{}, nil
|
||||
},
|
||||
dialCompanion: func(string) (transport.Companion, error) {
|
||||
companion := &fakeCompanion{accessibilityJSON: "[]"}
|
||||
dials = append(dials, companion)
|
||||
return companion, nil
|
||||
},
|
||||
}
|
||||
d, err := New(context.Background(), options)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer d.Close()
|
||||
|
||||
first := dials[0]
|
||||
if err := d.respawnAndRedial(context.Background()); err != nil {
|
||||
t.Fatalf("respawnAndRedial: %v", err)
|
||||
}
|
||||
if spawns != 2 {
|
||||
t.Fatalf("spawns = %d, want 2 (initial bring-up plus restart)", spawns)
|
||||
}
|
||||
if len(dials) != 2 || d.companion != transport.Companion(dials[1]) {
|
||||
t.Fatalf("restart must install the freshly dialed transport")
|
||||
}
|
||||
if indexOf(first.recorded(), "close") < 0 {
|
||||
t.Fatal("restart must close the dead transport")
|
||||
}
|
||||
}
|
||||
|
||||
// TestBringUpStopsChildWhenDialFails proves a failed bring-up does not leak
|
||||
// the child it spawned.
|
||||
func TestBringUpStopsChildWhenDialFails(t *testing.T) {
|
||||
t.Setenv("SANDERLING_SIMULATOR_COMPANION", "legacy")
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer listener.Close()
|
||||
go func() {
|
||||
for {
|
||||
connection, acceptErr := listener.Accept()
|
||||
if acceptErr != nil {
|
||||
return
|
||||
}
|
||||
_ = connection.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
child := exec.Command("sleep", "30")
|
||||
if err := child.Start(); err != nil {
|
||||
t.Fatalf("start child: %v", err)
|
||||
}
|
||||
options := Options{
|
||||
UniqueDeviceIdentifier: "FAKE-UDID",
|
||||
pickAddress: func() (string, error) { return listener.Addr().String(), nil },
|
||||
spawnChild: func(context.Context, string) (*exec.Cmd, error) {
|
||||
return child, nil
|
||||
},
|
||||
dialCompanion: func(string) (transport.Companion, error) {
|
||||
return nil, errors.New("dial refused")
|
||||
},
|
||||
}
|
||||
if _, err := New(context.Background(), options); err == nil {
|
||||
t.Fatal("New should fail when dial fails")
|
||||
}
|
||||
if child.ProcessState == nil {
|
||||
t.Fatal("failed bring-up must reap the spawned child")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user