mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
feat(testrun): route physical-device iOS runs to the device driver
Execute resolves a non-simulator iOS target through ios.ResolveDevice into its hardware UDID and CoreDevice id; buildDriver constructs NewDevice via a seam instead of rejecting the device. Generalizes the --ios-device and --ios-app-path help to cover the device path; signing stays env-read, never a flag.
This commit is contained in:
1 parent
06ea651c71
commit
f5cfc4407e
4 files changed
+211
-45
No files matched your search
+124
-12
@@ -2,27 +2,139 @@ package testrun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/priyanshujain/sanderling/internal/driver"
|
||||
"github.com/priyanshujain/sanderling/internal/driver/ioscompanion"
|
||||
"github.com/priyanshujain/sanderling/internal/ios"
|
||||
)
|
||||
|
||||
func TestBuildDriverRejectsPhysicalIOS(t *testing.T) {
|
||||
if _, err := exec.LookPath("xcrun"); err != nil {
|
||||
t.Skip("xcrun not on PATH; physical-iOS rejection is reached only after preflight")
|
||||
// stubDeviceDriver is a no-op DeviceDriver so routing tests never build or spawn
|
||||
// a real runner. Only construction wiring is under test.
|
||||
type stubDeviceDriver struct{ driver.DeviceDriver }
|
||||
|
||||
func TestBuildDriverRoutesPhysicalIOSToDeviceDriver(t *testing.T) {
|
||||
original := newDeviceDriver
|
||||
t.Cleanup(func() { newDeviceDriver = original })
|
||||
|
||||
var got ioscompanion.DeviceOptions
|
||||
closed := false
|
||||
newDeviceDriver = func(_ context.Context, options ioscompanion.DeviceOptions) (driver.DeviceDriver, func(), error) {
|
||||
got = options
|
||||
return stubDeviceDriver{}, func() { closed = true }, nil
|
||||
}
|
||||
|
||||
options := Options{Platform: "ios", BundleID: "app.folio", IosAppPath: "/tmp/iosApp.app"}
|
||||
options.iosIsSimulator = false
|
||||
options.iosUDID = "00008140-HW"
|
||||
options.iosCoreDeviceID = "CORE-1"
|
||||
|
||||
d, cleanup, err := buildDriver(context.Background(), options, io.Discard)
|
||||
if err != nil {
|
||||
t.Fatalf("buildDriver: %v", err)
|
||||
}
|
||||
if d == nil {
|
||||
t.Fatal("expected a device driver")
|
||||
}
|
||||
if got.HardwareUDID != "00008140-HW" || got.CoreDeviceID != "CORE-1" {
|
||||
t.Fatalf("DeviceOptions ids = %+v, want the resolved hardware/core ids", got)
|
||||
}
|
||||
if got.BundleID != "app.folio" || got.AppPath != "/tmp/iosApp.app" {
|
||||
t.Fatalf("DeviceOptions = %+v, want bundle and app path threaded through", got)
|
||||
}
|
||||
cleanup()
|
||||
if !closed {
|
||||
t.Fatal("cleanup must close the device driver")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDriverSurfacesDeviceConstructionError(t *testing.T) {
|
||||
original := newDeviceDriver
|
||||
t.Cleanup(func() { newDeviceDriver = original })
|
||||
newDeviceDriver = func(context.Context, ioscompanion.DeviceOptions) (driver.DeviceDriver, func(), error) {
|
||||
return nil, nil, errors.New("no signing creds")
|
||||
}
|
||||
options := Options{Platform: "ios"}
|
||||
options.iosIsSimulator = false
|
||||
if _, _, err := buildDriver(context.Background(), options, io.Discard); err == nil {
|
||||
t.Fatal("expected the device construction error to surface")
|
||||
}
|
||||
}
|
||||
|
||||
func swapIOSResolveSeams(t *testing.T) {
|
||||
t.Helper()
|
||||
origTarget, origDevice, origEnsure := iosResolveTarget, iosResolveDevice, iosEnsureSimulator
|
||||
t.Cleanup(func() {
|
||||
iosResolveTarget, iosResolveDevice, iosEnsureSimulator = origTarget, origDevice, origEnsure
|
||||
})
|
||||
}
|
||||
|
||||
_, cleanup, err := buildDriver(context.Background(), options, io.Discard)
|
||||
if cleanup != nil {
|
||||
cleanup()
|
||||
func TestResolveIOSTargetPhysicalDeviceFillsIDs(t *testing.T) {
|
||||
swapIOSResolveSeams(t)
|
||||
iosResolveTarget = func(_ context.Context, query string) (string, bool, error) {
|
||||
return query, false, nil
|
||||
}
|
||||
resolveDeviceCalled := ""
|
||||
iosResolveDevice = func(_ context.Context, query string) (ios.Device, error) {
|
||||
resolveDeviceCalled = query
|
||||
return ios.Device{Name: "iPhone", HardwareUDID: "00008140-HW", CoreDeviceID: "CORE-1"}, nil
|
||||
}
|
||||
iosEnsureSimulator = func(context.Context, string, io.Writer) error {
|
||||
t.Fatal("must not boot a simulator on the device path")
|
||||
return nil
|
||||
}
|
||||
|
||||
options := Options{Platform: "ios", IosDevice: "iPhone"}
|
||||
resolved, err := resolveIOSTarget(context.Background(), options, io.Discard)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resolveDeviceCalled != "iPhone" {
|
||||
t.Fatalf("ResolveDevice query = %q, want iPhone", resolveDeviceCalled)
|
||||
}
|
||||
if resolved.iosIsSimulator {
|
||||
t.Fatal("device target must not be marked a simulator")
|
||||
}
|
||||
if resolved.iosUDID != "00008140-HW" || resolved.iosCoreDeviceID != "CORE-1" {
|
||||
t.Fatalf("resolved ids = (%q, %q), want hardware/core ids", resolved.iosUDID, resolved.iosCoreDeviceID)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("expected physical-device iOS to be rejected")
|
||||
}
|
||||
|
||||
func TestResolveIOSTargetSimulatorSkipsDeviceResolution(t *testing.T) {
|
||||
swapIOSResolveSeams(t)
|
||||
iosResolveTarget = func(context.Context, string) (string, bool, error) {
|
||||
return "sim-udid", true, nil
|
||||
}
|
||||
ensured := false
|
||||
iosEnsureSimulator = func(context.Context, string, io.Writer) error { ensured = true; return nil }
|
||||
iosResolveDevice = func(context.Context, string) (ios.Device, error) {
|
||||
t.Fatal("simulator path must not resolve a physical device")
|
||||
return ios.Device{}, nil
|
||||
}
|
||||
|
||||
options := Options{Platform: "ios", IosDevice: "iPhone 17 Pro"}
|
||||
resolved, err := resolveIOSTarget(context.Background(), options, io.Discard)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !ensured {
|
||||
t.Fatal("simulator path must ensure the simulator is booted")
|
||||
}
|
||||
if !resolved.iosIsSimulator || resolved.iosUDID != "sim-udid" {
|
||||
t.Fatalf("resolved = %+v, want the booted simulator", resolved)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveIOSTargetDeviceResolutionErrorSurfaces(t *testing.T) {
|
||||
swapIOSResolveSeams(t)
|
||||
iosResolveTarget = func(_ context.Context, query string) (string, bool, error) { return query, false, nil }
|
||||
iosResolveDevice = func(context.Context, string) (ios.Device, error) {
|
||||
return ios.Device{}, errors.New("no device connected")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "not yet supported") {
|
||||
t.Fatalf("expected an unsupported-physical-iOS error, got %v", err)
|
||||
options := Options{Platform: "ios", IosDevice: "iPhone"}
|
||||
if _, err := resolveIOSTarget(context.Background(), options, io.Discard); err == nil {
|
||||
t.Fatal("expected the device-resolution error to surface")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user