diff --git a/internal/testrun/driver.go b/internal/testrun/driver.go index af4c108..5fa8f46 100644 --- a/internal/testrun/driver.go +++ b/internal/testrun/driver.go @@ -14,14 +14,15 @@ import ( "github.com/priyanshujain/sanderling/internal/android" "github.com/priyanshujain/sanderling/internal/driver" "github.com/priyanshujain/sanderling/internal/driver/chrome" + "github.com/priyanshujain/sanderling/internal/driver/ioscompanion" driverSidecar "github.com/priyanshujain/sanderling/internal/driver/sidecar" - "github.com/priyanshujain/sanderling/internal/ios" "github.com/priyanshujain/sanderling/internal/sidecarassets" ) // buildDriver creates the appropriate DeviceDriver for the platform and returns -// a cleanup function. For web, ChromeDriver is used directly; for android/ios -// the JVM sidecar is extracted, spawned, and dialed. +// a cleanup function. For web, ChromeDriver is used directly. An iOS simulator +// is driven by the native simulator companion (no JVM). Android and physical +// iOS devices use the JVM sidecar, which is extracted, spawned, and dialed. func buildDriver(ctx context.Context, options Options, stdout io.Writer) (driver.DeviceDriver, func(), error) { if err := Preflight(ctx, options.Platform); err != nil { return nil, nil, err @@ -31,6 +32,24 @@ func buildDriver(ctx context.Context, options Options, stdout io.Writer) (driver return d, func() { _ = d.Terminate(context.Background()) }, nil } + if options.Platform == "ios" && options.iosIsSimulator { + d, err := ioscompanion.New(ctx, ioscompanion.Options{ + UniqueDeviceIdentifier: options.iosUDID, + BundleID: options.BundleID, + AppPath: options.IosAppPath, + Output: stdout, + }) + if err != nil { + return nil, nil, fmt.Errorf("ios simulator driver: %w", err) + } + return d, d.Close, nil + } + + // Physical iOS devices and Android use the JVM sidecar, which requires java. + if err := preflightDevice(options.Platform); err != nil { + return nil, nil, err + } + sidecarDirectory := os.TempDir() + "/sanderling-sidecar" jarPath, err := sidecarassets.Extract(sidecarDirectory) if err != nil { @@ -47,8 +66,8 @@ func buildDriver(ctx context.Context, options Options, stdout io.Writer) (driver "--platform", options.Platform, } if options.Platform == "ios" { - if udid := ios.BootedUDID(ctx); udid != "" { - sidecarArgs = append(sidecarArgs, "--udid", udid) + if options.iosUDID != "" { + sidecarArgs = append(sidecarArgs, "--udid", options.iosUDID) } } sidecarCommand := exec.CommandContext(ctx, "java", sidecarArgs...) diff --git a/internal/testrun/testrun.go b/internal/testrun/testrun.go index ed22daf..9d48dce 100644 --- a/internal/testrun/testrun.go +++ b/internal/testrun/testrun.go @@ -23,15 +23,21 @@ const sidecarStartupTimeout = 30 * time.Second // Options are the parameters for a single test pipeline run. type Options struct { - Spec string - BundleID string - Platform string - AVD string - IosDevice string - Duration time.Duration - Seed int64 - Output string - ClearData bool + Spec string + BundleID string + Platform string + AVD string + IosDevice string + IosAppPath string + Duration time.Duration + Seed int64 + Output string + ClearData bool + + // iosUDID and iosIsSimulator are filled by Execute after resolving the iOS + // target, then read by buildDriver to choose the companion or sidecar path. + iosUDID string + iosIsSimulator bool } // Execute runs the full test pipeline: bundle, launch app, verify properties. @@ -42,9 +48,30 @@ func Execute(ctx context.Context, options Options, stdout io.Writer) error { return err } case "ios": - if err := ios.EnsureSimulator(ctx, options.IosDevice, stdout); err != nil { - return err + udid, isSimulator, err := ios.ResolveTarget(ctx, options.IosDevice) + if err != nil { + // No query and nothing booted: keep boot-first behavior, then resolve + // the simulator that EnsureSimulator just brought up. + if options.IosDevice == "" { + if bootErr := ios.EnsureSimulator(ctx, "", stdout); bootErr != nil { + return bootErr + } + udid, isSimulator, err = ios.ResolveTarget(ctx, "") + } + if err != nil { + return err + } + } else if isSimulator { + if err := ios.EnsureSimulator(ctx, options.IosDevice, stdout); err != nil { + return err + } + udid, isSimulator, err = ios.ResolveTarget(ctx, options.IosDevice) + if err != nil { + return err + } } + options.iosUDID = udid + options.iosIsSimulator = isSimulator } prep, err := prepareBundleInputs(options) if err != nil {