ios

package
v0.2.0-beta.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 4, 2026 License: Apache-2.0 Imports: 22 Imported by: 0

Documentation

Overview

Package ios is the host-side client for the iOS XCTest runner's HTTP API.

contracts/v0/ios-http.json is the contract: eighteen routes on a loopback server, their exact JSON shapes, and the mapping from HTTP status to error code. Nothing here talks to a simulator; the client is pure transport and is testable with an HTTP server without a simulator.

Index

Constants

View Source
const DefaultPort = 22087

DefaultPort is the runner's default loopback port, frozen by the contract's transport block.

View Source
const Platform = device.Platform("ios")

Platform is the device platform this driver reports.

Variables

This section is empty.

Functions

func DeclaredCapabilities

func DeclaredCapabilities() device.Capabilities

DeclaredCapabilities is the non-mutating iOS Simulator capability document used by both the driver and selected-platform flow preflight.

func DefaultBaseURL

func DefaultBaseURL(port int) string

DefaultBaseURL renders the contract's loopback address for a port.

Types

type AXElement

type AXElement struct {
	Identifier          string      `json:"identifier"`
	Frame               Frame       `json:"frame"`
	Value               *string     `json:"value,omitempty"`
	Title               *string     `json:"title,omitempty"`
	Label               string      `json:"label"`
	ElementType         int         `json:"elementType"`
	Enabled             bool        `json:"enabled"`
	HorizontalSizeClass int         `json:"horizontalSizeClass"`
	VerticalSizeClass   int         `json:"verticalSizeClass"`
	PlaceholderValue    *string     `json:"placeholderValue,omitempty"`
	Selected            bool        `json:"selected"`
	HasFocus            bool        `json:"hasFocus"`
	Children            []AXElement `json:"children,omitempty"`
	WindowContextID     float64     `json:"windowContextID"`
	DisplayID           int         `json:"displayID"`
}

AXElement is one node of the runner's accessibility hierarchy.

type Button

type Button string

Button is the pressButton vocabulary.

const (
	ButtonHome Button = "home"
	ButtonLock Button = "lock"
)

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client speaks the frozen runner API.

func NewClient

func NewClient(baseURL string, options ...Option) *Client

NewClient builds a client for a runner reachable at baseURL.

func (*Client) DeviceInfo

func (client *Client) DeviceInfo(ctx context.Context) (DeviceInfo, error)

func (*Client) EraseText

func (client *Client) EraseText(ctx context.Context, characters int, appIDs []string) error

func (*Client) Identity

func (client *Client) Identity(ctx context.Context) (string, error)

Identity is the health check that also answers who is serving: the runner echoes the id the host launched it with, so a driver that started a runner can tell its own child from a stranger holding the same port. A healthy runner launched without an id answers "".

func (*Client) InputText

func (client *Client) InputText(ctx context.Context, text string, appIDs []string) error

func (*Client) IsScreenStatic

func (client *Client) IsScreenStatic(ctx context.Context) (bool, error)

func (*Client) KeyboardVisible

func (client *Client) KeyboardVisible(ctx context.Context, appIDs []string) (bool, error)

func (*Client) LaunchApp

func (client *Client) LaunchApp(ctx context.Context, bundleID string) error

func (*Client) PressButton

func (client *Client) PressButton(ctx context.Context, button Button) error

func (*Client) PressKey

func (client *Client) PressKey(ctx context.Context, key Key, appIDs []string) error

func (*Client) RunningApp

func (client *Client) RunningApp(ctx context.Context, appIDs []string) (string, error)

func (*Client) Screenshot

func (client *Client) Screenshot(ctx context.Context, compressed bool) ([]byte, error)

Screenshot returns the raw image bytes: JPEG when compressed, PNG otherwise.

func (*Client) SetOrientation

func (client *Client) SetOrientation(ctx context.Context, orientation Orientation) error

func (*Client) SetPermissions

func (client *Client) SetPermissions(ctx context.Context, permissions map[string]string) error

