feat: enable parallel iOS simulator execution with per-device session tracking#3138
Open
qwertey6 wants to merge 1 commit intomobile-dev-inc:mainfrom
Open
feat: enable parallel iOS simulator execution with per-device session tracking#3138qwertey6 wants to merge 1 commit intomobile-dev-inc:mainfrom
qwertey6 wants to merge 1 commit intomobile-dev-inc:mainfrom
Conversation
iOS simulators share the host's localhost, causing port collisions when
multiple Maestro processes target different sims simultaneously. Session
tracking was per-platform, so two processes on different devices would
interfere with each other's sessions.
Changes:
- Per-device session tracking: SessionStore keys are now
"{platform}_{deviceId}_{sessionId}" instead of "{platform}_{sessionId}"
- Add --driver-host-port CLI flag for explicit XCTest server port
- Auto-select available ports with isPortAvailable() check
- Refactor SessionStore from singleton to injectable class (DI)
- Add shouldCloseSession(platform, deviceId) for per-device shutdown
instead of global activeSessions().isEmpty()
- Add cross-process file locking to KeyValueStore (~/.maestro/sessions)
- Append PID to debug log directory to prevent parallel race
- Enable useJUnitPlatform() in maestro-cli (was missing)
- Add SessionStoreTest with 8 tests covering isolation and lifecycle
Verified: 3 iOS simulators + Android emulator running simultaneously,
all passing. Both --driver-host-port (explicit) and auto-port-selection
work correctly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This was referenced Apr 4, 2026
Author
|
CI note: The E2E failures on Android and iOS are pre-existing and unrelated to this PR:
Both failures reproduce on other unrelated PRs in the repo ( Build, unit tests, web E2E, and iOS XCTest runner tests all pass. |
This was referenced Apr 10, 2026
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Proposed changes
Parallel execution on iOS is completely broken. Two
maestro testprocesses targeting different iOS simulators collide on the XCTest HTTP server port and interfere with each other's sessions. This makes--shard-splitunusable on iOS and forces sequential execution.Impact
--shard-splitand multi-process execution now work on iOSThread.sleep(1000)hack from Fix Android Parallel/Shards Race Condition #1867 — the per-device session check eliminates the race condition it was working aroundRoot cause
Two bugs compound (same analysis as #2339 by @avinash-bharti):
hasActiveSessions()checks if any session exists on the same platform — regardless of device. Process B sees process A's session and skipsdriver.open(), connecting to the wrong device.What this PR does differently from #2339 and #2821
Builds on the same architectural approach (per-device session tracking +
--driver-host-port), with additional correctness fixes discovered during testing:--driver-host-portflaginitialDelay=0fires before session check, causing process to find itself and skipdriver.open(). Broke Android completely.~/.maestro/sessionshad noFileLock, concurrent processes could corrupt session datashouldCloseSession(platform, deviceId)replaces globalactiveSessions().isEmpty()isPortAvailable()with correct check-then-claim orderingChanges
SessionStore.kt— Refactored from singleton to injectable class. Key format:"{platform}_{deviceId}_{sessionId}". New methods:hasActiveSessionForDevice(),shouldCloseSession(),activeSessionsForDevice()MaestroSessionManager.kt— UsesSessionStore.default, per-device heartbeat/delete/closeKeyValueStore.kt— AddedFileLockfor cross-process safetyApp.kt+TestCommand.kt—--driver-host-portCLI optionTestCommand.kt—selectPort()withisPortAvailable()checkSocketUtils.kt—isPortAvailable()functionDebugLogStore.kt— PID in log directory name (Java 8 compatible viaManagementFactory)parent?.driverHostPortinstead ofnullVerification
--driver-host-portexplicit ports--shard-split=2with 2 flowsTesting
SessionStoreTest(per-device isolation, key format, lifecycle, shutdown)useJUnitPlatform()added tomaestro-cli/build.gradle.kts(was missing)Issues fixed
Closes #1853, #2556, #1485, #2082, #2703
Supersedes #2339 and #2821 — credit to @avinash-bharti and @omnarayan for the root cause analysis and architectural direction.