From 0a4740c024e8251dcd5e485590807b6f0633a9d7 Mon Sep 17 00:00:00 2001 From: Dmitry Kulikovsky Date: Wed, 26 May 2021 08:28:41 +0100 Subject: [PATCH 1/2] One build tool - Buck (http://buck.build) is using uncommon approach for multidex. It is packing additional dex files not as classesX.dex but as a pack of .jar files in assets/secondary-program-dex-jars. This diff adds support for this approach allowing parsing of these files along with common classesX.dex. Minors: - no need to print non-unique test names. This should not happen in common but some apks somehow have it - filter out common example test name "testFoo" --- .../com/linkedin/dex/parser/DexParser.kt | 27 ++++++++++++++++--- .../linkedin/dex/parser/JUnit3Extensions.kt | 2 +- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/parser/src/main/kotlin/com/linkedin/dex/parser/DexParser.kt b/parser/src/main/kotlin/com/linkedin/dex/parser/DexParser.kt index f7ee411..b752518 100644 --- a/parser/src/main/kotlin/com/linkedin/dex/parser/DexParser.kt +++ b/parser/src/main/kotlin/com/linkedin/dex/parser/DexParser.kt @@ -61,7 +61,7 @@ private class DexParserCommand : CliktCommand() { @JvmStatic @JvmOverloads fun findTestNames(apkPath: String, customAnnotations: List = emptyList()): List { - return findTestMethods(apkPath, customAnnotations).map { it.testName } + return findTestMethods(apkPath, customAnnotations).map { it.testName }.distinct() } /** @@ -84,14 +84,35 @@ private class DexParserCommand : CliktCommand() { fun readDexFiles(path: String): List { ZipInputStream(FileInputStream(File(path))).use { zip -> return zip.entries() - .filter { it.name.endsWith(".dex") } - .map { zip.readBytes() } + .filter { + it.name.endsWith(".dex") or + (it.name.contains("secondary-program-dex-jars") + and it.name.endsWith(".jar")) + } + .map { + if (it.name.endsWith(".jar")) { + readSecondaryDex(zip.readBytes()) + } else { + zip.readBytes() + } + } .map { ByteBuffer.wrap(it) } .map(::DexFile) .toList() } } + private fun readSecondaryDex(jardex: ByteArray): ByteArray { + ZipInputStream(jardex.inputStream()).use { zip -> + return zip.entries() + .filter { + it.name.endsWith(".dex") + } + .map{ zip.readBytes() } + .first() + } + } + private fun ZipInputStream.entries(): Sequence { return object : Sequence { override fun iterator(): Iterator { diff --git a/parser/src/main/kotlin/com/linkedin/dex/parser/JUnit3Extensions.kt b/parser/src/main/kotlin/com/linkedin/dex/parser/JUnit3Extensions.kt index 73a62a3..5c8fa24 100644 --- a/parser/src/main/kotlin/com/linkedin/dex/parser/JUnit3Extensions.kt +++ b/parser/src/main/kotlin/com/linkedin/dex/parser/JUnit3Extensions.kt @@ -65,7 +65,7 @@ private fun findJUnit3Tests(dexFiles: List, testNames: MutableSet): List { val testClasses = findClassesWithSuperClass(descriptors) return createTestMethods(testClasses, { classDef, _ -> findMethodIds(classDef) }) - .filter { it.testName.contains("#test") } + .filter { it.testName.contains("#test") and !it.testName.contains("#testFoo") } } fun DexFile.findMethodIds(classDefItem: ClassDefItem): MutableList { From c3339dfb32e969370115b13fe61151aeaa39b26e Mon Sep 17 00:00:00 2001 From: Dmitry Kulikovsky Date: Wed, 26 May 2021 11:43:43 +0100 Subject: [PATCH 2/2] - "testFoo" turns out to be too generic if used with `contains`, changing it to a more explicit `endsWith` --- .../src/main/kotlin/com/linkedin/dex/parser/JUnit3Extensions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser/src/main/kotlin/com/linkedin/dex/parser/JUnit3Extensions.kt b/parser/src/main/kotlin/com/linkedin/dex/parser/JUnit3Extensions.kt index 5c8fa24..d659d16 100644 --- a/parser/src/main/kotlin/com/linkedin/dex/parser/JUnit3Extensions.kt +++ b/parser/src/main/kotlin/com/linkedin/dex/parser/JUnit3Extensions.kt @@ -65,7 +65,7 @@ private fun findJUnit3Tests(dexFiles: List, testNames: MutableSet): List { val testClasses = findClassesWithSuperClass(descriptors) return createTestMethods(testClasses, { classDef, _ -> findMethodIds(classDef) }) - .filter { it.testName.contains("#test") and !it.testName.contains("#testFoo") } + .filter { it.testName.contains("#test") and !it.testName.endsWith("#testFoo") } } fun DexFile.findMethodIds(classDefItem: ClassDefItem): MutableList {