Help us shape the future of Go! Take the 2026 Go Developer Survey

ios

package
v0.96.0 Latest Latest
Warning

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

Go to latest
Published: Oct 21, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

README

iOS runner

The ios Endly service provides iOS Simulator lifecycle and, incrementally, build/signing/deployment and E2E automation described in ios.md.

Implemented actions:

Action Description
ios:doctor Report Xcode tools, installed Simulator runtimes, and available Simulators
ios:build Run Simulator build/build-for-testing and discover/checksum .app/.xctestrun products
ios:simulator-start Attach to, clone, or create and boot a Simulator under a fenced lease
ios:simulator-stop Release the lease, shut down the Simulator, and delete only an owned clone
ios:device-list List paired physical iOS/iPadOS devices known to CoreDevice
ios:device-lease Acquire a process-shared fenced lease for one exact physical-device UDID
ios:device-release Release the lease without terminating, shutting down, or erasing the device
ios:destination-register Fence a provider/cloud device ID without local CoreDevice mutation
ios:destination-release Release an external registration without stopping provider infrastructure
ios:server-start Health-check/register external Appium or start a loopback-only managed server
ios:server-stop Release external registration or idempotently stop an owned server
ios:install Install one compatible Simulator or signed device .app with an explicit policy
ios:uninstall Remove an exact bundle through a valid destination lease
ios:launch Launch an exact bundle with argv-safe arguments/environment and return its PID
ios:terminate Terminate a Simulator bundle or exact physical-device PID through a valid lease
ios:test Run XCTest/test-without-building and normalize modern .xcresult summaries into assertions
ios:capture-start Start an owned Simulator unified-log stream with an optional predicate
ios:capture-stop Stop the stream and return sensitive log evidence metadata
ios:open Open an Appium XCUITest session with managed/prebuilt/preinstalled/external WDA
ios:attach Reconnect this process to an existing Appium session or descriptor
ios:run Run assigned app.*/device.* commands and retrying inline expectations
ios:repl Run a live terminal DSL, hierarchy inspector, screenshot tool, and command history
ios:artifact Store sensitive screenshot and bounded accessibility-source evidence through AFS
ios:close Idempotently close the Appium session
ios:cleanup Run every registered teardown in LIFO order and report all errors

The implementation uses argv-safe local command execution, Xcode destination build/test, archive/export signing inputs, devicectl physical-device lifecycle, and .xcresult tooling, a small W3C/Appium client, a closed mobile DSL AST, and injectable backends for deterministic lifecycle/protocol tests. The DSL supports strict plural lookup, first/last/nth/count, typed and textual commands, pointer gestures, richer element/device assertions, contexts, orientation, location, alerts, permissions, appearance, biometrics, and application lifecycle. Simulator capture can rotate bounded video segments; failed runs automatically retain screenshot, bounded source, bounded active-log tail, a checkpointed valid video segment, and a manifest while recording continues. Managed destinations and Appium ports use process-shared fenced leases, while owned processes still unwind through the LIFO context cleanup stack. Physical-device release never shuts down or erases hardware. Portable 0600 session descriptors let a second Endly process run ios:attach or inline ios:repl.attach; attachment is non-owning unless takeOwnership is explicit. A gated two-Simulator stress test verifies distinct Simulator/Appium/WDA/MJPEG/DerivedData resources under concurrent sessions. The bundled project contains one passing and one intentionally failing UI test; ios:test retains and normalizes the real .xcresult. collectDiagnostics defaults to never to avoid Xcode's ten-minute failure sysdiagnose and accepts on-failure when those diagnostics are wanted. open.wda owns all WDA lifecycle/signing/port capabilities and supports managed, prebuilt, preinstalled, and external; raw conflicting capabilities are rejected. Current compatibility rules reject preinstalled WDA below iOS 17 and on iOS 27+ Simulators, where direct XCTest-runner launch is not viable, with guidance to use another mode. Provider-neutral cloud mode fences an external destination, health-checks external Appium, supplies appReference plus namespaced provider capabilities, and never invokes local build/install/capture/stop against farm infrastructure. Persistent remote execution uses Endly's authenticated control-plane sessions; see mobile remote workers. Provider-specific upload APIs remain adapters around appReference rather than runner-owned credentials. On non-Darwin hosts, ios registers the same action-compatible unsupported-platform stub so Endly still builds and reports a useful error. Connected-device hardware validation remains.

External farm shape:

- action: ios:destination-register
  request: {provider: example-farm, deviceID: iphone-remote, platformVersion: "18.5"}
