From 90425952651fbd4dc35c0f61625efc44b3ea7afd Mon Sep 17 00:00:00 2001 From: Marcus Bermel Date: Wed, 22 Jun 2022 11:56:42 -0600 Subject: [PATCH] Update torque to use aapt2 --- torque-core/build.gradle | 12 +++ .../com/workday/torque/ApkTestParser.kt | 95 +++++++++++++------ .../kotlin/com/workday/torque/Installer.kt | 18 ++-- .../com/workday/torque/ModuleTestParser.kt | 15 ++- torque-gradle-plugin/build.gradle | 1 + torque-runner/build.gradle | 1 + 6 files changed, 106 insertions(+), 36 deletions(-) diff --git a/torque-core/build.gradle b/torque-core/build.gradle index 0415900..2f9239a 100755 --- a/torque-core/build.gradle +++ b/torque-core/build.gradle @@ -41,3 +41,15 @@ junitPlatform { } } } + +jar { + manifest { + attributes 'Implementation-Title': POM_NAME, + 'Built-Date': new Date(), + 'Built-JDK': System.getProperty('java.version'), + 'Built-Gradle': gradle.gradleVersion + } + from { + configurations.runtimeClasspath.findAll { it.name.endsWith('jar') }.collect { zipTree(it) } + } +} 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 8b14a7d..3e444c7 100755 --- a/torque-core/src/main/kotlin/com/workday/torque/ApkTestParser.kt +++ b/torque-core/src/main/kotlin/com/workday/torque/ApkTestParser.kt @@ -1,32 +1,56 @@ package com.workday.torque -import com.gojuno.commander.android.aapt +import com.gojuno.commander.android.androidHome import com.gojuno.commander.os.Notification 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.* class ApkTestParser { fun getValidatedTestPackage(testApkPath: String): ApkPackage.Valid { return parseTestPackage(testApkPath).validateApkPackage() } + private val buildTools: String? by lazy { + File(androidHome, "build-tools") + .listFiles() + .sortedArray() + .lastOrNull() + ?.absolutePath + } + 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 = listOf( - aapt, "dump", "badging", testApkPath - ), - unbufferedOutput = true + commandAndArgs = commandAndArgs, + unbufferedOutput = true, ) .ofType(Notification.Exit::class.java) .map { (output) -> - output - .readText() - .split(System.lineSeparator()) - // output format `package: name='$testPackage' versionCode='' versionName='' platformBuildVersionName='xxx'` - .firstOrNull { it.contains("package") } - ?.split(" ") - ?.firstOrNull { it.startsWith("name=") } + 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") + } + val splitPackageText = packageText + ?.split(" ") + val name = splitPackageText + ?.firstOrNull { it.startsWith("name=") } + + if (name.isNullOrEmpty()) { + return@map ApkPackage.ParseError("'name' token was null") + } + + name ?.split("'") ?.getOrNull(1) ?.let(ApkPackage::Valid) @@ -37,6 +61,18 @@ class ApkTestParser { .value() } + private fun makeOutputFile(): File { + return Random() + .nextInt() + .let { System.nanoTime() + it } + .let { name -> + File("$name.output").apply { + createNewFile() + deleteOnExit() + } + } + } + fun getValidatedTargetPackage(testApkPath: String): ApkPackage.Valid { return parseTargetPackage(testApkPath).validateApkPackage() } @@ -44,23 +80,29 @@ class ApkTestParser { private fun parseTargetPackage(testApkPath: String): ApkPackage { return process( commandAndArgs = listOf( - aapt, "dump", "xmltree", testApkPath, "AndroidManifest.xml" + aapt, "dump", "xmltree", testApkPath, "--file", "AndroidManifest.xml" ), - unbufferedOutput = true + unbufferedOutput = true, + keepOutputOnExit = true, ) .ofType(Notification.Exit::class.java) .map { (output) -> - output - .readText() - .split(System.lineSeparator()) - // output format `A: android:targetPackage(0x01010021)="$targetPackage" (Raw: "$targetPackag")` - .firstOrNull { it.contains("android:targetPackage") } - ?.split(" ") - ?.firstOrNull { it.startsWith("android:targetPackage") } - ?.substringAfter("=") - ?.trim('"') + 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 \$TEST_APK AndroidManifest.xml` output.") + ?: ApkPackage.ParseError("Cannot parse target package from `aapt dump xmltree ${testApkPath} AndroidManifest.xml` output.") } .toSingle() .toBlocking() @@ -85,7 +127,7 @@ class ApkTestParser { private fun parseTestRunner(testApkPath: String): TestRunner = process( commandAndArgs = listOf( - aapt, "dump", "xmltree", testApkPath, "AndroidManifest.xml" + aapt, "dump", "xmltree", testApkPath, "--file", "AndroidManifest.xml" ), unbufferedOutput = true ) @@ -96,11 +138,10 @@ class ApkTestParser { .split(System.lineSeparator()) .dropWhile { !it.contains("instrumentation") } .firstOrNull { it.contains("android:name") } - // output format : `A: android:name(0x01010003)="$testRunner" (Raw: "$testRunner")` ?.split("\"") ?.getOrNull(1) ?.let(TestRunner::Valid) - ?: TestRunner.ParseError("Cannot parse test runner from `aapt dump xmltree \$TEST_APK AndroidManifest.xml` output.") + ?: TestRunner.ParseError("Cannot parse test runner from `aapt dump xmltree ${testApkPath} AndroidManifest.xml` output.") } .toSingle() .toBlocking() 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 1c1774c..7e82169 100755 --- a/torque-core/src/main/kotlin/com/workday/torque/Installer.kt +++ b/torque-core/src/main/kotlin/com/workday/torque/Installer.kt @@ -97,10 +97,11 @@ class Installer(private val adbDevice: AdbDevice, private val processRunner: Pro .fromCallable { System.currentTimeMillis() } .flatMap { startTimeMillis -> adbInstallApk(pathToApk, installTimeout).map { it to startTimeMillis } } .map { (exitNotification, startTimeMillis) -> - val success = exitNotification - .preprocessOutput() - .filter { it.isNotEmpty() } - .firstOrNull { it.equals("Success", ignoreCase = true) } != null + val preProcessed = exitNotification + .preprocessOutput() + .filter { it.isNotEmpty() } + .firstOrNull { it.equals("Success", ignoreCase = true) } + val success = preProcessed != null val duration = System.currentTimeMillis() - startTimeMillis @@ -124,15 +125,16 @@ 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) + unbufferedOutput = true, + keepOutputOnExit = true) .ofType(Notification.Exit::class.java) } private fun Notification.Exit.preprocessOutput(): List { return output - .readText() - .split(System.lineSeparator()) - .map { it.trim() } + .readText() + .split(System.lineSeparator()) + .map { it.trim() } } } 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 14e4bcf..eee3b2d 100644 --- a/torque-core/src/main/kotlin/com/workday/torque/ModuleTestParser.kt +++ b/torque-core/src/main/kotlin/com/workday/torque/ModuleTestParser.kt @@ -1,10 +1,13 @@ package com.workday.torque +import com.gojuno.commander.os.log import com.linkedin.dex.parser.TestAnnotation import com.linkedin.dex.parser.TestMethod import com.workday.torque.pooling.ModuleInfo import com.workday.torque.pooling.TestModule 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()) { fun parseTestsFromModuleApks(): List { @@ -14,8 +17,18 @@ class ModuleTestParser(private val args: Args, private val apkTestParser: ApkTes .filterAnnotations(includedAnnotations = args.includedAnnotations, excludedAnnotations = args.excludedAnnotations) .filterClassRegexes(args.testClassRegexes) println("Filtered tests count: ${testMethods.size}") + val testApkFile = File(testApkPath) + + val time = System.currentTimeMillis() + val expired = time + TimeUnit.SECONDS.toMillis(5) + while((!testApkFile.exists() || testApkFile.length() < 1024) && System.currentTimeMillis() < expired) { + Thread.sleep(1000) + } val moduleInfo = createModuleInfo(testApkPath) - add(TestModule(moduleInfo, testMethods)) + val isMyModule = moduleInfo.moduleInfo.pathToApk.contains(other = "-DEBUG.apk", ignoreCase = false) + if (!isMyModule) { + add(TestModule(moduleInfo, testMethods)) + } } } } diff --git a/torque-gradle-plugin/build.gradle b/torque-gradle-plugin/build.gradle index a4f8aee..4f3951d 100755 --- a/torque-gradle-plugin/build.gradle +++ b/torque-gradle-plugin/build.gradle @@ -12,6 +12,7 @@ dependencies { } jar { + entryCompression = ZipEntryCompression.STORED manifest { attributes 'Implementation-Title': POM_NAME, 'Built-Date': new Date(), diff --git a/torque-runner/build.gradle b/torque-runner/build.gradle index f8c7388..dc20d15 100755 --- a/torque-runner/build.gradle +++ b/torque-runner/build.gradle @@ -10,6 +10,7 @@ dependencies { jar { + entryCompression = ZipEntryCompression.STORED // Build jar with dependencies. from(configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }) { exclude 'META-INF/*.SF'