func (*Client) SetTransportHint

func (client *Client) SetTransportHint(hint func() string)

SetTransportHint installs the explanation to attach when the runner cannot be reached at all. Only the owner of the runner process can supply it, which is why it arrives after construction rather than as an Option.

func (*Client) Status

func (client *Client) Status(ctx context.Context) error

Status is the health check. A 200 carrying anything other than "ok" is not a healthy runner, and reporting it as one would be worse than an error.

func (*Client) Swipe

func (client *Client) Swipe(ctx context.Context, request SwipeRequest) error

func (*Client) SwipeV2

func (client *Client) SwipeV2(ctx context.Context, request SwipeV2Request) error

func (*Client) TerminateApp

func (client *Client) TerminateApp(ctx context.Context, appID string) error

func (*Client) Touch

func (client *Client) Touch(ctx context.Context, request TouchRequest) error

func (*Client) ViewHierarchy

func (client *Client) ViewHierarchy(
	ctx context.Context,
	appIDs []string,
	excludeKeyboardElements bool,
) (ViewHierarchy, error)

type Code

type Code string

Code is the runner's error vocabulary, frozen by the contract.

const (
	CodeInternal     Code = "internal"
	CodePrecondition Code = "precondition"
	CodeTimeout      Code = "timeout"
)

type CommandRunner

type CommandRunner interface {
	Run(ctx context.Context, name string, args ...string) ([]byte, error)
}

CommandRunner executes one external command and returns its combined output.

type Device

type Device struct {
	UDID      string
	Name      string
	State     string
	Runtime   string
	Available bool
}

Device is one entry of the simulator inventory.

type DeviceInfo

type DeviceInfo struct {
	WidthPoints  float64           `json:"widthPoints"`
	HeightPoints float64           `json:"heightPoints"`
	WidthPixels  float64           `json:"widthPixels"`
	HeightPixels float64           `json:"heightPixels"`
	Orientation  ScreenOrientation `json:"orientation"`
}

DeviceInfo is the runner's screen geometry, in points and pixels.

type DeviceTools

type DeviceTools interface {
	Launch(ctx context.Context, bundleID string, arguments []LaunchArgument, terminateRunning bool) error
	Terminate(ctx context.Context, bundleID string) error
	Uninstall(ctx context.Context, bundleID string) error
	AppContainer(ctx context.Context, bundleID string) (string, error)
	Install(ctx context.Context, appPath string) error
	Diagnose(ctx context.Context, outputDirectory string, timeout time.Duration) error
	ResetKeychain(ctx context.Context) error
	OpenURL(ctx context.Context, url string) error
	SetLocation(ctx context.Context, latitude, longitude float64) error
	AddMedia(ctx context.Context, paths []string) error
	SetPermission(ctx context.Context, bundleID, permission, grant string) error
}

DeviceTools is the out-of-app half of the driver: everything it does to the device from outside the runner's wire. The simulator implementation is Simctl; a physical device supplies its own implementation, and the Driver cannot tell them apart. The in-app half stays on Client.

type Driver

type Driver struct {
	// contains filtered or unexported fields
}

Driver drives one simulator.

func NewDriver

func NewDriver(
	udid string, port int, client *Client, simctl DeviceTools, runner *RunnerBundle,
) *Driver

NewDriver binds a runner client and a device-tools surface to one udid. The simulator passes Simctl; a physical device passes its own DeviceTools.

func (*Driver) AddMedia

func (driver *Driver) AddMedia(ctx context.Context, request device.AddMediaRequest) error

func (*Driver) BackPress

func (driver *Driver) BackPress(context.Context) error

BackPress agrees with Capabilities: iOS has no platform back gesture.

func (*Driver) Capabilities

func (driver *Driver) Capabilities() device.Capabilities

Capabilities declares what this driver refuses. Every false here has a matching ErrUnsupported at call time; preflight reads this so a flow is refused before it runs rather than halfway through.

func (*Driver) ClearAppState

