Skip to content

Enable concurrent testing on multiple devices/simulators from same machine#2821

Open
omnarayan wants to merge 2 commits intomobile-dev-inc:mainfrom
omnarayan:feature/driver-host-port
Open

Enable concurrent testing on multiple devices/simulators from same machine#2821
omnarayan wants to merge 2 commits intomobile-dev-inc:mainfrom
omnarayan:feature/driver-host-port

Conversation

@omnarayan
Copy link
Copy Markdown

@omnarayan omnarayan commented Nov 8, 2025

Proposed changes

copilot:summary
Introduces a new global --driver-host-port CLI option that allows users to specify custom driver ports for both Android and iOS, enabling parallel test execution on multiple devices/simulators without port conflicts.

Problem

Currently, running tests concurrently on different devices fails because all Maestro instances attempt to use the default port 7001, resulting in:

  • Connection refused: localhost:7001
  • Command failed (tcp:7001): closed

Users cannot run different tests on different devices simultaneously.

Solution

  • Added --driver-host-port global CLI option to App.kt
  • Modified TestCommand.kt to respect user-specified port in selectPort() method
  • Updated all commands (RecordCommand, StudioCommand, QueryCommand, PrintHierarchyCommand, StartDeviceCommand) to pass custom port to session manager
  • Maintains backward compatibility - defaults to 7001 if not specified
  • Works for both Android devices and iOS simulators

Usage Example

# Terminal 1: Run auth tests on device 1 with port 7005
maestro --driver-host-port 7005 --device device1 test auth-suite.yaml

# Terminal 2: Run product tests on device 2 with port 7006
maestro --driver-host-port 7006 --device device2 test product-suite.yaml
Try it now (before merge)

To use this feature, you have three options:

  1. Wait for PR merge - This PR needs to be reviewed and merged
  2. Build from source - Clone this branch and build Maestro yourself
  3. Use pre-built version - Install the updated JAR directly:

Upgrade:

curl -fsSL https://raw.githubusercontent.com/omnarayan/Maestro/feature/driver-host-port-distribution/upgrade-maestro-ports.sh | sh

This replaces your Maestro JAR with the updated version. Usage:

maestro --driver-host-port 7005 --device device1 test flow.yaml
maestro --driver-host-port 7006 --device device2 test flow.yaml

Restore Original:

bash ~/.maestro-backups/backup/restore.sh

Credit

This implementation builds upon the approach proposed by @avinash-bharti in #2339, implementing the port configuration feature without the additional session management changes.

Testing

  • ✅ Verified concurrent execution on physical Android device (port 7005) + emulator (port 7006) running different test suites simultaneously
  • ✅ Confirmed no session mixing between devices - each test ran on its intended device with correct app
  • ✅ Backward compatibility verified: default port 7001 works without the flag
  • ✅ Tested on both Android devices and iOS simulators
  • ✅ All test steps completed successfully with no port conflicts

Test logs show both devices executing tests in parallel without interference.

Issues fixed

Resolves #1485, #1853, #2082, #2556, #2703
Related to #1818

omnarayan added a commit to omnarayan/Maestro that referenced this pull request Nov 9, 2025
- Add disclosure about DeviceLab.dev team building this feature
- Include context about why custom ports were needed
- Link to upstream PR mobile-dev-inc#2821
- Fix download URL to use feature/driver-host-port-distribution branch
- Show broader use cases beyond DeviceLab
omnarayan added a commit to omnarayan/Maestro that referenced this pull request Nov 9, 2025
…sure

Add user-facing upgrade script that:
- Automatically fetches latest fork-* release from GitHub API
- No hardcoded version - always gets the newest release
- Backs up original JARs with easy restore option
- Includes comprehensive disclosure about DeviceLab.dev sponsorship
- Explains technical context and broader use cases
- Links to upstream PR mobile-dev-inc#2821

Also includes release notes template with same disclosure and context
for distribution transparency.

Features:
- Dynamic release discovery via GitHub API
- Single backup strategy (overwrites previous)
- Auto-generated restore script
- Clear messaging about temporary/stopgap nature
- Version verification instructions
@Fishbowler
Copy link
Copy Markdown
Contributor

I noted that you've said this resolves #2703 - if you manually specify a port that's already in use, how does this behave?

@omnarayan
Copy link
Copy Markdown
Author

I noted that you've said this resolves #2703 - if you manually specify a port that's already in use, how does this behave?

Good catch on the earlier gap! I've just pushed a fix for this.

Now if you specify a port that's already in use:

maestro --driver-host-port 7005 test flow.yaml

You'll get a clear error:

Port 7005 is already in use. Please specify a different port with --driver-host-port

Same for the default port - if 7001 is occupied:

Default port 7001 is already in use. Use --driver-host-port to specify a different port

The port check happens right before the driver starts, so it catches conflicts at the point of use.