- action: ios:server-start
  request: {destination: $destinationRegister.Lease, mode: external, serverURL: $secureAppiumURL}
- action: ios:open
  request:
    destination: $destinationRegister.Lease
    server: $serverStart.Server
    appReference: farm://apps/build-456
    capabilities:
      farm:options: {project: endly}

Run the real host check:

endly -r=service/testing/runner/ios/test/doctor.yaml

Run the complete bundled-fixture integration on an installed Simulator runtime:

ENDLY_IOS_EMULATOR_INTEGRATION=1 \
  go test ./service/testing/runner/ios -run '^TestIOSSimulatorIntegration$' -v -count=1

This creates an owned Simulator, builds and checksums test/fixture/FixtureApp, installs and launches it, captures unified logs and a non-empty screenshot, terminates the app, and deletes the Simulator.

To include the complete managed-Appium/XCUITest DSL path:

export PATH=/path/to/node-v24/bin:$PATH
ENDLY_IOS_EMULATOR_INTEGRATION=1 \
ENDLY_IOS_APPIUM_INTEGRATION=1 \
ENDLY_IOS_APPIUM_EXECUTABLE=/path/to/appium \
ENDLY_IOS_APPIUM_HOME=/path/to/appium-home \
  go test ./service/testing/runner/ios -run '^TestIOSSimulatorIntegration$' -v -count=1

That path additionally starts owned Appium/WDA processes, locates the fixture by accessibility identifier, verifies its initial text, taps the increment button, waits for Count: 1, captures Appium screenshot/source evidence, and verifies complete cleanup.

Run the two-lane isolation stress test:

ENDLY_IOS_PARALLEL_INTEGRATION=1 \
  go test ./service/testing/runner/ios -run '^TestIOSParallelSimulatorStress$' -v -count=1

Run the WDA lifecycle compatibility suite:

ENDLY_IOS_WDA_MODES_INTEGRATION=1 \
  go test ./service/testing/runner/ios -run '^TestIOSWDAModesIntegration$' -v -count=1

Query CoreDevice through the physical-device parser:

ENDLY_IOS_DEVICE_LIST_INTEGRATION=1 \
  go test ./service/testing/runner/ios -run '^TestPhysicalDeviceListIntegration$' -v -count=1

Build and checksum a real unsigned archive without requiring signing credentials:

ENDLY_IOS_ARCHIVE_INTEGRATION=1 \
  go test ./service/testing/runner/ios -run '^TestIOSArchiveIntegration$' -v -count=1

Run test/repl.yaml to enter the live inspector. Once setup completes, Endly displays an ios[session]> prompt:

:status
:tree increment
count = app.getByTestId("count").text()
app.getByTestId("increment").tap()
expect(app.getByTestId("count")).toHaveText("Count: 1", 10000)
:screenshot
:history
:quit

Inspector commands are :status, :source, :tree [filter], :find <text>, :screenshot, :history, !<number>, :help, :close, and :quit. In a real terminal, Tab completes DSL/meta commands and Up/Down navigate persistent history. Command errors are printed and the prompt continues unless failOnError is enabled. Ctrl-C returns to the workflow so deferred cleanup runs.

For a deliberate cross-process handoff, select an existing Simulator with keepBooted: true, use an external Appium server, and open with descriptorPath plus keepSession: true. A later process can enter the inspector directly:

action: ios:repl
request:
  attach:
    descriptorPath: /secure/run/ios-session.json
    takeOwnership: false
  historyPath: /secure/run/ios-history.jsonl

Use takeOwnership: true only when :close or cleanup should delete the remote Appium session and its descriptor.

Documentation

Index

Constants

View Source
const ServiceID = "ios"

Variables

This section is empty.

Functions

func New

func New() endly.Service

Types

type Artifact

type Artifact struct {
	Kind        string
	BundleID    string
	HostPath    string
	ArtifactURL string
	SHA256      string
	Size        int64
}

type ArtifactRequest

type ArtifactRequest struct {
	SessionID      string
	Destination    *DestinationLease
	Directory      string
	Screenshot     bool
	PageSource     bool
	MaxSourceBytes int
}

func (*ArtifactRequest) Init

func (r *ArtifactRequest) Init() error

func (*ArtifactRequest) Validate

func (r *ArtifactRequest) Validate() error

type ArtifactResponse

type ArtifactResponse struct {
	Artifacts []*mobile.Evidence
}

type AttachRequest

