From 3cdd535334952c5da222a529e086ce1e6c3b037f Mon Sep 17 00:00:00 2001 From: Marcus Bermel Date: Tue, 5 Jul 2022 08:20:19 -0600 Subject: [PATCH 1/5] Put mostly unhelpful print behind verbose flag --- .../src/main/kotlin/com/workday/torque/ModuleTestParser.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/torque-core/src/main/kotlin/com/workday/torque/ModuleTestParser.kt b/torque-core/src/main/kotlin/com/workday/torque/ModuleTestParser.kt index eee3b2d..5ea7142 100644 --- a/torque-core/src/main/kotlin/com/workday/torque/ModuleTestParser.kt +++ b/torque-core/src/main/kotlin/com/workday/torque/ModuleTestParser.kt @@ -9,14 +9,16 @@ import com.workday.torque.pooling.TestModuleInfo import java.io.File import java.util.concurrent.TimeUnit -class ModuleTestParser(private val args: Args, private val apkTestParser: ApkTestParser = ApkTestParser()) { +class ModuleTestParser(private val args: Args, private val apkTestParser: ApkTestParser = ApkTestParser(args.verboseOutput)) { fun parseTestsFromModuleApks(): List { return args.testApkPaths.fold(mutableListOf()) { accumulatedModules, testApkPath -> accumulatedModules.apply { val testMethods = apkTestParser.getTests(testApkPath) .filterAnnotations(includedAnnotations = args.includedAnnotations, excludedAnnotations = args.excludedAnnotations) .filterClassRegexes(args.testClassRegexes) - println("Filtered tests count: ${testMethods.size}") + if (args.verboseOutput) { + log("Filtered tests count: ${testMethods.size}") + } val testApkFile = File(testApkPath) val time = System.currentTimeMillis() From d4a051cf9e483bd79d7ef93423e96ff16c5cb70f Mon Sep 17 00:00:00 2001 From: Marcus Bermel Date: Tue, 5 Jul 2022 08:20:53 -0600 Subject: [PATCH 2/5] Buffering --- .../kotlin/com/workday/torque/AdbCommander.kt | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 torque-core/src/main/kotlin/com/workday/torque/AdbCommander.kt diff --git a/torque-core/src/main/kotlin/com/workday/torque/AdbCommander.kt b/torque-core/src/main/kotlin/com/workday/torque/AdbCommander.kt new file mode 100644 index 0000000..f615bbf --- /dev/null +++ b/torque-core/src/main/kotlin/com/workday/torque/AdbCommander.kt @@ -0,0 +1,57 @@ +package com.workday.torque + +import com.gojuno.commander.android.AdbDevice +import com.gojuno.commander.android.adb +import com.gojuno.commander.android.deviceModel +import com.gojuno.commander.os.Notification +import com.gojuno.commander.os.log +import com.gojuno.commander.os.process +import rx.Observable + +fun connectedAdbDevices(unbufferedOutput: Boolean = true): Observable> { + return process( + commandAndArgs = listOf(adb, "devices"), + unbufferedOutput = unbufferedOutput + ) + .ofType(Notification.Exit::class.java) + .map { it.output.readText() } + .map { + when (it.contains("List of devices attached")) { + true -> it + false -> throw IllegalStateException("Adb output is not correct: $it.") + } + } + .retry { retryCount, exception -> + val shouldRetry = retryCount < 5 && exception is IllegalStateException + if (shouldRetry) { + log("runningEmulators: retrying $exception.") + } + + shouldRetry + } + .flatMapIterable { + it + .substringAfter("List of devices attached") + .split(System.lineSeparator()) + .map { it.trim() } + .filter { it.isNotEmpty() } + .filter { it.contains("online") || it.contains("device") } + } + .map { line -> + val serial = line.substringBefore("\t") + val online = when { + line.contains("offline", ignoreCase = true) -> false + line.contains("device", ignoreCase = true) -> true + else -> throw IllegalStateException("Unknown adb output for device: $line") + } + AdbDevice(id = serial, online = online) + } + .flatMapSingle { device -> + deviceModel(device).map { model -> + device.copy(model = model) + } + } + .toList() + .map { it.toSet() } + .doOnError { log("Error during getting connectedAdbDevices, error = $it") } +} \ No newline at end of file From 9458c69f2017eb652d157e9c37f8b3d335d4f24e Mon Sep 17 00:00:00 2001 From: Marcus Bermel Date: Tue, 5 Jul 2022 08:28:34 -0600 Subject: [PATCH 3/5] Torque Versioning --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 5197c93..b2f8d60 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=1.1.2 +VERSION_NAME=1.1.4 GROUP=com.workday POM_DESCRIPTION=A Reactive Android instrumentation test orchestrator with multi-library-modules-testing and test pooling/grouping support. POM_URL=https://github.com/Workday/torque From 1a99fb40cd08a6ccdc33d719b158d2990768c1c5 Mon Sep 17 00:00:00 2001 From: Marcus Bermel Date: Tue, 5 Jul 2022 11:24:46 -0600 Subject: [PATCH 4/5] Tweak output buffering. Reformat some files. --- .../com/workday/torque/AdbDeviceFinder.kt | 3 +- .../com/workday/torque/ApkTestParser.kt | 203 ++++++++++-------- .../kotlin/com/workday/torque/Installer.kt | 23 +- .../kotlin/com/workday/torque/LogcatFileIO.kt | 47 ++-- 4 files changed, 157 insertions(+), 119 deletions(-) diff --git a/torque-core/src/main/kotlin/com/workday/torque/AdbDeviceFinder.kt b/torque-core/src/main/kotlin/com/workday/torque/AdbDeviceFinder.kt index 0c2f304..58fb0c7 100755 --- a/torque-core/src/main/kotlin/com/workday/torque/AdbDeviceFinder.kt +++ b/torque-core/src/main/kotlin/com/workday/torque/AdbDeviceFinder.kt @@ -1,13 +1,12 @@ package com.workday.torque -import com.gojuno.commander.android.connectedAdbDevices import com.gojuno.commander.os.log import io.reactivex.Single class AdbDeviceFinder { fun onlineAdbDevices(): Single> { - return connectedAdbDevices() + return connectedAdbDevices(unbufferedOutput = false) .toSingle() .toV2Single() .map { diff --git a/torque-core/src/main/kotlin/com/workday/torque/ApkTestParser.kt b/torque-core/src/main/kotlin/com/workday/torque/ApkTestParser.kt index 3e444c7..46992f5 100755 --- a/torque-core/src/main/kotlin/com/workday/torque/ApkTestParser.kt +++ b/torque-core/src/main/kotlin/com/workday/torque/ApkTestParser.kt @@ -2,13 +2,14 @@ package com.workday.torque import com.gojuno.commander.android.androidHome import com.gojuno.commander.os.Notification +import com.gojuno.commander.os.log import com.gojuno.commander.os.process import com.linkedin.dex.parser.DexParser import com.linkedin.dex.parser.TestMethod import java.io.File -import java.util.* +import java.util.concurrent.TimeUnit -class ApkTestParser { +class ApkTestParser(private val verboseOutput: Boolean = false) { fun getValidatedTestPackage(testApkPath: String): ApkPackage.Valid { return parseTestPackage(testApkPath).validateApkPackage() } @@ -20,57 +21,50 @@ class ApkTestParser { .lastOrNull() ?.absolutePath } - val aapt: String by lazy { buildTools?.let { "$buildTools/aapt2" } ?: "" } + private val aapt: String by lazy { buildTools?.let { "$buildTools/aapt2" } ?: "" } private fun parseTestPackage(testApkPath: String): ApkPackage { - val commandAndArgs = listOf( - aapt, "dump", "badging", testApkPath - ) return process( - commandAndArgs = commandAndArgs, - unbufferedOutput = true, + print = verboseOutput, + commandAndArgs = listOf( + aapt, "dump", "badging", testApkPath + ), + unbufferedOutput = false, // Note: flipping this flag may cause strange errors, due to OS-specific buffer flushing ) - .ofType(Notification.Exit::class.java) - .map { (output) -> - val unTouched = output - .readText() - val packageText = unTouched - .split(System.lineSeparator()) - .firstOrNull { it.contains("package") } - - if (packageText.isNullOrEmpty()) { - return@map ApkPackage.ParseError("'package' token was null") + .ofType(Notification.Exit::class.java) + .map { (output) -> + val rawOutput = output + .readText() + val packageText = rawOutput + .split(System.lineSeparator()) + .firstOrNull { it.contains("package") } + + if (packageText.isNullOrEmpty()) { + if (verboseOutput) { + log("parseTestPackage, raw output: $rawOutput") } - val splitPackageText = packageText - ?.split(" ") - val name = splitPackageText - ?.firstOrNull { it.startsWith("name=") } + return@map ApkPackage.ParseError("parseTestPackage, 'package' token was null") + } + val name = packageText + .split(" ") + .firstOrNull { it.startsWith("name=") } - if (name.isNullOrEmpty()) { - return@map ApkPackage.ParseError("'name' token was null") + if (name.isNullOrEmpty()) { + if (verboseOutput) { + log("parseTestPackage, name token was null. package token: $packageText") } - - name - ?.split("'") - ?.getOrNull(1) - ?.let(ApkPackage::Valid) - ?: ApkPackage.ParseError("Cannot parse test package from `aapt dump badging \$APK` output.") + return@map ApkPackage.ParseError("'name' token was null") } - .toSingle() - .toBlocking() - .value() - } - private fun makeOutputFile(): File { - return Random() - .nextInt() - .let { System.nanoTime() + it } - .let { name -> - File("$name.output").apply { - createNewFile() - deleteOnExit() - } + name + .split("'") + .getOrNull(1) + ?.let(ApkPackage::Valid) + ?: ApkPackage.ParseError("Cannot parse test package from `aapt2 dump badging \$APK` output.") } + .toSingle() + .toBlocking() + .value() } fun getValidatedTargetPackage(testApkPath: String): ApkPackage.Valid { @@ -79,34 +73,41 @@ class ApkTestParser { private fun parseTargetPackage(testApkPath: String): ApkPackage { return process( - commandAndArgs = listOf( - aapt, "dump", "xmltree", testApkPath, "--file", "AndroidManifest.xml" - ), - unbufferedOutput = true, - keepOutputOnExit = true, + print = verboseOutput, + commandAndArgs = listOf( + aapt, "dump", "xmltree", testApkPath, "--file", "AndroidManifest.xml" + ), + unbufferedOutput = false, // Note: flipping this flag may cause strange errors, due to OS-specific buffer flushing + keepOutputOnExit = false, ) - .ofType(Notification.Exit::class.java) - .map { (output) -> - val initialOutput = output - .readText() - .split(System.lineSeparator()) - .firstOrNull { it.contains("android:targetPackage") } - - val secondaryOutput = initialOutput - ?.split(" ") - val finalOutput = secondaryOutput - ?.firstOrNull { - it.contains("android:targetPackage") - } - ?.substringAfter("=") - ?.trim('"') - finalOutput - ?.let(ApkPackage::Valid) - ?: ApkPackage.ParseError("Cannot parse target package from `aapt dump xmltree ${testApkPath} AndroidManifest.xml` output.") + .ofType(Notification.Exit::class.java) + .map { (output) -> + val initialOutput = output + .readText() + .split(System.lineSeparator()) + .firstOrNull { it.contains("android:targetPackage") } + + if (verboseOutput) { + log("parseTargetPackage, output file path: ${output.absolutePath}") + log("parseTargetPackage, initialOutput: $initialOutput") } - .toSingle() - .toBlocking() - .value() + + val finalOutput = initialOutput + ?.split(" ") + ?.firstOrNull { it.contains("android:targetPackage") } + ?.substringAfter("=") + ?.trim('"') + + if (verboseOutput) { + log("parseTargetPackage finalOutput: $finalOutput") + } + finalOutput + ?.let(ApkPackage::Valid) + ?: ApkPackage.ParseError("Cannot parse target package from `aapt dump xmltree ${testApkPath} --file AndroidManifest.xml` output.") + } + .toSingle() + .toBlocking() + .value() } private fun ApkPackage.validateApkPackage(): ApkPackage.Valid { @@ -125,28 +126,48 @@ class ApkTestParser { } private fun parseTestRunner(testApkPath: String): TestRunner = - process( - commandAndArgs = listOf( - aapt, "dump", "xmltree", testApkPath, "--file", "AndroidManifest.xml" - ), - unbufferedOutput = true - ) - .ofType(Notification.Exit::class.java) - .map { (output) -> - output - .readText() - .split(System.lineSeparator()) - .dropWhile { !it.contains("instrumentation") } - .firstOrNull { it.contains("android:name") } - ?.split("\"") - ?.getOrNull(1) - ?.let(TestRunner::Valid) - ?: TestRunner.ParseError("Cannot parse test runner from `aapt dump xmltree ${testApkPath} AndroidManifest.xml` output.") - } - .toSingle() - .toBlocking() - .value() + process( + print = verboseOutput, + commandAndArgs = listOf( + aapt, "dump", "xmltree", testApkPath, "--file", "AndroidManifest.xml" + ), + unbufferedOutput = false, // Note: flipping this flag may cause strange errors, due to OS-specific buffer flushing + keepOutputOnExit = false, + ) + .ofType(Notification.Exit::class.java) + .map { + val expiration = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(10) + Pair(it, expiration) + } + .map { it.first } + .map { (output) -> + val rawOutput = output + .readText() + val dropWhile = rawOutput + .split(System.lineSeparator()) + .dropWhile { !it.contains("instrumentation") } + val firstOrNull = dropWhile + .firstOrNull { it.contains("android:name") } + + if (firstOrNull.isNullOrEmpty() && verboseOutput) { + log("parseTestRunner, raw output: $rawOutput") + } + val testRunnerText = firstOrNull + ?.split("\"") + ?.getOrNull(1) + + if (verboseOutput) { + log("parseTestRunner, identified test runner: $testRunnerText") + } + + testRunnerText + ?.let(TestRunner::Valid) + ?: TestRunner.ParseError("Cannot parse test runner from `aapt dump xmltree $testApkPath --file AndroidManifest.xml` output.") + } + .toSingle() + .toBlocking() + .value() private fun TestRunner.validateTestRunner(): TestRunner.Valid { @@ -163,6 +184,6 @@ class ApkTestParser { fun getTests(testApkPath: String) = parseTests(testApkPath) private fun parseTests(testApkPath: String): List = - DexParser.findTestMethods(testApkPath).map { TestMethod(it.testName, it.annotations) } + DexParser.findTestMethods(testApkPath).map { TestMethod(it.testName, it.annotations) } } diff --git a/torque-core/src/main/kotlin/com/workday/torque/Installer.kt b/torque-core/src/main/kotlin/com/workday/torque/Installer.kt index 7e82169..cd38909 100755 --- a/torque-core/src/main/kotlin/com/workday/torque/Installer.kt +++ b/torque-core/src/main/kotlin/com/workday/torque/Installer.kt @@ -1,6 +1,7 @@ package com.workday.torque import com.gojuno.commander.os.Notification +import com.gojuno.commander.os.log import com.workday.torque.pooling.ModuleInfo import com.workday.torque.pooling.TestChunk import com.workday.torque.pooling.TestModuleInfo @@ -9,7 +10,11 @@ import io.reactivex.Observable import kotlinx.coroutines.rx2.await import java.util.concurrent.TimeUnit -class Installer(private val adbDevice: AdbDevice, private val processRunner: ProcessRunner = ProcessRunner()) { +class Installer( + private val adbDevice: AdbDevice, + private val processRunner: ProcessRunner = ProcessRunner(), + private val verboseOutput: Boolean = false, +) { suspend fun ensureTestPackageInstalled(args: Args, testChunk: TestChunk) { if (isChunkApkInstalled(testChunk)) { @@ -81,7 +86,7 @@ class Installer(private val adbDevice: AdbDevice, private val processRunner: Pro return processRunner.runAdb( commandAndArgs = listOf("-s", adbDevice.id, "uninstall", modulePackage), timeout = installTimeout, - unbufferedOutput = true) + unbufferedOutput = false) .ofType(Notification.Exit::class.java) } @@ -125,14 +130,20 @@ class Installer(private val adbDevice: AdbDevice, private val processRunner: Pro return processRunner.runAdb( commandAndArgs = listOf("-s", adbDevice.id, "install", "-r", "-g", pathToApk), timeout = installTimeout, - unbufferedOutput = true, - keepOutputOnExit = true) - .ofType(Notification.Exit::class.java) + unbufferedOutput = false, // Note: flipping this flag may cause strange errors, due to OS-specific buffer flushing + keepOutputOnExit = true, + ).ofType(Notification.Exit::class.java) } private fun Notification.Exit.preprocessOutput(): List { - return output + val rawOutput = output .readText() + + if (verboseOutput) { + log("preprocessOutput, raw/untouched output: $rawOutput") + } + + return rawOutput .split(System.lineSeparator()) .map { it.trim() } } diff --git a/torque-core/src/main/kotlin/com/workday/torque/LogcatFileIO.kt b/torque-core/src/main/kotlin/com/workday/torque/LogcatFileIO.kt index f857d6c..ab97ef5 100755 --- a/torque-core/src/main/kotlin/com/workday/torque/LogcatFileIO.kt +++ b/torque-core/src/main/kotlin/com/workday/torque/LogcatFileIO.kt @@ -10,10 +10,11 @@ import java.util.concurrent.TimeUnit private const val FULL_LOGCAT_FILE_NAME = "full.logcat" class LogcatFileIO( - private val adbDevice: AdbDevice, - private val timeoutSeconds: Int, - outputDirPath: String, - private val processRunner: ProcessRunner = ProcessRunner() + private val adbDevice: AdbDevice, + private val timeoutSeconds: Int, + outputDirPath: String, + private val processRunner: ProcessRunner = ProcessRunner(), + private val verboseOutput: Boolean = false, ) { private val logsDir = File(File(outputDirPath, "logs"), adbDevice.id) @@ -24,25 +25,31 @@ class LogcatFileIO( internal suspend fun redirectLogcatToFile() { fullLogcatFile.parentFile.mkdirs() clearLogcat() - processRunner.runAdb(commandAndArgs = listOf("-s", adbDevice.id, "logcat"), - timeout = Timeout(timeoutSeconds, TimeUnit.SECONDS), - redirectOutputTo = fullLogcatFile) - .ofType(Notification.Start::class.java) - .doOnError { - when (it) { - is InterruptedException -> Unit // Expected case, interrupt comes from System.exit(0). - else -> adbDevice.log("Error during redirecting logcat to file $fullLogcatFile, error = $it") - } + processRunner.runAdb( + commandAndArgs = listOf("-s", adbDevice.id, "logcat"), + timeout = Timeout(timeoutSeconds, TimeUnit.SECONDS), + redirectOutputTo = fullLogcatFile, + keepOutputOnExit = true + ) + .ofType(Notification.Start::class.java) + .doOnError { + when (it) { + is InterruptedException -> Unit // Expected case, interrupt comes from System.exit(0). + else -> adbDevice.log("Error during redirecting logcat to file $fullLogcatFile, error = $it") } - .awaitFirst() + } + .awaitFirst() } private suspend fun clearLogcat() { try { - processRunner.runAdb(commandAndArgs = listOf("-s", adbDevice.id, "logcat", "-c"), - timeout = Timeout(timeoutSeconds, TimeUnit.SECONDS)) - .ofType(Notification.Exit::class.java) - .awaitFirst() + processRunner.runAdb( + commandAndArgs = listOf("-s", adbDevice.id, "logcat", "-c"), + timeout = Timeout(timeoutSeconds, TimeUnit.SECONDS), + print = verboseOutput, + ) + .ofType(Notification.Exit::class.java) + .awaitFirst() } catch (e: Exception) { adbDevice.log("Could not clear logcat on device. $e") } @@ -50,8 +57,8 @@ class LogcatFileIO( fun writeLogcatFileForTest(testLogcat: TestLogcat) { getLogcatFileForTest(testLogcat.testDetails) - .apply { parentFile.mkdirs() } - .writeText(testLogcat.logcat) + .apply { parentFile.mkdirs() } + .writeText(testLogcat.logcat) } fun getLogcatFileForTest(testDetails: TestDetails): File { From bc5c99ed20486a6ab4ee7aa6c702081030b24a5d Mon Sep 17 00:00:00 2001 From: Marcus Bermel Date: Tue, 5 Jul 2022 11:26:20 -0600 Subject: [PATCH 5/5] Feed in the project path, since AGP 7 seems to have changed the working directory/running directory context when running the plugin. --- .../main/kotlin/com/workday/torque/ResultWriter.kt | 13 ++++++++----- .../kotlin/com/workday/torque/TestRunFactory.kt | 7 ++++--- .../src/main/kotlin/com/workday/torque/Torque.kt | 13 ++++++++----- .../kotlin/com/workday/torque/TestRunFactorySpec.kt | 8 +++++--- .../test/kotlin/com/workday/torque/TorqueSpec.kt | 11 +++++++---- .../com/workday/torque/gradle/TorquePlugin.kt | 7 +++++-- .../src/main/kotlin/com/workday/torque/Main.kt | 6 ++++-- 7 files changed, 41 insertions(+), 24 deletions(-) diff --git a/torque-core/src/main/kotlin/com/workday/torque/ResultWriter.kt b/torque-core/src/main/kotlin/com/workday/torque/ResultWriter.kt index 94f2e42..1b75316 100755 --- a/torque-core/src/main/kotlin/com/workday/torque/ResultWriter.kt +++ b/torque-core/src/main/kotlin/com/workday/torque/ResultWriter.kt @@ -6,18 +6,21 @@ import com.workday.torque.html.writeHtmlReport import java.io.File import java.util.Date -class ResultWriter(val args: Args) { +class ResultWriter(args: Args, workingDirectory: String) { + + private val outputDirectory = "$workingDirectory/${args.outputDirectory}" + private val resultFilePath = "$workingDirectory/${args.resultFilePath}" fun clearOutputDirectory() { - File(args.outputDirectory).deleteRecursively() + File(outputDirectory).deleteRecursively() } fun write(startTime: Long, adbDeviceTestSessions: List) { - adbDeviceTestSessions.writeCiResultToOutputFile(args.resultFilePath) + adbDeviceTestSessions.writeCiResultToOutputFile(resultFilePath) val suites = adbDeviceTestSessions.toSuites() suites.run { - generateHtmlReport(args.outputDirectory) - generateJunit4Report(args.outputDirectory) + generateHtmlReport(outputDirectory) + generateJunit4Report(outputDirectory) printFinalResult(startTime) } } diff --git a/torque-core/src/main/kotlin/com/workday/torque/TestRunFactory.kt b/torque-core/src/main/kotlin/com/workday/torque/TestRunFactory.kt index a2d7abe..369363d 100644 --- a/torque-core/src/main/kotlin/com/workday/torque/TestRunFactory.kt +++ b/torque-core/src/main/kotlin/com/workday/torque/TestRunFactory.kt @@ -9,7 +9,7 @@ import kotlinx.coroutines.async import kotlinx.coroutines.rx2.asSingle import kotlinx.coroutines.rx2.await -class TestRunFactory { +class TestRunFactory(private val workingDirectory: String) { fun runTestSession( adbDevice: AdbDevice, @@ -18,10 +18,11 @@ class TestRunFactory { logcatFileIO: LogcatFileIO = LogcatFileIO( adbDevice = adbDevice, timeoutSeconds = args.chunkTimeoutSeconds.toInt(), - outputDirPath = args.outputDirectory + outputDirPath = "$workingDirectory/${args.outputDirectory}", + verboseOutput = true, ), logcatRecorder: LogcatRecorder = LogcatRecorder(adbDevice, logcatFileIO), - installer: Installer = Installer(adbDevice), + installer: Installer = Installer(adbDevice = adbDevice, verboseOutput = args.verboseOutput), filePuller: FilePuller = FilePuller(adbDevice), testChunkRunner: TestChunkRunner = TestChunkRunner(args, adbDevice, logcatFileIO, installer), testChunkRetryer: TestChunkRetryer = TestChunkRetryer(adbDevice, args, logcatFileIO, testChunkRunner, installer) diff --git a/torque-core/src/main/kotlin/com/workday/torque/Torque.kt b/torque-core/src/main/kotlin/com/workday/torque/Torque.kt index 41fc88a..da72b80 100755 --- a/torque-core/src/main/kotlin/com/workday/torque/Torque.kt +++ b/torque-core/src/main/kotlin/com/workday/torque/Torque.kt @@ -6,11 +6,14 @@ import io.reactivex.Single import java.util.concurrent.TimeUnit import java.util.concurrent.TimeoutException -class Torque(private val args: Args, - private val moduleTestParser: ModuleTestParser = ModuleTestParser(args), - private val adbDeviceFinder: AdbDeviceFinder = AdbDeviceFinder(), - private val testRunFactory: TestRunFactory = TestRunFactory(), - private val resultWriter: ResultWriter = ResultWriter(args)) { +class Torque( + private val args: Args, + private val workingDirectory: String, + private val moduleTestParser: ModuleTestParser = ModuleTestParser(args), + private val adbDeviceFinder: AdbDeviceFinder = AdbDeviceFinder(), + private val testRunFactory: TestRunFactory = TestRunFactory(workingDirectory), + private val resultWriter: ResultWriter = ResultWriter(args, workingDirectory), +) { fun run() { val startTime = System.currentTimeMillis() diff --git a/torque-core/src/test/kotlin/com/workday/torque/TestRunFactorySpec.kt b/torque-core/src/test/kotlin/com/workday/torque/TestRunFactorySpec.kt index 7fffa41..d252aeb 100755 --- a/torque-core/src/test/kotlin/com/workday/torque/TestRunFactorySpec.kt +++ b/torque-core/src/test/kotlin/com/workday/torque/TestRunFactorySpec.kt @@ -15,9 +15,11 @@ import org.jetbrains.spek.api.Spek import org.jetbrains.spek.api.dsl.context import org.jetbrains.spek.api.dsl.given import org.jetbrains.spek.api.dsl.it +import java.nio.file.Paths class TestRunFactorySpec : Spek( { + val workingDirectory = Paths.get("./").toAbsolutePath().toString() context("Run test chunk") { val adbDevice = AdbDevice("id", "model", online = true) val logcatFileIO = mockk(relaxed = true) @@ -52,7 +54,7 @@ class TestRunFactorySpec : Spek( val args = Args().apply { appApkPath = "somePath" } - TestRunFactory().runTestSession(adbDevice, args, testPool, logcatFileIO, logcatRecorder, installer, filePuller, testChunkRunner, chunkRetryer) + TestRunFactory(workingDirectory).runTestSession(adbDevice, args, testPool, logcatFileIO, logcatRecorder, installer, filePuller, testChunkRunner, chunkRetryer) .test() .await() @@ -80,7 +82,7 @@ class TestRunFactorySpec : Spek( testFilesPullDeviceDirectory = "somePath" testFilesPullHostDirectory = "somePath" } - TestRunFactory().runTestSession(adbDevice, args, testPool, logcatFileIO, logcatRecorder, installer, filePuller, testChunkRunner, chunkRetryer) + TestRunFactory(workingDirectory).runTestSession(adbDevice, args, testPool, logcatFileIO, logcatRecorder, installer, filePuller, testChunkRunner, chunkRetryer) .test() .await() @@ -96,7 +98,7 @@ class TestRunFactorySpec : Spek( appApkPath = "somePath" } - TestRunFactory().runTestSession(adbDevice, args, testPool, logcatFileIO, logcatRecorder, installer, filePuller, testChunkRunner, chunkRetryer) + TestRunFactory(workingDirectory).runTestSession(adbDevice, args, testPool, logcatFileIO, logcatRecorder, installer, filePuller, testChunkRunner, chunkRetryer) .test() .await() diff --git a/torque-core/src/test/kotlin/com/workday/torque/TorqueSpec.kt b/torque-core/src/test/kotlin/com/workday/torque/TorqueSpec.kt index f9942e5..7666b4d 100755 --- a/torque-core/src/test/kotlin/com/workday/torque/TorqueSpec.kt +++ b/torque-core/src/test/kotlin/com/workday/torque/TorqueSpec.kt @@ -8,10 +8,13 @@ import org.jetbrains.spek.api.Spek import org.jetbrains.spek.api.dsl.context import org.jetbrains.spek.api.dsl.given import org.jetbrains.spek.api.dsl.it +import java.nio.file.Paths import kotlin.test.assertFailsWith class TorqueSpec : Spek ( { + val currentDir = Paths.get("./").toAbsolutePath().toString() + context("Run Torque") { val args = Args() val moduleTestParser = mockk { @@ -31,7 +34,7 @@ class TorqueSpec : Spek ( val resultWriter = mockk(relaxed = true) given("completed test sessions") { - val torque = Torque(args, moduleTestParser, adbDeviceFinder, testRunFactory, resultWriter) + val torque = Torque(args, currentDir, moduleTestParser, adbDeviceFinder, testRunFactory, resultWriter,) it("Parses tests from apks and starts TestRuns on connectedDevices and writes the results") { torque.run() @@ -50,7 +53,7 @@ class TorqueSpec : Spek ( val failedModuleTestParser = mockk { every { parseTestsFromModuleApks() } throws IllegalStateException("apk parse error") } - val torque = Torque(args, failedModuleTestParser, adbDeviceFinder, testRunFactory, resultWriter) + val torque = Torque(args, currentDir, failedModuleTestParser, adbDeviceFinder, testRunFactory, resultWriter,) it("passes that exception to the caller") { assertFailsWith(IllegalStateException::class, "apk parse error") { torque.run() @@ -62,7 +65,7 @@ class TorqueSpec : Spek ( val failedAdbDeviceFinder = mockk { every { onlineAdbDevices() } throws IllegalStateException("Error: No devices available for tests.") } - val torque = Torque(args, moduleTestParser, failedAdbDeviceFinder, testRunFactory, resultWriter) + val torque = Torque(args, currentDir, moduleTestParser, failedAdbDeviceFinder, testRunFactory, resultWriter,) it("passes that exception to the caller") { assertFailsWith(IllegalStateException::class, "Error: No devices available for tests.") { torque.run() @@ -74,7 +77,7 @@ class TorqueSpec : Spek ( val failedResultWriter = mockk(relaxed = true) { every { write(any(), any()) } throws IllegalStateException("Error: 0 tests were run.") } - val torque = Torque(args, moduleTestParser, adbDeviceFinder, testRunFactory, failedResultWriter) + val torque = Torque(args, currentDir, moduleTestParser, adbDeviceFinder, testRunFactory, failedResultWriter,) it("passes that exception to the caller") { assertFailsWith(IllegalStateException::class, "Error: 0 tests were run.") { torque.run() diff --git a/torque-gradle-plugin/src/main/kotlin/com/workday/torque/gradle/TorquePlugin.kt b/torque-gradle-plugin/src/main/kotlin/com/workday/torque/gradle/TorquePlugin.kt index 59c580d..dbddbff 100755 --- a/torque-gradle-plugin/src/main/kotlin/com/workday/torque/gradle/TorquePlugin.kt +++ b/torque-gradle-plugin/src/main/kotlin/com/workday/torque/gradle/TorquePlugin.kt @@ -87,8 +87,11 @@ class TorquePlugin : Plugin { private fun configureTaskOptionsAndRun(torqueArgs: Args, task: TorqueRunTask) { torqueArgs.configureTaskOptions(task) - println("Starting Torque run with args: $torqueArgs") - Torque(torqueArgs).run() + if (torqueArgs.verboseOutput) { + println("Starting Torque run with args: $torqueArgs") + } + val rootDir = task.project.rootDir.toString() + Torque(args = torqueArgs, workingDirectory = rootDir).run() } private fun Args.configureTaskOptions(task: TorqueRunTask) { diff --git a/torque-runner/src/main/kotlin/com/workday/torque/Main.kt b/torque-runner/src/main/kotlin/com/workday/torque/Main.kt index 7a125bc..42f7fd9 100755 --- a/torque-runner/src/main/kotlin/com/workday/torque/Main.kt +++ b/torque-runner/src/main/kotlin/com/workday/torque/Main.kt @@ -1,14 +1,16 @@ package com.workday.torque +import java.nio.file.Paths import kotlin.system.exitProcess private val PARAMETER_HELP_NAMES = setOf("--help", "-help", "help", "-h") fun main(rawArgs: Array) { + val currentDirectoryPath = Paths.get("./").toAbsolutePath() val args = parseProcessArgs(rawArgs) - Torque(args).run() + Torque(args = args, workingDirectory = currentDirectoryPath.toString()).run() - System.exit(0) + exitProcess(0) } private fun parseProcessArgs(rawArgs: Array): Args {