We missed this earlier because our internal tooling checks port availability before passing --driver-host-port to Maestro, so we weren't hitting this scenario directly. Apologies for the oversight.

Comment thread maestro-cli/src/main/java/maestro/cli/command/TestCommand.kt
Comment thread maestro-cli/src/main/java/maestro/cli/command/TestCommand.kt
@Fishbowler Fishbowler self-assigned this Jan 9, 2026
…esting

Introduces a new global --driver-host-port option that allows users to specify custom driver ports for both Android and iOS, enabling parallel test execution on multiple devices/simulators without port conflicts. This addresses the limitation where running tests concurrently would fail due to all instances attempting to use the default port.

Changes:
- Added --driver-host-port global option to App.kt
- Modified TestCommand.kt to respect user-specified port in selectPort()
- Updated all commands (RecordCommand, StudioCommand, QueryCommand, PrintHierarchyCommand, StartDeviceCommand) to pass custom port to session manager
- Works for both Android devices and iOS simulators

Usage:
  maestro --driver-host-port 7005 --device <device-id> test flow.yaml

Credit: This implementation builds upon the approach proposed by @avinash-bharti in mobile-dev-inc#2339, implementing the port configuration feature without the additional session management changes.

Resolves mobile-dev-inc#1485, mobile-dev-inc#1853, mobile-dev-inc#2082, mobile-dev-inc#2556, mobile-dev-inc#2703
Related to mobile-dev-inc#1818
Previously, when port 7001 (or a user-specified port) was already in use,
Maestro would fail with obscure connection errors. This made it difficult
for users to understand the root cause.

This change adds port availability checks before attempting to use a port:
- If the default port 7001 is occupied, show a clear error suggesting --driver-host-port
- If a user-specified port is occupied, show a clear error asking to use a different port
- For sharded runs, skip unavailable ports when selecting from the port range

We missed this earlier because our internal tooling checks port availability
before passing --driver-host-port to Maestro - apologies for the oversight.

Fixes mobile-dev-inc#2703
@Fishbowler Fishbowler force-pushed the feature/driver-host-port branch from b3e5872 to aabc7c9 Compare January 9, 2026 12:19
@Fishbowler
Copy link
Copy Markdown
Contributor

Rebased

@Fishbowler
Copy link
Copy Markdown
Contributor

Spotted an e2e failure, but that's unrelated - opened #2920 to solve that

Copy link
Copy Markdown
Contributor

@Fishbowler Fishbowler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a little test of this.

This works:

  1. maestro --platform android test ...
  2. maestro --driver-host-port 7010 --platform ios test ....

This doesn't work:

  1. maestro --driver-host-port 7010 --platform ios test ....
  2. maestro --platform android test ...

Maybe first command locks 7001 as well as the custom port in use?

Inverting the platforms works, so perhaps specific to Android implementation?

@omnarayan
Copy link
Copy Markdown
Author

I had a little test of this.

This works:

  1. maestro --platform android test ...
  2. maestro --driver-host-port 7010 --platform ios test ....

This doesn't work:

  1. maestro --driver-host-port 7010 --platform ios test ....
  2. maestro --platform android test ...

Maybe first command locks 7001 as well as the custom port in use?

Inverting the platforms works, so perhaps specific to Android implementation?

If you need my help, I'm happy to contribute — just need some time since it's not just a code change, it requires proper testing. But if you could take it forward, I'd be more than happy.

Fix location: maestro-cli/src/main/java/maestro/cli/session/MaestroSessionManager.kt:106-111

connectToExistingSession = if (isStudio) {
    false
} else if (driverHostPort != null) {
    // Custom port specified → skip session store check for all devices
    false  // Changed from 'true' to 'false'
} else {
    SessionStore.hasActiveSessions(
        sessionId,
        // ...
    )
}

@varun-jupiter
Copy link
Copy Markdown

Hey @Fishbowler @omnarayan, I am a completely new to Maestro and I too have this use case. Stumbled upon this PR as a fix for it. I see you are just changing port selection in selectPort. I just randomised the port in just the test command and tried running 2 parallel sessions after having done installDist.
./maestro --device d1 test /path/to/test --include-tags t1 --debug-output /path/to/output -e APP_ID=my-app
./maestro --device d2 test /path/to/test --include-tags t1 --debug-output /path/to/output -e APP_ID=my-app
Both are android devices.
However, I see this in the debug log.

12:42:59.316 [ INFO] maestro.cli.runner.TestSuiteInteractor.runFlow:  Running flow flow1
12:42:59.322 [ INFO] maestro.Maestro.cachedDeviceInfo_delegate$lambda$0: Getting device info
12:42:59.416 [ERROR] maestro.drivers.AndroidDriver.runDeviceCall: Not able to reach the gRPC server while doing android device call
12:42:59.416 [ERROR] maestro.cli.runner.TestSuiteInteractor.runFlow: Failed to complete flow
io.grpc.StatusRuntimeException: UNAVAILABLE: io exception
	at io.grpc.stub.ClientCalls.toStatusRuntimeException(ClientCalls.java:275) ~[grpc-stub-1.57.2.jar:1.57.2]
	at io.grpc.stub.ClientCalls.getUnchecked(ClientCalls.java:256) ~[grpc-stub-1.57.2.jar:1.57.2]
	at io.grpc.stub.ClientCalls.blockingUnaryCall(ClientCalls.java:169) ~[grpc-stub-1.57.2.jar:1.57.2]
	at maestro_android.MaestroDriverGrpc$MaestroDriverBlockingStub.deviceInfo(MaestroDriverGrpc.java:740) ~[maestro-client.jar:?]
	at maestro.drivers.AndroidDriver.deviceInfo$lambda$4(AndroidDriver.kt:212) ~[maestro-client.jar:?]
	at maestro.drivers.AndroidDriver.runDeviceCall(AndroidDriver.kt:1263) ~[maestro-client.jar:?]
	at maestro.drivers.AndroidDriver.deviceInfo(AndroidDriver.kt:211) ~[maestro-client.jar:?]
	at maestro.Maestro.cachedDeviceInfo_delegate$lambda$0(Maestro.kt:49) ~[maestro-client.jar:?]
	at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:86) ~[kotlin-stdlib-2.2.20.jar:2.2.20-release-333]
	at maestro.Maestro.getCachedDeviceInfo(Maestro.kt:47) ~[maestro-client.jar:?]
	at maestro.orchestra.Orchestra.initJsEngine(Orchestra.kt:301) ~[maestro-orchestra.jar:?]
	at maestro.orchestra.Orchestra.runFlow(Orchestra.kt:160) ~[maestro-orchestra.jar:?]
	at maestro.cli.runner.TestSuiteInteractor.runFlow(TestSuiteInteractor.kt:243) ~[maestro-cli-2.

Am I missing something here? I have been trying to figure out what exactly is happening here but its taking a while.
Would be really helpful if one of you can point me in the right direction!

My change in selectPort:

    private fun selectPort(effectiveShards: Int): Int =
        if (effectiveShards == 1) {
            val portToUse = (7001..7128).random()
            logger.info("Port picked for usage: $portToUse")
            portToUse
        } else (7001..7128).shuffled().find { port ->
            usedPorts.putIfAbsent(port, true) == null
        } ?: error("No available ports found")

@Jonatthu
Copy link
Copy Markdown

Jonatthu commented Feb 8, 2026

Why no one wants to merge this? or make this continue doing progress? :( chat apps would be amazing to test with this

@varun-jupiter
Copy link
Copy Markdown

Ok so I dug a little more and as per @omnarayan's suggestions, looks like the android driver does not open if there are active sessions detected.
I have made a change to accommodate this and its working for me now.
Tested with 3 concurrent executions across 3 separate devices.
Will try and raise the change soon.

@Fishbowler
Copy link
Copy Markdown
Contributor

Thanks!

@pamartineza
Copy link
Copy Markdown

I would like to add another usecase where this feature is very valuable:

We have a dedicated powerful Mac Studio Machine to run up to 6 jobs concurrently of a self-hosted Giltab CI runner (shell executor mode).

If two or more different branches are pushed and maestro tests are run, despite starting 2 or more different android emulators with different device ids, the maestro tests fail due to port collision.

Right now we have to run different jobs that execute maestro test in series rather than in parallel which is not an optimal experience.

@d2callpod
Copy link
Copy Markdown

Is there a reason why this hasn’t been merged? This is a great feature/bug. We have a lot of cross device features that we would like to test.

@Fishbowler
Copy link
Copy Markdown
Contributor

We're not ignoring it. We're considering it alongside other changes. We're also working on Studio improvements, and we've been at a bunch of MCP changes recently.

How do you think this compares to #3138 ?

@d2callpod
Copy link
Copy Markdown

We're not ignoring it. We're considering it alongside other changes. We're also working on Studio improvements, and we've been at a bunch of MCP changes recently.

How do you think this compares to #3138 ?

I’ve done some testing on Android devices and it works well and is a lot simpler to use. There is no need for any configuration for #3138, so that is definitely my preference.

@orennu
Copy link
Copy Markdown

orennu commented May 10, 2026

when is this expected to be on offiical release?
our usecase is running maestro as ui test step from pytest since our e2e flow includes device management via adb/ios equivilant which is not supported by maestro.
we would like to run tests in parallel from the same machine for runtime optimization.

@Fishbowler
Copy link
Copy Markdown
Contributor

As above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] Run maestro on multiple devices at the same time on a single computer

7 participants