type AttachRequest struct {
	SessionID        string
	DescriptorPath   string
	BackendSessionID string
	ServerURL        string
	TargetID         string
	TakeOwnership    bool
}

func (*AttachRequest) Validate

func (r *AttachRequest) Validate() error

type AttachResponse

type AttachResponse struct {
	Session SessionHandle
}

type BuildRequest

type BuildRequest struct {
	ProjectPath        string
	WorkspacePath      string
	Scheme             string
	Configuration      string
	Destination        DestinationLease
	DerivedDataPath    string
	Mode               string
	SDK                string
	ArchivePath        string
	ExportPath         string
	ExportOptionsPlist string
	Signing            *IOSSigningProfile
	BuildSettings      map[string]string
	TimeoutMs          int
}

func (*BuildRequest) Init

func (r *BuildRequest) Init() error

func (*BuildRequest) Validate

func (r *BuildRequest) Validate() error

type BuildResponse

type BuildResponse struct {
	Artifacts  []Artifact
	Stdout     string
	Stderr     string
	DurationMs int
}

type CaptureHandle

type CaptureHandle struct {
	ID          string
	Destination DestinationLease
	PID         int
	LogPath     string
}

type CaptureStartRequest

type CaptureStartRequest struct {
	Destination    DestinationLease
	Predicate      string
	LogPath        string
	Video          bool
	VideoDirectory string
	SegmentMs      int
}

func (*CaptureStartRequest) Init

func (r *CaptureStartRequest) Init() error

func (*CaptureStartRequest) Validate

func (r *CaptureStartRequest) Validate() error

type CaptureStartResponse

type CaptureStartResponse struct{ Capture CaptureHandle }

type CaptureStopRequest

type CaptureStopRequest struct{ Capture CaptureHandle }

func (*CaptureStopRequest) Validate

func (r *CaptureStopRequest) Validate() error

type CaptureStopResponse

type CaptureStopResponse struct {
	Stopped   bool
	Artifact  *mobile.Evidence
	Artifacts []*mobile.Evidence
	Errors    []string
	Warning   string
}

type CleanupRequest

type CleanupRequest struct {
	Session     *SessionHandle
	Server      *ServerHandle
	Destination *DestinationLease
}

type CleanupResponse

type CleanupResponse struct {
	Errors []mobile.CleanupError
}

type CloseRequest

type CloseRequest struct {
	SessionID string
}

func (*CloseRequest) Validate

func (r *CloseRequest) Validate() error

type CloseResponse

type CloseResponse struct {
	Closed  bool
	Warning string
}

type DestinationLease

type DestinationLease struct {
	ID                string
	Fence             uint64
	UDID              string
	Name              string
	Runtime           string
	Kind              string // simulator or device; empty is legacy simulator
	OwnedClone        bool
	PreserveOnRelease bool
	ProcessLease      *mobile.LeaseHandle
}

func (DestinationLease) IsDevice

func (l DestinationLease) IsDevice() bool

func (DestinationLease) IsExternal

func (l DestinationLease) IsExternal() bool

type DestinationRegisterRequest

type DestinationRegisterRequest struct {
	Provider        string
	DeviceID        string
	PlatformVersion string
}

func (*DestinationRegisterRequest) Validate

func (r *DestinationRegisterRequest) Validate() error

type DestinationRegisterResponse

type DestinationRegisterResponse struct {
	Lease DestinationLease
}

type DestinationReleaseRequest

type DestinationReleaseRequest struct {
	Lease DestinationLease
}

func (*DestinationReleaseRequest) Validate

func (r *DestinationReleaseRequest) Validate() error

type DestinationReleaseResponse

type DestinationReleaseResponse struct {
	Released bool
	Warning  string
}

type DeviceLeaseRequest

type DeviceLeaseRequest struct {
	UDID      string
	TimeoutMs int
}

func (*DeviceLeaseRequest) Init

func (r *DeviceLeaseRequest) Init() error

func (*DeviceLeaseRequest) Validate

func (r *DeviceLeaseRequest) Validate() error

type DeviceLeaseResponse

type DeviceLeaseResponse struct {
	Lease  DestinationLease
	Device PhysicalDevice
}

type DeviceListRequest

type DeviceListRequest struct {
	TimeoutMs int
}

func (*DeviceListRequest) Init

func (r *DeviceListRequest) Init() error

type DeviceListResponse

type DeviceListResponse struct {
	Devices []PhysicalDevice
}

type DeviceReleaseRequest

type DeviceReleaseRequest struct {
	Lease DestinationLease
}

func (*DeviceReleaseRequest) Validate