func (driver *Driver) ClearAppState(ctx context.Context, request device.AppRequest) error

ClearAppState preserves the installed application bundle, uninstalls it to clear its data container, then reinstalls the same bundle. The engine applies permissions and launches only after this call succeeds.

func (*Driver) ClearKeychain

func (driver *Driver) ClearKeychain(ctx context.Context) error

func (*Driver) Close

func (driver *Driver) Close(ctx context.Context) error

Close stops a runner this driver started and leaves the simulator as it was. Shutting the simulator down would destroy the state an operator may want to inspect after a failed run, and the session that booted it is the one that should decide its fate. An operator-started runner is left alone for the same reason.

func (*Driver) CollectCrashArtifacts

func (driver *Driver) CollectCrashArtifacts(
	ctx context.Context,
	request device.ArtifactRequest,
) ([]device.Artifact, error)

func (*Driver) ContentDescriptor

func (driver *Driver) ContentDescriptor(
	ctx context.Context,
	request device.ContentDescriptorRequest,
) (device.TreeNode, error)

func (*Driver) CurrentOrientation

func (driver *Driver) CurrentOrientation(ctx context.Context) (device.Orientation, error)

func (*Driver) DeviceInfo

func (driver *Driver) DeviceInfo(ctx context.Context) (device.DeviceInfo, error)

func (*Driver) EraseText

func (driver *Driver) EraseText(ctx context.Context, request device.EraseTextRequest) error

func (*Driver) HideKeyboard

func (driver *Driver) HideKeyboard(ctx context.Context) error

HideKeyboard has no route of its own. The keyboard dismisses on return, which is the gesture a person would use.

The press is skipped when no keyboard is up, and not only to save a round trip: the runner refuses to type when nothing on screen accepts text, so asking it to press Return on a screen without a keyboard turns a keyboard that is already hidden into a failed command.

func (*Driver) InputText

func (driver *Driver) InputText(ctx context.Context, request device.InputTextRequest) error

func (*Driver) IsAirplaneModeEnabled

func (driver *Driver) IsAirplaneModeEnabled(context.Context) (bool, error)

func (*Driver) IsKeyboardVisible

func (driver *Driver) IsKeyboardVisible(ctx context.Context, request device.KeyboardRequest) (bool, error)

func (*Driver) IsShutdown

func (driver *Driver) IsShutdown(ctx context.Context) (bool, error)

IsShutdown asks the runner, not simctl. A booted simulator whose runner has died is unusable for a flow even though simctl still calls it Booted.

func (*Driver) KillApp

func (driver *Driver) KillApp(ctx context.Context, request device.AppRequest) error

KillApp is StopApp on iOS. A simulator has no distinction between a graceful stop and a kill: simctl terminate is the only verb, and inventing a difference would mean one of the two silently does the other's job.

func (*Driver) LaunchApp

func (driver *Driver) LaunchApp(ctx context.Context, request device.LaunchAppRequest) error

LaunchApp goes through simctl, not the runner: only simctl can carry the typed launch arguments, and the runner's route would drop them silently.

func (*Driver) LongPress

func (driver *Driver) LongPress(ctx context.Context, request device.LongPressRequest) error

LongPress sends the same route with a duration. TouchRequest.Duration is a pointer because its presence is what makes the touch a long press, so a tap must not send a zero.

func (*Driver) Name

func (driver *Driver) Name() string

Name identifies the runner this driver talks to, port included. Each shard has its own port, so the pair identifies the runner in operator messages.

func (*Driver) Open

func (driver *Driver) Open(ctx context.Context) error

Open confirms the runner is answering so absence is reported as a setup failure. With a bundle it owns the runner's whole life: start it, then poll until it answers. See managed_runner.go.

func (driver *Driver) OpenLink(ctx context.Context, request device.OpenLinkRequest) error

OpenLink hands the URL to the simulator, which opens it in the system default. A browser choice cannot be honored, and honoring it silently in the wrong browser would be worse than refusing.

