mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
fix(ioscompanion): reinstall via simctl and sanitize companion env
This commit is contained in:
1 parent
5ab2991dd0
commit
93b62d4aa5
2 files changed
+34
-6
No files matched your search
@@ -16,6 +16,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
@@ -95,6 +96,10 @@ type Driver struct {
|
|||||||
// A seam so tests skip the xcrun shell-out.
|
// A seam so tests skip the xcrun shell-out.
|
||||||
resetContainer func(ctx context.Context) error
|
resetContainer func(ctx context.Context) error
|
||||||
|
|
||||||
|
// reinstallApp uninstalls and reinstalls the app bundle for clear-state.
|
||||||
|
// A seam so tests skip the simctl shell-outs.
|
||||||
|
reinstallApp func(ctx context.Context) error
|
||||||
|
|
||||||
// idleClock drives WaitForIdle's settle poll. A seam so tests substitute a
|
// idleClock drives WaitForIdle's settle poll. A seam so tests substitute a
|
||||||
// fake clock and avoid the real settle cap.
|
// fake clock and avoid the real settle cap.
|
||||||
idleClock Clock
|
idleClock Clock
|
||||||
@@ -153,6 +158,7 @@ func New(ctx context.Context, options Options) (*Driver, error) {
|
|||||||
driverInstance.address = address
|
driverInstance.address = address
|
||||||
driverInstance.restart = driverInstance.respawnAndRedial
|
driverInstance.restart = driverInstance.respawnAndRedial
|
||||||
driverInstance.resetContainer = driverInstance.resetDataContainer
|
driverInstance.resetContainer = driverInstance.resetDataContainer
|
||||||
|
driverInstance.reinstallApp = driverInstance.simctlReinstall
|
||||||
driverInstance.processContext, driverInstance.processCancel = context.WithCancel(ctx)
|
driverInstance.processContext, driverInstance.processCancel = context.WithCancel(ctx)
|
||||||
|
|
||||||
if err := driverInstance.bringUp(ctx); err != nil {
|
if err := driverInstance.bringUp(ctx); err != nil {
|
||||||
@@ -306,8 +312,7 @@ func (d *Driver) Launch(ctx context.Context, bundleID string, clearState bool, e
|
|||||||
// container and warns once that a full reinstall needs the app path.
|
// container and warns once that a full reinstall needs the app path.
|
||||||
func (d *Driver) clearAppState(ctx context.Context) error {
|
func (d *Driver) clearAppState(ctx context.Context) error {
|
||||||
if d.appPath != "" {
|
if d.appPath != "" {
|
||||||
_ = d.withRecovery(ctx, func() error { return d.companion.Uninstall(ctx, d.bundleID) })
|
if err := d.reinstallApp(ctx); err != nil {
|
||||||
if err := d.withRecovery(ctx, func() error { return d.companion.Install(ctx, d.appPath) }); err != nil {
|
|
||||||
return fmt.Errorf("reinstall %s: %w", d.appPath, err)
|
return fmt.Errorf("reinstall %s: %w", d.appPath, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -319,6 +324,18 @@ func (d *Driver) clearAppState(ctx context.Context) error {
|
|||||||
return d.resetContainer(ctx)
|
return d.resetContainer(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// simctlReinstall uninstalls and reinstalls the app bundle via simctl. App
|
||||||
|
// lifecycle stays with simctl: the companion's install RPC misreads current
|
||||||
|
// simulator targets' architectures and rejects valid bundles.
|
||||||
|
func (d *Driver) simctlReinstall(ctx context.Context) error {
|
||||||
|
_ = exec.CommandContext(ctx, "xcrun", "simctl", "uninstall", d.udid, d.bundleID).Run()
|
||||||
|
output, err := exec.CommandContext(ctx, "xcrun", "simctl", "install", d.udid, d.appPath).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("simctl install: %w: %s", err, strings.TrimSpace(string(output)))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// resetDataContainer deletes the contents of the app's data container so the
|
// resetDataContainer deletes the contents of the app's data container so the
|
||||||
// next launch starts with empty storage.
|
// next launch starts with empty storage.
|
||||||
func (d *Driver) resetDataContainer(ctx context.Context) error {
|
func (d *Driver) resetDataContainer(ctx context.Context) error {
|
||||||
@@ -698,6 +715,14 @@ func (d *Driver) realSpawnChild(ctx context.Context, address string) (*exec.Cmd,
|
|||||||
command := exec.CommandContext(ctx, binaryPath, "--udid", d.udid, "--grpc-port", port)
|
command := exec.CommandContext(ctx, binaryPath, "--udid", d.udid, "--grpc-port", port)
|
||||||
command.Stdout = d.output
|
command.Stdout = d.output
|
||||||
command.Stderr = d.output
|
command.Stderr = d.output
|
||||||
|
// The companion echoes its whole environment into the run log at startup,
|
||||||
|
// so it gets a minimal one: secrets in the parent environment must never
|
||||||
|
// reach run artifacts.
|
||||||
|
command.Env = []string{
|
||||||
|
"HOME=" + os.Getenv("HOME"),
|
||||||
|
"PATH=/usr/bin:/bin",
|
||||||
|
"TMPDIR=" + os.TempDir(),
|
||||||
|
}
|
||||||
command.Cancel = func() error { return command.Process.Signal(syscall.SIGTERM) }
|
command.Cancel = func() error { return command.Process.Signal(syscall.SIGTERM) }
|
||||||
command.WaitDelay = shutdownGrace
|
command.WaitDelay = shutdownGrace
|
||||||
if err := command.Start(); err != nil {
|
if err := command.Start(); err != nil {
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ func newTestDriver(companion transport.Companion) *Driver {
|
|||||||
screenHeight: 844,
|
screenHeight: 844,
|
||||||
}
|
}
|
||||||
d.resetContainer = func(context.Context) error { return nil }
|
d.resetContainer = func(context.Context) error { return nil }
|
||||||
|
d.reinstallApp = func(context.Context) error { return nil }
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,14 +141,16 @@ func TestLaunchClearStateReinstallsWithAppPath(t *testing.T) {
|
|||||||
companion := &fakeCompanion{accessibilityJSON: "[]"}
|
companion := &fakeCompanion{accessibilityJSON: "[]"}
|
||||||
d := newTestDriver(companion)
|
d := newTestDriver(companion)
|
||||||
d.appPath = "/tmp/Sample.app"
|
d.appPath = "/tmp/Sample.app"
|
||||||
|
reinstalls := 0
|
||||||
|
d.reinstallApp = func(context.Context) error { reinstalls++; return nil }
|
||||||
if err := d.Launch(context.Background(), "", true, nil); err != nil {
|
if err := d.Launch(context.Background(), "", true, nil); err != nil {
|
||||||
t.Fatalf("Launch: %v", err)
|
t.Fatalf("Launch: %v", err)
|
||||||
}
|
}
|
||||||
if indexOf(companion.calls, "uninstall") < 0 || indexOf(companion.calls, "install") < 0 {
|
if reinstalls != 1 {
|
||||||
t.Fatalf("clear-state with app path must uninstall then install; got %v", companion.calls)
|
t.Fatalf("clear-state with app path must reinstall exactly once; got %d", reinstalls)
|
||||||
}
|
}
|
||||||
if indexOf(companion.calls, "uninstall") > indexOf(companion.calls, "install") {
|
if indexOf(companion.calls, "launch") < indexOf(companion.calls, "terminate") {
|
||||||
t.Fatalf("uninstall must precede install; got %v", companion.calls)
|
t.Fatalf("launch must still follow terminate; got %v", companion.calls)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user