func (r *DeviceReleaseRequest) Validate() error

type DeviceReleaseResponse

type DeviceReleaseResponse struct {
	Released bool
	Warning  string
}

type DoctorRequest

type DoctorRequest struct {
	Required []string
}

func (*DoctorRequest) Init

func (r *DoctorRequest) Init() error

type DoctorResponse

type DoctorResponse struct {
	Ready      bool
	Checks     []mobile.Check
	Runtimes   []string
	Simulators []IOSSimulator
	Devices    []PhysicalDevice
}

func (*DoctorResponse) IsOutput

func (r *DoctorResponse) IsOutput() bool

func (*DoctorResponse) Messages

func (r *DoctorResponse) Messages() []*msg.Message

type IOSSigningProfile

type IOSSigningProfile struct {
	Style               string // automatic or manual
	TeamID              string
	Identity            string
	ProvisioningProfile string
	KeychainPath        string // must already be unlocked by the worker
}

type IOSSimulator

type IOSSimulator struct {
	UDID        string
	Name        string
	State       string
	Runtime     string
	IsAvailable bool
}

type InstallRequest

type InstallRequest struct {
	Destination DestinationLease
	App         Artifact
	BundleID    string
	State       string
}

func (*InstallRequest) Init

func (r *InstallRequest) Init() error

func (*InstallRequest) Validate

func (r *InstallRequest) Validate() error

type InstallResponse

type InstallResponse struct {
	Installed bool
	BundleID  string
	AppPath   string
	State     string
}

type LaunchRequest

type LaunchRequest struct {
	Destination DestinationLease
	BundleID    string
	Arguments   []string
	Environment map[string]string
}

func (*LaunchRequest) Validate

func (r *LaunchRequest) Validate() error

type LaunchResponse

type LaunchResponse struct {
	PID    int
	Output string
}

type OpenRequest

type OpenRequest struct {
	SessionID      string
	Destination    DestinationLease
	Server         ServerHandle
	BundleID       string
	App            *Artifact
	AppReference   string
	Capabilities   map[string]interface{}
	DescriptorPath string
	KeepSession    bool
	WDA            *WDAOptions
}

func (*OpenRequest) Init

func (r *OpenRequest) Init() error

func (*OpenRequest) Validate

func (r *OpenRequest) Validate() error

type OpenResponse

type OpenResponse struct {
	Session SessionHandle
}

type PhysicalDevice

type PhysicalDevice struct {
	Identifier    string
	UDID          string
	Name          string
	Platform      string
	OSVersion     string
	State         string
	Connection    string
	PairingState  string
	DeveloperMode string
}

type REPLRequest

type REPLRequest struct {
	SessionID         string
	Attach            *AttachRequest
	Prompt            string
	ArtifactDirectory string
	ActionTimeoutMs   int
	PollIntervalMs    int
	MaxSourceBytes    int
	MaxTreeNodes      int
	FailOnError       bool
	HistoryPath       string
	MaxHistory        int
}

func (*REPLRequest) Init

func (r *REPLRequest) Init() error

func (*REPLRequest) Validate

func (r *REPLRequest) Validate() error

type REPLResponse

type REPLResponse struct {
	SessionID string
	Result    *mobile.REPLResult
}

type RunRequest

type RunRequest struct {
	SessionID        string
	Commands         []interface{}
	ActionTimeoutMs  int
	PollIntervalMs   int
	FailureArtifacts *mobile.FailureArtifactOptions
}

func (*RunRequest) Init

func (r *RunRequest) Init() error

func (*RunRequest) Validate

func (r *RunRequest) Validate() error

type RunResponse

type RunResponse struct {
	Data        map[string]interface{}
	Steps       []mobile.ExecutionStep
	Validations []*assertly.Validation
	Failures    []*mobile.FailureEvidence
}

func (*RunResponse) Assertion

func (r *RunResponse) Assertion() []*assertly.Validation

type ServerHandle

type ServerHandle struct {
	ID           string
	Endpoint     string
	Ownership    string
	PID          int
	LogPath      string
	ProcessLease *mobile.LeaseHandle
}

type ServerStartRequest

type ServerStartRequest struct {
	Destination      DestinationLease
	Mode             string
	ServerURL        string
	Executable       string
	AppiumHome       string
	Address          string
	Port             int
	BasePath         string
	LogPath          string
	StartupTimeoutMs int
}

func (*ServerStartRequest) Init

func (r *ServerStartRequest) Init() error

func (*ServerStartRequest) Validate