func (*Driver) PressKey

func (driver *Driver) PressKey(ctx context.Context, request device.PressKeyRequest) error

func (*Driver) QueryOnDeviceElements

func (driver *Driver) QueryOnDeviceElements(
	context.Context,
	device.QueryRequest,
) ([]device.TreeNode, error)

QueryOnDeviceElements has no route in the frozen contract. The runner returns a whole hierarchy; it does not evaluate queries against it.

func (*Driver) ResetProxy

func (driver *Driver) ResetProxy(context.Context) error

func (*Driver) ScrollVertical

func (driver *Driver) ScrollVertical(ctx context.Context, request device.ScrollVerticalRequest) error

ScrollVertical is a swipe across the middle of the screen. The runner has no scroll route, so the distance comes from the device's own geometry rather than a guessed constant.

func (*Driver) SetAirplaneMode

func (driver *Driver) SetAirplaneMode(context.Context, device.AirplaneModeRequest) error

func (*Driver) SetAndroidChromeDevToolsEnabled

func (driver *Driver) SetAndroidChromeDevToolsEnabled(
	context.Context,
	device.ChromeDevToolsRequest,
) error

func (*Driver) SetLocation

func (driver *Driver) SetLocation(ctx context.Context, location device.Location) error

func (*Driver) SetOrientation

func (driver *Driver) SetOrientation(ctx context.Context, orientation device.Orientation) error

func (*Driver) SetPermissions

func (driver *Driver) SetPermissions(ctx context.Context, request device.PermissionsRequest) error

SetPermissions issues one simctl call per permission. simctl takes a single service per invocation, and map order must not decide what gets applied first — a permission change can terminate the app, so the order is observable.

func (*Driver) SetProxy

func (driver *Driver) SetProxy(context.Context, device.Proxy) error

func (*Driver) StartDeviceLogCapture

func (driver *Driver) StartDeviceLogCapture(
	ctx context.Context,
	request device.DeviceLogRequest,
) (device.CaptureID, error)

func (*Driver) StartScreenRecording

func (driver *Driver) StartScreenRecording(
	ctx context.Context,
	request device.ScreenRecordingRequest,
) (device.CaptureID, error)

StartScreenRecording spawns `xcrun simctl io <udid> recordVideo <sink>` as a long-lived child (specs/02-device-drivers.md line 9) and returns a CaptureID that StopScreenRecording later stops. simctl writes straight to the sink and finalizes the .mov on SIGINT, so the sink is the artifact once stop returns.

func (*Driver) StopApp

func (driver *Driver) StopApp(ctx context.Context, request device.AppRequest) error

func (*Driver) StopDeviceLogCapture

func (driver *Driver) StopDeviceLogCapture(
	ctx context.Context,
	id device.CaptureID,
) ([]device.Artifact, error)

func (*Driver) StopScreenRecording

func (driver *Driver) StopScreenRecording(
	ctx context.Context,
	id device.CaptureID,
) ([]device.Artifact, error)

StopScreenRecording ends a recording StartScreenRecording began and returns the artifact at its sink. It is deliberately NOT on the frozen Driver surface — v0 declares only the start half — so the recording controller completes the lifecycle by calling this concrete method directly.

func (*Driver) Swipe

func (driver *Driver) Swipe(ctx context.Context, request device.SwipeRequest) error

Swipe covers all three shapes specs/02-device-drivers.md §1 gives the driver: explicit points, a bare direction, and an element point plus a direction.

Direction-only swipes resolve screen geometry through the device before calculating their endpoints.

func (*Driver) TakeScreenshot

func (driver *Driver) TakeScreenshot(
	ctx context.Context,
	request device.ScreenshotRequest,
) ([]byte, error)

func (*Driver) Tap

func (driver *Driver) Tap(ctx context.Context, request device.TapRequest) error

func (*Driver) WaitForAppToSettle

func (driver *Driver) WaitForAppToSettle(
	ctx context.Context,
	request device.SettleRequest,
) (*device.ViewHierarchy, error)

