feat(testrun): launch iOS app via XCTest with env vars instead of simctl

This commit is contained in:
pj committed 2026-04-23 15:49:51 +07:00
1 parent 003aba9ea2
commit bff3a49324
3 files changed
+40 -19

No files matched your search

+9
View File
@@ -69,6 +69,15 @@ func LaunchApp(ctx context.Context, bundleID string, env map[string]string) erro
return nil
}
// BootedUDID returns the UDID of the currently booted iOS simulator, or "" if none is booted.
func BootedUDID(ctx context.Context) string {
d, _ := bootedSimulator(ctx)
if d == nil {
return ""
}
return d.UDID
}
func bootedSimulator(ctx context.Context) (*simDevice, error) {
out, err := exec.CommandContext(ctx, "xcrun", "simctl", "list", "devices", "booted", "--json").Output()
if err != nil {
+9 -2
View File
@@ -12,6 +12,7 @@ import (
"github.com/priyanshujain/sanderling/internal/driver"
"github.com/priyanshujain/sanderling/internal/driver/chrome"
driverSidecar "github.com/priyanshujain/sanderling/internal/driver/sidecar"
"github.com/priyanshujain/sanderling/internal/ios"
"github.com/priyanshujain/sanderling/internal/sidecar"
)
@@ -35,10 +36,16 @@ func buildDriver(ctx context.Context, options Options, stdout io.Writer) (driver
if err != nil {
return nil, nil, err
}
sidecarCommand := exec.CommandContext(ctx, "java", "-jar", jarPath,
sidecarArgs := []string{"-jar", jarPath,
"--port", strconv.Itoa(sidecarPort),
"--platform", options.Platform,
)
}
if options.Platform == "ios" {
if udid := ios.BootedUDID(ctx); udid != "" {
sidecarArgs = append(sidecarArgs, "--udid", udid)
}
}
sidecarCommand := exec.CommandContext(ctx, "java", sidecarArgs...)
sidecarCommand.Stdout = stdout
sidecarCommand.Stderr = stdout
sidecarCommand.Env = android.EnvWithAndroidPlatformTools(os.Environ())
+22 -17
View File
@@ -110,7 +110,7 @@ func Execute(ctx context.Context, options Options, stdout io.Writer) error {
acceptChannel <- acceptResult{conn: conn, err: acceptErr}
}()
if err := activeDriver.Launch(ctx, options.BundleID, false); err != nil {
if err := activeDriver.Launch(ctx, options.BundleID, false, nil); err != nil {
return fmt.Errorf("launch app: %w", err)
}
fmt.Fprintf(stdout, "launched %s; waiting for SDK to connect (%.0fs timeout)\n", options.BundleID, sdkAcceptTimeout.Seconds())
@@ -133,35 +133,40 @@ func Execute(ctx context.Context, options Options, stdout io.Writer) error {
defer listener.Close()
agentPort := listener.Addr().(*net.TCPAddr).Port
// Launch app via simctl with SANDERLING_PORT so the SDK can connect.
if err := ios.LaunchApp(ctx, options.BundleID, map[string]string{
agentServer := agent.NewServer(listener)
type acceptResult struct {
conn *agent.Conn
err error
}
acceptChannel := make(chan acceptResult, 1)
go func() {
acceptCtx, cancel := context.WithTimeout(ctx, sdkAcceptTimeout)
defer cancel()
conn, acceptErr := agentServer.Accept(acceptCtx)
acceptChannel <- acceptResult{conn: conn, err: acceptErr}
}()
// Launch app via XCTest with SANDERLING_PORT so the SDK can connect.
if err := activeDriver.Launch(ctx, options.BundleID, false, map[string]string{
"SANDERLING_PORT": strconv.Itoa(agentPort),
}); err != nil {
return fmt.Errorf("launch app: %w", err)
}
fmt.Fprintf(stdout, "iOS app launched with SANDERLING_PORT=%d; waiting for SDK (%.0fs timeout)\n", agentPort, sdkAcceptTimeout.Seconds())
agentServer := agent.NewServer(listener)
acceptCtx, acceptCancel := context.WithTimeout(ctx, sdkAcceptTimeout)
conn, acceptErr := agentServer.Accept(acceptCtx)
acceptCancel()
if acceptErr != nil {
return fmt.Errorf("accept SDK: %w", acceptErr)
result := <-acceptChannel
if result.err != nil {
return fmt.Errorf("accept SDK: %w", result.err)
}
connection = conn
connection = result.conn
defer connection.Close()
hello := connection.Hello()
fmt.Fprintf(stdout, "SDK connected: platform=%s app=%s sdk=%s\n", hello.Platform, hello.AppPackage, hello.Version)
// Initialize Maestro XCTest session after the app is running.
// clearState=false avoids relaunching the already-connected app.
if err := activeDriver.Launch(ctx, options.BundleID, false); err != nil {
fmt.Fprintf(stdout, "warn: maestro launch: %v\n", err)
}
case "web":
fmt.Fprintln(stdout, "web mode: skipping SDK")
if err := activeDriver.Launch(ctx, options.BundleID, false); err != nil {
if err := activeDriver.Launch(ctx, options.BundleID, false, nil); err != nil {
return fmt.Errorf("launch app: %w", err)
}
}