func (r *ServerStartRequest) Validate() error

type ServerStartResponse

type ServerStartResponse struct{ Server ServerHandle }

type ServerStopRequest

type ServerStopRequest struct{ Server ServerHandle }

func (*ServerStopRequest) Validate

func (r *ServerStopRequest) Validate() error

type ServerStopResponse

type ServerStopResponse struct {
	Stopped bool
	Warning string
}

type SessionHandle

type SessionHandle struct {
	ID               string
	BackendSessionID string
	Destination      DestinationLease
	Server           ServerHandle
}

type SimulatorStartRequest

type SimulatorStartRequest struct {
	UDID          string
	BaseName      string
	CloneName     string
	DeviceType    string
	Runtime       string
	Erase         bool
	KeepBooted    bool
	BootTimeoutMs int
}

func (*SimulatorStartRequest) Init

func (r *SimulatorStartRequest) Init() error

func (*SimulatorStartRequest) Validate

func (r *SimulatorStartRequest) Validate() error

type SimulatorStartResponse

type SimulatorStartResponse struct {
	Lease DestinationLease
}

type SimulatorStopRequest

type SimulatorStopRequest struct {
	Lease DestinationLease
}

func (*SimulatorStopRequest) Validate

func (r *SimulatorStopRequest) Validate() error

type SimulatorStopResponse

type SimulatorStopResponse struct {
	Shutdown bool
	Deleted  bool
	Warning  string
}

type TerminateRequest

type TerminateRequest struct {
	Destination DestinationLease
	BundleID    string
	PID         int
}

func (*TerminateRequest) Validate

func (r *TerminateRequest) Validate() error

type TerminateResponse

type TerminateResponse struct{ Terminated bool }

type TestRequest

type TestRequest struct {
	Destination        DestinationLease
	ProjectPath        string
	WorkspacePath      string
	Scheme             string
	XCTestRunPath      string
	Mode               string
	OnlyTesting        []string
	SkipTesting        []string
	ResultBundlePath   string
	DerivedDataPath    string
	CollectDiagnostics string
	TimeoutMs          int
}

func (*TestRequest) Init

func (r *TestRequest) Init() error

func (*TestRequest) Validate

func (r *TestRequest) Validate() error

type TestResponse

type TestResponse struct {
	Summary     XCTestSummary
	Output      string
	DurationMs  int
	Validations []*assertly.Validation
}

func (*TestResponse) Assertion

func (r *TestResponse) Assertion() []*assertly.Validation

type UninstallRequest

type UninstallRequest struct {
	Destination DestinationLease
	BundleID    string
}

func (*UninstallRequest) Validate

func (r *UninstallRequest) Validate() error

type UninstallResponse

type UninstallResponse struct {
	Removed  bool
	BundleID string
}

type WDAOptions

type WDAOptions struct {
	Mode                  string // managed, prebuilt, preinstalled, external
	DerivedDataPath       string
	PrebuiltWDAPath       string
	WebDriverAgentURL     string
	UpdatedBundleID       string
	UpdatedBundleIDSuffix *string
	XcodeOrgID            string
	XcodeSigningID        string
	XcodeConfigFile       string
	KeychainPath          string
	KeychainPasswordFile  string
	LocalPort             int
	MJPEGServerPort       int
	UseNewWDA             *bool
	PrebuildWDA           bool
	LaunchTimeoutMs       int
	ConnectionTimeoutMs   int
	StartupRetries        int
	StartupRetryMs        int
}

WDAOptions owns the WebDriverAgent lifecycle capabilities so callers cannot smuggle conflicting raw capabilities into OpenRequest.Capabilities.

func (*WDAOptions) Init

func (o *WDAOptions) Init()

func (*WDAOptions) Validate

func (o *WDAOptions) Validate() error

type XCTestFailure

type XCTestFailure struct {
	TestName       string `json:"testName"`
	TargetName     string `json:"targetName"`
	FailureText    string `json:"failureText"`
	TestIdentifier string `json:"testIdentifierString"`
}

type XCTestSummary

type XCTestSummary struct {
	Title            string          `json:"title"`
	Result           string          `json:"result"`
	TotalTestCount   int             `json:"totalTestCount"`
	PassedTests      int             `json:"passedTests"`
	FailedTests      int             `json:"failedTests"`
	SkippedTests     int             `json:"skippedTests"`
	ExpectedFailures int             `json:"expectedFailures"`
	TestFailures     []XCTestFailure `json:"testFailures"`
}

Jump to

Keyboard shortcuts

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