WaitForAppToSettle returns nil when settling cannot be confirmed. Callers must not interpret nil as settled confirmation.

func (*Driver) WaitUntilScreenIsStatic

func (driver *Driver) WaitUntilScreenIsStatic(
	ctx context.Context,
	_ device.ScreenStaticRequest,
) (bool, error)

type Error

type Error struct {
	Code    Code
	Message string
	Status  int
}

Error is a runner-reported failure. The body's code is authoritative; the status mapping is the fallback for a runner that answers without one.

func (*Error) Error

func (err *Error) Error() string

func (*Error) Retryable

func (err *Error) Retryable() bool

Retryable reports whether resending the request could plausibly succeed. Both XCUITest timeout signatures the contract pins are non-retryable, and a precondition failure will fail again the same way; only an internal error is worth another attempt.

type ExecRunner

type ExecRunner struct{}

ExecRunner runs commands for real.

func (ExecRunner) Run

func (ExecRunner) Run(ctx context.Context, name string, args ...string) ([]byte, error)

type Frame

type Frame struct {
	X      float64 `json:"X"`
	Y      float64 `json:"Y"`
	Width  float64 `json:"Width"`
	Height float64 `json:"Height"`
}

Frame is an element's screen-space rectangle.

type Key

type Key string

Key is the pressKey vocabulary.

const (
	KeyDelete Key = "delete"
	KeyReturn Key = "return"
	KeyEnter  Key = "enter"
	KeyTab    Key = "tab"
	KeySpace  Key = "space"
	KeyEscape Key = "escape"
)

type LaunchArgument

type LaunchArgument struct {
	Key   string
	Value string
	Type  string
}

LaunchArgument is one typed launch argument. Type uses the public documentation's vocabulary: string, boolean, integer, double.

type Option

type Option func(*Client)

Option customizes a client at construction.

func WithHTTPClient

func WithHTTPClient(client *http.Client) Option

WithHTTPClient replaces the underlying HTTP client, for callers that need their own transport or timeout.

type Orientation

type Orientation string

Orientation is the setOrientation vocabulary.

const (
	OrientationPortrait       Orientation = "portrait"
	OrientationLandscapeLeft  Orientation = "landscapeLeft"
	OrientationLandscapeRight Orientation = "landscapeRight"
	OrientationUpsideDown     Orientation = "upsideDown"
)

type RunnerBundle

type RunnerBundle struct {
	XCTestRun string
}

RunnerBundle names a prebuilt runner the driver may start itself. A nil bundle selects operator-started mode, where the runner must already serve.

XCTestRun is the .xctestrun `xcodebuild build-for-testing` leaves in the derived-data products directory. It is the whole reason a self-starting runner is possible: `test-without-building -xctestrun <path>` needs no Xcode project at run time, only the built products the file points at.

type ScreenOrientation

type ScreenOrientation string

ScreenOrientation is the deviceInfo orientation vocabulary.

const (
	ScreenOrientationPortrait       ScreenOrientation = "portrait"
	ScreenOrientationUpsideDown     ScreenOrientation = "portrait-upside-down"
	ScreenOrientationLandscapeLeft  ScreenOrientation = "landscape-left"
	ScreenOrientationLandscapeRight ScreenOrientation = "landscape-right"
)

type Simctl

type Simctl struct {
	// contains filtered or unexported fields
}

Simctl is the typed surface over `xcrun simctl` for one simulator.

func NewSimctl

func NewSimctl(udid string, runner CommandRunner) *Simctl

NewSimctl binds a udid to a runner. An empty udid is legal only for the commands that are not device-scoped, such as ListDevices.

func (*Simctl) AddMedia

func (simctl *Simctl) AddMedia(ctx context.Context, paths []string) error

func (*Simctl) AppContainer

func (simctl *Simctl) AppContainer(ctx context.Context, bundleID string) (string, error)

AppContainer returns the installed .app bundle path, not its data container. ClearAppState preserves that bundle before uninstalling the application.

