diff --git a/README.md b/README.md index b14200e..477f77b 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,13 @@ Composer shipped as jar, to run it you need JVM 1.8+: `java -jar composer-latest * Default: `true`. * `False` may be applicable when you run tests conditionally(via annotation/package filters) and empty suite is a valid outcome. * Example: `--fail-if-no-tests false` +* `--with-orchestrator` + * Either `true` or `false` to enable/disable running tests via Android Test Orchestrator. + * Default: `false`. + * When enabled - minimizes shared state and isolates test crashes. + * Requires test orchestrator & test services APKs to be installed on device before executing. + * More info: https://developer.android.com/training/testing/junit-runner#using-android-test-orchestrator + * Example: `--with-orchestrator true` ##### Example @@ -138,7 +145,8 @@ java -jar composer-latest-version.jar \ --output-directory artifacts/composer-output \ --instrumentation-arguments key1 value1 key2 value2 \ --verbose-output false \ ---keep-output-on-exit false +--keep-output-on-exit false \ +--with-orchestrator false ``` ### Download diff --git a/composer/src/main/kotlin/com/gojuno/composer/Args.kt b/composer/src/main/kotlin/com/gojuno/composer/Args.kt index ffe0da2..e852129 100644 --- a/composer/src/main/kotlin/com/gojuno/composer/Args.kt +++ b/composer/src/main/kotlin/com/gojuno/composer/Args.kt @@ -106,7 +106,15 @@ data class Args( description = "Either `true` or `false` to enable/disable error on empty test suite. True by default.", order = 11 ) - var failIfNoTests: Boolean = true + var failIfNoTests: Boolean = true, + + @Parameter( + names = arrayOf("--with-orchestrator"), + required = false, + description = "Either `true` or `false` to enable/disable running tests via Android Test Orchestrator. False by default.", + order = 12 + ) + var runWithOrchestrator: Boolean = false ) // No way to share array both for runtime and annotation without reflection. diff --git a/composer/src/main/kotlin/com/gojuno/composer/Main.kt b/composer/src/main/kotlin/com/gojuno/composer/Main.kt index 296513d..a32963c 100644 --- a/composer/src/main/kotlin/com/gojuno/composer/Main.kt +++ b/composer/src/main/kotlin/com/gojuno/composer/Main.kt @@ -118,21 +118,36 @@ private fun runAllTests(args: Args, testPackage: TestPackage.Valid, testRunner: .subscribeOn(Schedulers.io()) .toList() .flatMap { + val targetInstrumentation: List> + val testPackageName: String + val testRunnerClass: String + + if (args.runWithOrchestrator) { + targetInstrumentation = listOf("targetInstrumentation" to "${testPackage.value}/${testRunner.value}") + testPackageName = "android.support.test.orchestrator" + testRunnerClass = "android.support.test.orchestrator.AndroidTestOrchestrator" + } else { + targetInstrumentation = emptyList() + testPackageName = testPackage.value + testRunnerClass = testRunner.value + } + val instrumentationArguments = buildShardArguments( shardingOn = args.shard, shardIndex = index, devices = connectedAdbDevices.size - ) + args.instrumentationArguments.pairArguments() + ) + args.instrumentationArguments.pairArguments() + targetInstrumentation device .runTests( - testPackageName = testPackage.value, - testRunnerClass = testRunner.value, + testPackageName = testPackageName, + testRunnerClass = testRunnerClass, instrumentationArguments = instrumentationArguments.formatInstrumentationArguments(), outputDir = File(args.outputDirectory), verboseOutput = args.verboseOutput, - keepOutput = args.keepOutputOnExit + keepOutput = args.keepOutputOnExit, + useTestServices = args.runWithOrchestrator ) .flatMap { adbDeviceTestRun -> writeJunit4Report( diff --git a/composer/src/main/kotlin/com/gojuno/composer/TestRun.kt b/composer/src/main/kotlin/com/gojuno/composer/TestRun.kt index 4a845cd..2fde0bb 100644 --- a/composer/src/main/kotlin/com/gojuno/composer/TestRun.kt +++ b/composer/src/main/kotlin/com/gojuno/composer/TestRun.kt @@ -44,18 +44,22 @@ fun AdbDevice.runTests( instrumentationArguments: String, outputDir: File, verboseOutput: Boolean, - keepOutput: Boolean + keepOutput: Boolean, + useTestServices: Boolean ): Single { val adbDevice = this val logsDir = File(File(outputDir, "logs"), adbDevice.id) val instrumentationOutputFile = File(logsDir, "instrumentation.output") + val commandPrefix = if (useTestServices) { + "CLASSPATH=$(pm path android.support.test.services) app_process / android.support.test.services.shellexecutor.ShellMain " + } else "" val runTests = process( commandAndArgs = listOf( adb, "-s", adbDevice.id, - "shell", "am instrument -w -r $instrumentationArguments $testPackageName/$testRunnerClass" + "shell", "${commandPrefix}am instrument -w -r $instrumentationArguments $testPackageName/$testRunnerClass" ), timeout = null, redirectOutputTo = instrumentationOutputFile, diff --git a/composer/src/test/kotlin/com/gojuno/composer/ArgsSpec.kt b/composer/src/test/kotlin/com/gojuno/composer/ArgsSpec.kt index 5c00376..51d4d96 100644 --- a/composer/src/test/kotlin/com/gojuno/composer/ArgsSpec.kt +++ b/composer/src/test/kotlin/com/gojuno/composer/ArgsSpec.kt @@ -30,7 +30,8 @@ class ArgsSpec : Spek({ devices = emptyList(), devicePattern = "", installTimeoutSeconds = 120, - failIfNoTests = true + failIfNoTests = true, + runWithOrchestrator = false )) } } @@ -193,4 +194,15 @@ class ArgsSpec : Spek({ } } } + + context("parse args with --with-orchestrator") { + + val args by memoized { + parseArgs(rawArgsWithOnlyRequiredFields + "--with-orchestrator") + } + + it("parses --with-orchestrator correctly") { + assertThat(args.runWithOrchestrator).isEqualTo(true) + } + } })