ios

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Aug 6, 2026 License: Apache-2.0 Imports: 17 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 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) 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) 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) 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"`
}

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

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 *Simctl, runner *RunnerBundle,
) *Driver

NewDriver binds a runner client and a simctl surface to one udid.

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 is a no-op: specs/02-device-drivers.md line 49 defines that iOS has no back gesture at the driver level. Returning an error would fail every flow that shares a back step with Android.

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 uninstalls. A simulator has no per-app "clear data" verb, and reporting success without clearing anything would leave a flow running against the previous session's data.

func (*Driver) ClearKeychain

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

func (*Driver) Close

func (driver *Driver) Close(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(
	context.Context,
	device.ArtifactRequest,
) ([]device.Artifact, error)

func (*Driver) ContentDescriptor

func (driver *Driver) ContentDescriptor(
	ctx context.Context,
	request device.ContentDescriptorRequest,
) (device.TreeNode, 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.

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(
	context.Context,
	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(
	context.Context,
	device.CaptureID,
) ([]device.Artifact, error)

func (*Driver) StopScreenRecording

func (driver *Driver) StopScreenRecording(
	_ 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 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) Boot

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

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