func (*Simctl) Boot

func (simctl *Simctl) Boot(ctx context.Context) error

func (*Simctl) Diagnose

func (simctl *Simctl) Diagnose(ctx context.Context, outputDirectory string, timeout time.Duration) error

Diagnose collects a device-scoped archive without opening Finder. Both the host context and simctl's own timeout bound collection.

func (*Simctl) Install

func (simctl *Simctl) Install(ctx context.Context, appPath string) error

func (*Simctl) Launch

func (simctl *Simctl) Launch(
	ctx context.Context,
	bundleID string,
	arguments []LaunchArgument,
	terminateRunning bool,
) error

Launch starts an app. terminateRunning maps onto simctl's own --terminate-running-process, which is how stopApp is expressed here.

func (*Simctl) ListDevices

func (simctl *Simctl) ListDevices(ctx context.Context) ([]Device, error)

ListDevices returns every available simulator across runtimes. Unavailable entries are dropped: they cannot be booted, so offering them as targets would only produce a later failure.

func (*Simctl) OpenURL

func (simctl *Simctl) OpenURL(ctx context.Context, url string) error

func (*Simctl) ResetKeychain

func (simctl *Simctl) ResetKeychain(ctx context.Context) error

ResetKeychain clears the whole simulator keychain, which is what clearKeychain means on iOS.

func (*Simctl) Screenshot

func (simctl *Simctl) Screenshot(ctx context.Context, outputPath string) error

func (*Simctl) SetLocation

func (simctl *Simctl) SetLocation(ctx context.Context, latitude, longitude float64) error

SetLocation sets the simulated location. simctl takes one "lat,lon" argument, and the numbers are rendered without an exponent so a coordinate never reaches the device in a form it cannot parse.

func (*Simctl) SetPermission

func (simctl *Simctl) SetPermission(ctx context.Context, bundleID, permission, grant string) error

SetPermission maps the authored grant onto simctl's privacy verbs. The grant set is the same exact three the engine validates, so an unknown one is refused here rather than passed to the device as a stray verb.

func (*Simctl) Shutdown

func (simctl *Simctl) Shutdown(ctx context.Context) error

func (*Simctl) Terminate

func (simctl *Simctl) Terminate(ctx context.Context, bundleID string) error

Terminate stops an app, and treats an app that was not running as success.

simctl exits 3 with "found nothing to terminate" when the app is already stopped. The caller's goal is satisfied in that case. Handle it here because StopApp and KillApp share this behavior.

func (*Simctl) Uninstall

func (simctl *Simctl) Uninstall(ctx context.Context, bundleID string) error

type SwipeRequest

type SwipeRequest struct {
	AppID    string  `json:"appId,omitempty"`
	StartX   float64 `json:"startX"`
	StartY   float64 `json:"startY"`
	EndX     float64 `json:"endX"`
	EndY     float64 `json:"endY"`
	Duration float64 `json:"duration"`
}

SwipeRequest is the v1 swipe, which carries a single optional appId.

type SwipeV2Request

type SwipeV2Request struct {
	StartX   float64  `json:"startX"`
	StartY   float64  `json:"startY"`
	EndX     float64  `json:"endX"`
	EndY     float64  `json:"endY"`
	Duration float64  `json:"duration"`
	AppIDs   []string `json:"appIds,omitempty"`
}

SwipeV2Request is the orientation-aware swipe, which carries an appId list.

type TouchRequest

type TouchRequest struct {
	X        float64  `json:"x"`
	Y        float64  `json:"y"`
	Duration *float64 `json:"duration,omitempty"`
}

TouchRequest taps at a point. Duration is a pointer because its presence is what turns the touch into a long press, so an absent one must not be sent as a zero.

type ViewHierarchy

type ViewHierarchy struct {
	AXElement AXElement `json:"axElement"`
	Depth     int       `json:"depth"`
}

ViewHierarchy is the runner's hierarchy response.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL