From ec0d03d97639f2d39064d0c9f5eb37f5c1dd46ab Mon Sep 17 00:00:00 2001 From: Aurimas Liutikas Date: Tue, 3 Feb 2026 13:32:51 -0800 Subject: [PATCH] Add a way to pass in orchestrator option when calling FTL Test: usage in androidx.dev --- .../ci/testRunner/FirebaseTestLabController.kt | 7 +++++-- .../dev/androidx/ci/testRunner/TestMatrixStore.kt | 13 +++++++++---- .../dev/androidx/ci/testRunner/TestRunnerService.kt | 4 +++- .../androidx/ci/testRunner/TestRunnerServiceImpl.kt | 7 +++++-- .../dev/androidx/ci/testRunner/TestScheduler.kt | 2 +- .../dev/androidx/ci/testRunner/dto/TestRun.kt | 6 ++++-- .../androidx/ci/testRunner/TestMatrixStoreTest.kt | 4 +++- 7 files changed, 30 insertions(+), 13 deletions(-) diff --git a/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/FirebaseTestLabController.kt b/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/FirebaseTestLabController.kt index f96aa41..5aaf970 100644 --- a/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/FirebaseTestLabController.kt +++ b/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/FirebaseTestLabController.kt @@ -21,6 +21,7 @@ import dev.androidx.ci.firebase.FirebaseTestLabApi import dev.androidx.ci.firebase.dto.EnvironmentType import dev.androidx.ci.generated.ftl.AndroidDevice import dev.androidx.ci.generated.ftl.AndroidDeviceList +import dev.androidx.ci.generated.ftl.AndroidInstrumentationTest.OrchestratorOption import dev.androidx.ci.generated.ftl.ClientInfo import dev.androidx.ci.generated.ftl.EnvironmentMatrix import dev.androidx.ci.generated.ftl.ShardingOption @@ -112,7 +113,8 @@ internal class FirebaseTestLabController( cachedTestMatrixFilter: CachedTestMatrixFilter, testTargets: List? = null, flakyTestAttempts: Int = 2, - testTimeoutSeconds: Int = 2700 + testTimeoutSeconds: Int = 2700, + orchestratorOption: OrchestratorOption? = null, ): List { val devices = (devicePicker ?: defaultDevicePicker).pickDevices() logger.info { @@ -131,7 +133,8 @@ internal class FirebaseTestLabController( cachedTestMatrixFilter = cachedTestMatrixFilter, testTargets = testTargets, flakyTestAttempts = flakyTestAttempts, - testTimeoutSeconds = testTimeoutSeconds + testTimeoutSeconds = testTimeoutSeconds, + orchestratorOption = orchestratorOption, ) } } diff --git a/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/TestMatrixStore.kt b/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/TestMatrixStore.kt index d357e91..5a1ebd3 100644 --- a/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/TestMatrixStore.kt +++ b/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/TestMatrixStore.kt @@ -22,6 +22,7 @@ import dev.androidx.ci.firebase.FirebaseTestLabApi import dev.androidx.ci.firebase.ToolsResultApi import dev.androidx.ci.gcloud.GcsPath import dev.androidx.ci.generated.ftl.AndroidInstrumentationTest +import dev.androidx.ci.generated.ftl.AndroidInstrumentationTest.OrchestratorOption import dev.androidx.ci.generated.ftl.ClientInfo import dev.androidx.ci.generated.ftl.EnvironmentMatrix import dev.androidx.ci.generated.ftl.FileReference @@ -72,7 +73,8 @@ internal class TestMatrixStore( cachedTestMatrixFilter: CachedTestMatrixFilter = { true }, testTargets: List? = null, flakyTestAttempts: Int = 2, - testTimeoutSeconds: Int = 2700 + testTimeoutSeconds: Int = 2700, + orchestratorOption: OrchestratorOption? = null, ): TestMatrix { val testRunId = TestRun.createId( @@ -82,7 +84,8 @@ internal class TestMatrixStore( sharding = sharding, appApk = appApk.apkInfo, testApk = testApk.apkInfo, - deviceSetup = deviceSetup + deviceSetup = deviceSetup, + orchestratorOption = orchestratorOption, ) logger.trace { "test run id: $testRunId" @@ -263,7 +266,8 @@ internal class TestMatrixStore( pullScreenshots: Boolean = false, testTargets: List? = null, flakyTestAttempts: Int = 2, - testTimeoutSeconds: Int = 2700 + testTimeoutSeconds: Int = 2700, + orchestratorOption: OrchestratorOption? = null, ): TestMatrix { val packageName = firebaseTestLabApi.getApkDetails( FileReference( @@ -302,7 +306,8 @@ internal class TestMatrixStore( gcsPath = testApk.gcsPath.path ), shardingOption = sharding, - testTargets = testTargets + testTargets = testTargets, + orchestratorOption = orchestratorOption ), testSetup = testSetup ) diff --git a/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/TestRunnerService.kt b/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/TestRunnerService.kt index 11a9351..82d80b9 100644 --- a/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/TestRunnerService.kt +++ b/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/TestRunnerService.kt @@ -9,6 +9,7 @@ import dev.androidx.ci.firebase.ToolsResultApi import dev.androidx.ci.gcloud.GcsPath import dev.androidx.ci.gcloud.GoogleCloudApi import dev.androidx.ci.generated.ftl.AndroidDevice +import dev.androidx.ci.generated.ftl.AndroidInstrumentationTest.OrchestratorOption import dev.androidx.ci.generated.ftl.ClientInfo import dev.androidx.ci.generated.ftl.ShardingOption import dev.androidx.ci.generated.ftl.TestEnvironmentCatalog @@ -83,7 +84,8 @@ interface TestRunnerService { cachedTestMatrixFilter: CachedTestMatrixFilter = { true }, testTargets: List? = null, flakyTestAttempts: Int = 2, - testTimeoutSeconds: Int = 2700 + testTimeoutSeconds: Int = 2700, + orchestratorOption: OrchestratorOption? = null, ): ScheduleTestsResponse /** diff --git a/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/TestRunnerServiceImpl.kt b/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/TestRunnerServiceImpl.kt index 7ffdf4f..4db2529 100644 --- a/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/TestRunnerServiceImpl.kt +++ b/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/TestRunnerServiceImpl.kt @@ -22,6 +22,7 @@ import dev.androidx.ci.gcloud.BlobVisitor import dev.androidx.ci.gcloud.GcsPath import dev.androidx.ci.gcloud.GoogleCloudApi import dev.androidx.ci.generated.ftl.AndroidDevice +import dev.androidx.ci.generated.ftl.AndroidInstrumentationTest.OrchestratorOption import dev.androidx.ci.generated.ftl.ClientInfo import dev.androidx.ci.generated.ftl.ShardingOption import dev.androidx.ci.generated.ftl.TestEnvironmentCatalog @@ -103,7 +104,8 @@ internal class TestRunnerServiceImpl internal constructor( cachedTestMatrixFilter: CachedTestMatrixFilter, testTargets: List?, flakyTestAttempts: Int, - testTimeoutSeconds: Int + testTimeoutSeconds: Int, + orchestratorOption: OrchestratorOption?, ): TestRunnerService.ScheduleTestsResponse { val testMatrices = testLabController.submitTests( appApk = appApk ?: apkStore.getPlaceholderApk(), @@ -116,7 +118,8 @@ internal class TestRunnerServiceImpl internal constructor( cachedTestMatrixFilter = cachedTestMatrixFilter, testTargets = testTargets, flakyTestAttempts = flakyTestAttempts, - testTimeoutSeconds = testTimeoutSeconds + testTimeoutSeconds = testTimeoutSeconds, + orchestratorOption = orchestratorOption, ) return TestRunnerService.ScheduleTestsResponse.create( testMatrices diff --git a/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/TestScheduler.kt b/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/TestScheduler.kt index a54a24f..e8867d3 100644 --- a/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/TestScheduler.kt +++ b/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/TestScheduler.kt @@ -125,7 +125,7 @@ internal sealed interface TestScheduler { ).filter { it.entry.name.endsWith("AndroidTest.json") }.mapNotNull { zipEntryScope -> - adapter.fromJson(zipEntryScope.bytes.toString(Charsets.UTF_8)).also { testRunConfig -> + adapter.fromJson(zipEntryScope.bytes.toString(Charsets.UTF_8)).also { _ -> "Found AndroidTest config: ${zipEntryScope.entry.name}" } }.filter { testRunConfig -> diff --git a/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/dto/TestRun.kt b/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/dto/TestRun.kt index 41a60d5..7c7bf94 100644 --- a/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/dto/TestRun.kt +++ b/AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/dto/TestRun.kt @@ -23,6 +23,7 @@ import com.google.cloud.datastore.Key import com.squareup.moshi.Moshi import com.squareup.moshi.Types import dev.androidx.ci.datastore.DatastoreApi +import dev.androidx.ci.generated.ftl.AndroidInstrumentationTest.OrchestratorOption import dev.androidx.ci.generated.ftl.ClientInfo import dev.androidx.ci.generated.ftl.EnvironmentMatrix import dev.androidx.ci.generated.ftl.ShardingOption @@ -72,7 +73,8 @@ internal class TestRun( appApk: ApkInfo, testApk: ApkInfo, deviceSetup: DeviceSetup?, - sharding: ShardingOption? + sharding: ShardingOption?, + orchestratorOption: OrchestratorOption?, ): Id { val json = adapter.toJson( mapOf( @@ -88,7 +90,7 @@ internal class TestRun( it.gcsPath.path }, // The order we install additional apks is important, so we do not sort here. "directoriesToPull" to deviceSetup?.directoriesToPull?.sorted() - ) + ) + if (orchestratorOption != null) mapOf("orchestratorOption" to orchestratorOption) else mapOf() ) val sha = sha256(json.toByteArray(Charsets.UTF_8)) return Id(datastoreApi.createKey(datastoreApi.testRunObjectKind, sha)) diff --git a/AndroidXCI/lib/src/test/kotlin/dev/androidx/ci/testRunner/TestMatrixStoreTest.kt b/AndroidXCI/lib/src/test/kotlin/dev/androidx/ci/testRunner/TestMatrixStoreTest.kt index 8c83bee..82a6c4a 100644 --- a/AndroidXCI/lib/src/test/kotlin/dev/androidx/ci/testRunner/TestMatrixStoreTest.kt +++ b/AndroidXCI/lib/src/test/kotlin/dev/androidx/ci/testRunner/TestMatrixStoreTest.kt @@ -23,6 +23,7 @@ import dev.androidx.ci.fake.FakeToolsResultApi import dev.androidx.ci.gcloud.GcsPath import dev.androidx.ci.generated.ftl.AndroidDevice import dev.androidx.ci.generated.ftl.AndroidDeviceList +import dev.androidx.ci.generated.ftl.AndroidInstrumentationTest.OrchestratorOption import dev.androidx.ci.generated.ftl.ClientInfo import dev.androidx.ci.generated.ftl.ClientInfoDetail import dev.androidx.ci.generated.ftl.EnvironmentMatrix @@ -340,7 +341,8 @@ internal class TestMatrixStoreTest { sharding = ShardingOption(), appApk = createFakeApk("app.pak").apkInfo, testApk = createFakeApk("test.apk").apkInfo, - deviceSetup = DeviceSetup() + deviceSetup = DeviceSetup(), + orchestratorOption = OrchestratorOption.DO_NOT_USE_ORCHESTRATOR, ) val resultGcsPath1 = store.createUniqueResultGcsPath(testRunId)