Marathon
Marathon is a test runner for parallel execution on local device pools. Patrol
integrates with Marathon the same way as other device farms: build standard
native test artifacts with patrol build, then run Marathon externally.
Before you proceed, complete the native setup guide for your target platform.
Prerequisites
- Android: any OS with Android SDK and emulators or physical devices
- iOS (local Marathon): macOS with Xcode, iOS simulators, and Java (installed automatically with Marathon via Homebrew)
- Patrol CLI matching your project's
patrolpackage version - Marathon CLI 0.10+:
brew tap malinskiy/tap
brew install malinskiy/tap/marathonAndroid
Patrol emits standard instrumentation APKs. Set testParserConfiguration.type to
remote so Marathon discovers Dart tests at runtime through PatrolJUnitRunner
(the same mechanism Android Test Orchestrator uses). Marathon's default local
parser reads the bytecode without a device, and your Dart tests are not in there.
remote already reports one entry per Dart test, so there is nothing here that
build-time test discovery needs to
fix.
1. Add Marathonfile
Create Marathonfile.android in your app root:
name: "My app Android"
outputDir: "build/reports/marathon/android"
vendorConfiguration:
type: "Android"
applicationApk: "build/app/outputs/apk/debug/app-debug.apk"
testApplicationApk: "build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk"
testParserConfiguration:
type: "remote"Adjust APK paths if you use flavors or release builds.
2. Build and run
patrol build android
marathon run -m Marathonfile.androidMarathon discovers connected emulators automatically. Reports are written to
outputDir.
iOS (simulator)
Patrol registers XCTest methods at runtime, so set
testParserConfiguration.type to xctest. Marathon's default nm parser reads
the compiled bundle, where none of your Dart tests exist yet, and finds nothing.
On iOS, build-time test discovery
is the better setup for Marathon. It compiles a real XCTest method per Dart test,
so Marathon lists them straight off the bundle with its default nm parser,
without booting a simulator and launching your app to enumerate them, and it's
what per-test sharding across machines needs. The config below keeps the xctest
parser, so it works either way.
Set batchingStrategy to run multiple Patrol tests per xcodebuild invocation
ā Marathon's default is one test per batch, which is significantly slower.
1. Add Marathonfile and Marathondevices
Create Marathonfile.ios:
name: "My app iOS"
outputDir: "build/reports/marathon/ios"
batchingStrategy:
type: "fixed-size"
size: 10
vendorConfiguration:
type: "iOS"
testParserConfiguration:
type: "xctest"
bundle:
application: "build/ios_integ/Build/Products/Debug-iphonesimulator/Runner.app"
testApplication: "build/ios_integ/Build/Products/Debug-iphonesimulator/RunnerUITests-Runner.app/PlugIns/RunnerUITests.xctest"
testType: "xcuitest"Create Marathondevices in the same directory:
workers:
- transport:
type: local
devices:
- type: simulatorProfile
deviceType: com.apple.CoreSimulator.SimDeviceType.iPhone-17
- type: simulatorProfile
deviceType: com.apple.CoreSimulator.SimDeviceType.iPhone-17-ProUse simulatorProfile so Marathon provisions simulators automatically. Pick
device types available on your Mac (xcrun simctl list devicetypes).
To run tests in parallel on one Mac, add multiple simulators to
Marathondevices and set batchingStrategy.size smaller than your test count
so Marathon splits batches across devices.
2. Build and run
patrol build ios --debug --simulator
marathon run -m Marathonfile.iosOr use the all-in-one script from the e2e app:
./run_marathon_iosVerify discovery with:
marathon parse -m Marathonfile.iosThis should list all Patrol tests.
Reference example
The Patrol repository's e2e app includes working configs:
| File | Purpose |
|---|---|
Marathonfile.android | Android Marathon config |
Marathonfile.ios | iOS Marathon config |
Marathondevices | Local iOS simulator pool |
run_marathon_android | Build + run Android |
run_marathon_ios | Build + run iOS |