From 6b62f18714a249d815e30994e0e6288108e973df Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Sat, 7 Sep 2024 09:59:10 -0400 Subject: [PATCH 01/10] Support jar info html reports --- gradle/libs.versions.toml | 1 + reports/build.gradle | 1 + .../diffuse/diff/ArchiveFilesDiff.kt | 204 +++++++++++++++++- .../jakewharton/diffuse/diff/ComponentDiff.kt | 60 ++++++ .../com/jakewharton/diffuse/diff/JarDiff.kt | 3 + .../com/jakewharton/diffuse/diff/JarsDiff.kt | 59 +++++ .../diffuse/info/ArchiveFilesInfo.kt | 93 +++++++- .../com/jakewharton/diffuse/info/JarInfo.kt | 5 + .../com/jakewharton/diffuse/info/JarsInfo.kt | 34 +++ .../diffuse/report/html/JarDiffHtmlReport.kt | 78 +++++++ .../diffuse/report/html/JarInfoHtmlReport.kt | 57 +++++ .../com/jakewharton/diffuse/report/strings.kt | 16 ++ 12 files changed, 605 insertions(+), 6 deletions(-) create mode 100644 reports/src/main/kotlin/com/jakewharton/diffuse/report/html/JarDiffHtmlReport.kt create mode 100644 reports/src/main/kotlin/com/jakewharton/diffuse/report/html/JarInfoHtmlReport.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 710b07ca..a8624a05 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,6 +14,7 @@ protobufJava = { module = "com.google.protobuf:protobuf-java", version.ref = "pr clikt = "com.github.ajalt.clikt:clikt:4.4.0" junit = "junit:junit:4.13.2" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" +kotlinxHtml = "org.jetbrains.kotlinx:kotlinx-html:0.11.0" okio = "com.squareup.okio:okio:3.9.0" byteunits = "com.jakewharton.byteunits:byteunits:0.9.1" asm = "org.ow2.asm:asm:9.7" diff --git a/reports/build.gradle b/reports/build.gradle index 1e3aa938..a39cceaf 100644 --- a/reports/build.gradle +++ b/reports/build.gradle @@ -5,6 +5,7 @@ apply plugin: 'org.jetbrains.dokka' dependencies { api projects.formats implementation libs.picnic + implementation libs.kotlinxHtml implementation libs.diffUtils testImplementation libs.junit diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ArchiveFilesDiff.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ArchiveFilesDiff.kt index 64f124fd..8b97a093 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ArchiveFilesDiff.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ArchiveFilesDiff.kt @@ -5,6 +5,7 @@ import com.jakewharton.diffuse.diffuseTable import com.jakewharton.diffuse.format.ArchiveFile.Type import com.jakewharton.diffuse.format.ArchiveFiles import com.jakewharton.diffuse.io.Size +import com.jakewharton.diffuse.report.htmlEncoded import com.jakewharton.diffuse.report.toDiffString import com.jakewharton.picnic.TableSectionDsl import com.jakewharton.picnic.TextAlignment.BottomCenter @@ -12,6 +13,16 @@ import com.jakewharton.picnic.TextAlignment.BottomLeft import com.jakewharton.picnic.TextAlignment.MiddleCenter import com.jakewharton.picnic.TextAlignment.MiddleRight import com.jakewharton.picnic.renderText +import kotlinx.html.FlowContent +import kotlinx.html.TR +import kotlinx.html.style +import kotlinx.html.table +import kotlinx.html.tbody +import kotlinx.html.td +import kotlinx.html.tfoot +import kotlinx.html.thead +import kotlinx.html.tr +import kotlinx.html.unsafe internal class ArchiveFilesDiff( val oldFiles: ArchiveFiles, @@ -120,7 +131,7 @@ internal fun ArchiveFilesDiff.toSummaryTable( } } - fun TableSectionDsl.addApkRow(name: String, type: Type? = null) { + fun TableSectionDsl.addArchiveRow(name: String, type: Type? = null) { val old = if (type != null) oldFiles.filterValues { it.type == type } else oldFiles val new = if (type != null) newFiles.filterValues { it.type == type } else newFiles val oldSize = old.values.fold(Size.ZERO) { acc, file -> acc + file.size } @@ -150,7 +161,7 @@ internal fun ArchiveFilesDiff.toSummaryTable( alignment = MiddleRight } for (type in displayTypes) { - addApkRow(type.displayName, type) + addArchiveRow(type.displayName, type) } } @@ -158,7 +169,7 @@ internal fun ArchiveFilesDiff.toSummaryTable( cellStyle { alignment = MiddleRight } - addApkRow("total") + addArchiveRow("total") } }.renderText() @@ -243,3 +254,190 @@ internal fun ArchiveFilesDiff.toDetailReport() = buildString { }.renderText(), ) } + +internal fun FlowContent.toSummaryTable( + name: String, + diff: ArchiveFilesDiff, + displayTypes: List, + skipIfEmptyTypes: Set = emptySet(), +) { + table { + thead { + if (diff.includeCompressed) { + tr { + td { + rowSpan = "2" + style = "text-align: left; vertical-align: bottom;" + +name + } + td { + colSpan = "3" + style = "text-align: center; vertical-align: bottom;" + +"compressed" + } + td { + colSpan = "3" + style = "text-align: center; vertical-align: bottom;" + +"uncompressed" + } + } + tr { + td { +"old" } + td { +"new" } + td { +"diff" } + td { +"old" } + td { +"new" } + td { +"diff" } + } + } else { + tr { + td { + style = "text-align: left; vertical-align: bottom;" + +name + } + td { +"old" } + td { +"new" } + td { +"diff" } + } + } + } + + fun TR.addArchiveRow(name: String, type: Type? = null) { + val old = if (type != null) diff.oldFiles.filterValues { it.type == type } else diff.oldFiles + val new = if (type != null) diff.newFiles.filterValues { it.type == type } else diff.newFiles + val oldSize = old.values.fold(Size.ZERO) { acc, file -> acc + file.size } + val newSize = new.values.fold(Size.ZERO) { acc, file -> acc + file.size } + val oldUncompressedSize = old.values.fold(Size.ZERO) { acc, file -> acc + file.uncompressedSize } + val newUncompressedSize = new.values.fold(Size.ZERO) { acc, file -> acc + file.uncompressedSize } + if (oldSize != Size.ZERO || newSize != Size.ZERO || type !in skipIfEmptyTypes) { + val uncompressedDiff = (newUncompressedSize - oldUncompressedSize).toDiffString() + if (diff.includeCompressed) { + td { +name } + td { +oldSize.toString() } + td { +newSize.toString() } + td { +(newSize - oldSize).toString() } + td { +oldUncompressedSize.toString() } + td { +newUncompressedSize.toString() } + td { +uncompressedDiff } + } else { + td { +name } + td { +oldUncompressedSize.toString() } + td { +newUncompressedSize.toString() } + td { +uncompressedDiff } + } + } + } + + tbody { + style = "text-align: right; vertical-align: center;" + + for (type in displayTypes) { + tr { + addArchiveRow(type.displayName, type) + } + } + } + + tfoot { + style = "text-align: right; vertical-align: center;" + tr { addArchiveRow("total") } + } + } +} + +internal fun FlowContent.toDetailReport(diff: ArchiveFilesDiff) { + table { + thead { + if (diff.includeCompressed) { + tr { + td { + style = "text-align: center; vertical-align: center;" + colSpan = "2" + +"compressed" + } + td { + style = "text-align: center; vertical-align: center;" + colSpan = "2" + +"uncompressed" + } + + td { + style = "text-align: left; vertical-align: bottom;" + rowSpan = "2" + +"path" + } + } + tr { + td { +"size" } + td { +"diff" } + td { +"size" } + td { +"diff" } + } + } else { + tr { + td { +"size" } + td { +"diff" } + td { + style = "text-align: left; vertical-align: bottom;" + +"path" + } + } + } + } + tfoot { + tr { + if (diff.includeCompressed) { + val totalSize = diff.changes.fold(Size.ZERO) { acc, change -> acc + change.size } + val totalDiff = diff.changes.fold(Size.ZERO) { acc, change -> acc + change.sizeDiff } + td { + style = "text-align: right; vertical-align: center;" + +totalSize.toString() + } + td { + style = "text-align: right; vertical-align: center;" + +totalDiff.toDiffString() + } + } + val totalUncompressedSize = diff.changes.fold(Size.ZERO) { acc, change -> acc + change.uncompressedSize } + val totalUncompressedDiff = diff.changes.fold(Size.ZERO) { acc, change -> acc + change.uncompressedSizeDiff } + td { + style = "text-align: right; vertical-align: center;" + +totalUncompressedSize.toString() + } + td { + style = "text-align: right; vertical-align: center;" + +totalUncompressedDiff.toDiffString() + } + td { +"(total)" } + } + } + for ((path, size, sizeDiff, uncompressedSize, uncompressedSizeDiff, type) in diff.changes) { + val typeChar = when (type) { + Change.Type.Added -> '+' + Change.Type.Removed -> '-' + Change.Type.Changed -> '∆' + } + tr { + if (diff.includeCompressed) { + td { + style = "text-align: right; vertical-align: center;" + if (type != Change.Type.Removed) +size.toString() else +"" + } + td { + style = "text-align: right; vertical-align: center;" + +sizeDiff.toDiffString() + } + } + td { + style = "text-align: right; vertical-align: center;" + if (type != Change.Type.Removed) +uncompressedSize.toString() else +"" + } + td { + style = "text-align: right; vertical-align: center;" + +uncompressedSizeDiff.toDiffString() + } + td { unsafe { raw("$typeChar $path".htmlEncoded) } } + } + } + } +} diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ComponentDiff.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ComponentDiff.kt index dbf1e85b..da2ff817 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ComponentDiff.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ComponentDiff.kt @@ -1,8 +1,21 @@ package com.jakewharton.diffuse.diff import com.jakewharton.diffuse.diffuseTable +import com.jakewharton.diffuse.report.htmlEncoded import com.jakewharton.diffuse.report.toDiffString import com.jakewharton.picnic.renderText +import kotlinx.html.FlowContent +import kotlinx.html.br +import kotlinx.html.div +import kotlinx.html.h3 +import kotlinx.html.span +import kotlinx.html.style +import kotlinx.html.table +import kotlinx.html.tbody +import kotlinx.html.td +import kotlinx.html.thead +import kotlinx.html.tr +import kotlinx.html.unsafe internal class ComponentDiff( val oldRawCount: Int, @@ -71,3 +84,50 @@ internal fun StringBuilder.appendComponentDiff(name: String, diff: ComponentDiff ) } } + +internal fun FlowContent.appendComponentDiff(name: String, diff: ComponentDiff<*>) { + if (diff.changed) { + h3 { +"$name:" } + + div { + style = "margin-left: 16pt;" + + table { + thead { + tr { + td { +"old" } + td { +"new" } + td { +"diff" } + } + } + tbody { + val diffSize = (diff.added.size - diff.removed.size).toDiffString() + val addedSize = diff.added.size.toDiffString(zeroSign = '+') + val removedSize = (-diff.removed.size).toDiffString(zeroSign = '-') + + tr { + td { +diff.oldCount.toString() } + td { +diff.newCount.toString() } + td { +"$diffSize ($addedSize $removedSize)" } + } + } + } + + if (diff.added.isNotEmpty()) { + br() + diff.added.forEach { + span { unsafe { raw("+ $it".htmlEncoded) } } + br() + } + } + + if (diff.removed.isNotEmpty()) { + br() + diff.removed.forEach { + span { unsafe { raw("- $it".htmlEncoded) } } + br() + } + } + } + } +} diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/JarDiff.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/JarDiff.kt index 580b4890..6fb9e3c8 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/JarDiff.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/JarDiff.kt @@ -3,6 +3,7 @@ package com.jakewharton.diffuse.diff import com.jakewharton.diffuse.format.ApiMapping import com.jakewharton.diffuse.format.Jar import com.jakewharton.diffuse.report.Report +import com.jakewharton.diffuse.report.html.JarDiffHtmlReport import com.jakewharton.diffuse.report.text.JarDiffTextReport internal class JarDiff( @@ -17,4 +18,6 @@ internal class JarDiff( val changed = jars.changed || archive.changed override fun toTextReport(): Report = JarDiffTextReport(this) + + override fun toHtmlReport(): Report = JarDiffHtmlReport(this) } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/JarsDiff.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/JarsDiff.kt index eb0d2faa..3e586eb0 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/JarsDiff.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/JarsDiff.kt @@ -10,6 +10,15 @@ import com.jakewharton.diffuse.report.toDiffString import com.jakewharton.picnic.TextAlignment.MiddleLeft import com.jakewharton.picnic.TextAlignment.MiddleRight import com.jakewharton.picnic.renderText +import kotlinx.html.FlowContent +import kotlinx.html.TBODY +import kotlinx.html.style +import kotlinx.html.table +import kotlinx.html.tbody +import kotlinx.html.td +import kotlinx.html.th +import kotlinx.html.thead +import kotlinx.html.tr internal class JarsDiff( val oldJars: List, @@ -75,3 +84,53 @@ internal fun JarsDiff.toDetailReport() = buildString { appendComponentDiff("METHODS", methods) appendComponentDiff("FIELDS", fields) } + +internal fun FlowContent.toHtmlSummary(name: String, diff: JarsDiff) { + table { + thead { + tr { + th { +name } + th { +"old" } + th { +"new" } + th { + colSpan = "2" + +"diff" + } + } + } + + tbody { + fun TBODY.addRow(name: String, diff: ComponentDiff<*>) { + tr { + style = "text-align: right; vertical-align: middle;" + td { +name } + td { +diff.oldCount.toString() } + td { +diff.newCount.toString() } + td { + style = "border-right: none;" + +(diff.added.size - diff.removed.size).toDiffString() + } + + val addedSize = diff.added.size.toDiffString(zeroSign = '+') + val removedSize = (-diff.removed.size).toDiffString(zeroSign = '-') + td { + style = "border-left: none; padding-left: 0; text-align: left;" + +"($addedSize $removedSize)" + } + } + } + + // TODO addRow("strings", strings)? + addRow("classes", diff.classes) + addRow("methods", diff.methods) + addRow("fields", diff.fields) + } + } +} + +internal fun FlowContent.toDetailReport(diff: JarsDiff) { + // TODO appendComponentDiff("STRINGS", strings)? + appendComponentDiff("CLASSES", diff.classes) + appendComponentDiff("METHODS", diff.methods) + appendComponentDiff("FIELDS", diff.fields) +} diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/info/ArchiveFilesInfo.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/info/ArchiveFilesInfo.kt index ebe659ec..8ad29d6d 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/info/ArchiveFilesInfo.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/info/ArchiveFilesInfo.kt @@ -7,6 +7,16 @@ import com.jakewharton.diffuse.io.Size import com.jakewharton.picnic.TableSectionDsl import com.jakewharton.picnic.TextAlignment import com.jakewharton.picnic.renderText +import kotlinx.html.FlowContent +import kotlinx.html.TR +import kotlinx.html.style +import kotlinx.html.table +import kotlinx.html.tbody +import kotlinx.html.td +import kotlinx.html.tfoot +import kotlinx.html.th +import kotlinx.html.thead +import kotlinx.html.tr internal fun ArchiveFiles.toSummaryTable( name: String, @@ -37,7 +47,7 @@ internal fun ArchiveFiles.toSummaryTable( } } - fun TableSectionDsl.addApkRow(name: String, type: ArchiveFile.Type? = null) { + fun TableSectionDsl.addArchiveFileRow(name: String, type: ArchiveFile.Type? = null) { val old = if (type != null) filterValues { it.type == type } else this@toSummaryTable val oldSize = old.values.fold(Size.ZERO) { acc, file -> acc + file.size } val oldUncompressedSize = old.values.fold(Size.ZERO) { acc, file -> acc + file.uncompressedSize } @@ -59,7 +69,7 @@ internal fun ArchiveFiles.toSummaryTable( alignment = TextAlignment.MiddleRight } for (type in displayTypes) { - addApkRow(type.displayName, type) + addArchiveFileRow(type.displayName, type) } } @@ -67,6 +77,83 @@ internal fun ArchiveFiles.toSummaryTable( cellStyle { alignment = TextAlignment.MiddleRight } - addApkRow("total") + addArchiveFileRow("total") } }.renderText() + +internal fun FlowContent.toSummaryTable( + name: String, + files: ArchiveFiles, + displayTypes: List, + skipIfEmptyTypes: Set = emptySet(), + includeCompressed: Boolean = true, +) { + table { + thead { + tr { + if (includeCompressed) { + th { + style = "text-align: left; vertical-align: bottom;" + + +name + } + + th { + style = "text-align: center; vertical-align: bottom;" + + +"compressed" + } + + th { + style = "text-align: center; vertical-align: bottom;" + + +"uncompressed" + } + } else { + th { + style = "text-align: left; vertical-align: bottom;" + + +name + } + + th { + +"size" + } + } + } + } + + fun TR.addArchiveFileRow(name: String, type: ArchiveFile.Type? = null) { + val old = if (type != null) files.filterValues { it.type == type } else files + val oldSize = old.values.fold(Size.ZERO) { acc, file -> acc + file.size } + val oldUncompressedSize = old.values.fold(Size.ZERO) { acc, file -> acc + file.uncompressedSize } + if (oldSize != Size.ZERO || type !in skipIfEmptyTypes) { + if (includeCompressed) { + td { +name } + td { +oldSize.toString() } + td { +oldUncompressedSize.toString() } + } else { + td { +name } + td { +oldUncompressedSize.toString() } + } + } + } + + tbody { + style = "text-align: right; vertical-align: middle;" + + for (type in displayTypes) { + tr { + addArchiveFileRow(type.displayName, type) + } + } + } + + tfoot { + tr { + style = "text-align: right; vertical-align: middle;" + addArchiveFileRow("total") + } + } + } +} diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/info/JarInfo.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/info/JarInfo.kt index 2b07bd53..2b320fa3 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/info/JarInfo.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/info/JarInfo.kt @@ -2,6 +2,7 @@ package com.jakewharton.diffuse.info import com.jakewharton.diffuse.format.Jar import com.jakewharton.diffuse.report.Report +import com.jakewharton.diffuse.report.html.JarInfoHtmlReport import com.jakewharton.diffuse.report.text.JarInfoTextReport class JarInfo( @@ -10,4 +11,8 @@ class JarInfo( override fun toTextReport(): Report { return JarInfoTextReport(jar) } + + override fun toHtmlReport(): Report { + return JarInfoHtmlReport(jar) + } } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/info/JarsInfo.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/info/JarsInfo.kt index eebe734e..e419fdba 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/info/JarsInfo.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/info/JarsInfo.kt @@ -6,6 +6,13 @@ import com.jakewharton.diffuse.format.Jar import com.jakewharton.diffuse.format.Method import com.jakewharton.picnic.TextAlignment import com.jakewharton.picnic.renderText +import kotlinx.html.FlowContent +import kotlinx.html.style +import kotlinx.html.table +import kotlinx.html.tbody +import kotlinx.html.th +import kotlinx.html.thead +import kotlinx.html.tr internal fun List.toSummaryTable(name: String) = diffuseTable { header { @@ -27,3 +34,30 @@ internal fun List.toSummaryTable(name: String) = diffuseTable { addRow("fields") { it.members.filterIsInstance() } } }.renderText() + +internal fun FlowContent.toSummaryTable(name: String, jars: List) { + table { + thead { + tr { + th { +name } + th { +"count" } + } + } + + tbody { + style = "text-align: right; vertical-align: middle;" + + fun addRow(name: String, selector: (Jar) -> List) { + tr { + th { +name } + th { +jars.flatMap(selector).size.toString() } + } + } + + // TODO addRow("strings", strings)? + addRow("classes", Jar::classes) + addRow("methods") { it.members.filterIsInstance() } + addRow("fields") { it.members.filterIsInstance() } + } + } +} diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/JarDiffHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/JarDiffHtmlReport.kt new file mode 100644 index 00000000..2d11b2ae --- /dev/null +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/JarDiffHtmlReport.kt @@ -0,0 +1,78 @@ +package com.jakewharton.diffuse.report.html + +import com.jakewharton.diffuse.diff.JarDiff +import com.jakewharton.diffuse.diff.toDetailReport +import com.jakewharton.diffuse.diff.toHtmlSummary +import com.jakewharton.diffuse.diff.toSummaryTable +import com.jakewharton.diffuse.format.ArchiveFile.Type +import com.jakewharton.diffuse.report.Report +import kotlinx.html.body +import kotlinx.html.br +import kotlinx.html.details +import kotlinx.html.h2 +import kotlinx.html.head +import kotlinx.html.html +import kotlinx.html.span +import kotlinx.html.stream.appendHTML +import kotlinx.html.style +import kotlinx.html.summary +import kotlinx.html.unsafe + +internal class JarDiffHtmlReport(private val jarDiff: JarDiff) : Report { + override fun write(appendable: Appendable) { + appendable.appendHTML().html { + head { + style(type = "text/css") { + unsafe { + raw( + """ + table{ + border-collapse:collapse; + border:1px solid #000; + } + + table td{ + border:1px solid #000; + } + """.trimIndent(), + ) + } + } + } + + body { + h2 { +"Summary" } + + span { +"OLD: ${jarDiff.oldJar.filename}" } + br() + span { +"NEW: ${jarDiff.newJar.filename}" } + br() + br() + + toSummaryTable("JAR", jarDiff.archive, Type.JAR_TYPES) + + br() + + toHtmlSummary("", jarDiff.jars) + + if (jarDiff.archive.changed) { + br() + details { + summary { +"JAR" } + toDetailReport(jarDiff.archive) + } + } + + if (jarDiff.jars.changed) { + br() + details { + summary { +"CLASSFILES" } + toDetailReport(jarDiff.jars) + } + } + } + } + } + + override fun toString() = buildString { write(this) } +} diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/JarInfoHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/JarInfoHtmlReport.kt new file mode 100644 index 00000000..9bef9a37 --- /dev/null +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/JarInfoHtmlReport.kt @@ -0,0 +1,57 @@ +package com.jakewharton.diffuse.report.html + +import com.jakewharton.diffuse.format.ArchiveFile.Type +import com.jakewharton.diffuse.format.Jar +import com.jakewharton.diffuse.info.toSummaryTable +import com.jakewharton.diffuse.report.Report +import kotlinx.html.body +import kotlinx.html.br +import kotlinx.html.details +import kotlinx.html.head +import kotlinx.html.html +import kotlinx.html.p +import kotlinx.html.stream.appendHTML +import kotlinx.html.style +import kotlinx.html.summary +import kotlinx.html.unsafe + +internal class JarInfoHtmlReport(private val jar: Jar) : Report { + override fun write(appendable: Appendable) { + appendable.appendHTML().html { + head { + style(type = "text/css") { + unsafe { + raw( + """ + table{ + border-collapse:collapse; + border:1px solid #000; + } + + table td{ + border:1px solid #000; + } + """.trimIndent(), + ) + } + } + } + + body { + p { +jar.filename!! } + + toSummaryTable("JAR", jar.files, Type.JAR_TYPES) + + br() + + details { + summary { +"CLASSFILE INFO" } + + toSummaryTable("entity", listOf(jar)) + } + } + } + } + + override fun toString() = buildString { write(this) } +} diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/strings.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/strings.kt index a7834bb8..08609f40 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/strings.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/strings.kt @@ -25,3 +25,19 @@ internal fun Size.toDiffString() = buildString { } append(this@toDiffString) } + +internal val String.htmlEncoded: String + get() = buildString { + for (char in this@htmlEncoded) { + when (char) { + '&' -> append("&") + '<' -> append("<") + '>' -> append(">") + '\"' -> append(""") + '\'' -> append("'") + '→' -> append("→") + '∆' -> append("Δ") + else -> append(char) + } + } + } From 58fd2b728b070c305f8d07ab1e2d326f171b15e7 Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Sun, 8 Sep 2024 16:16:58 -0400 Subject: [PATCH 02/10] Support dex info html reports --- .../com/jakewharton/diffuse/info/DexInfo.kt | 5 ++ .../com/jakewharton/diffuse/info/DexesInfo.kt | 60 +++++++++++++++++++ .../diffuse/report/html/DexDiffHtmlReport.kt | 12 ++++ .../diffuse/report/html/DexInfoHtmlReport.kt | 45 ++++++++++++++ 4 files changed, 122 insertions(+) create mode 100644 reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexDiffHtmlReport.kt create mode 100644 reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexInfoHtmlReport.kt diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/info/DexInfo.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/info/DexInfo.kt index 1a8c8aee..4fbcd9f8 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/info/DexInfo.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/info/DexInfo.kt @@ -2,6 +2,7 @@ package com.jakewharton.diffuse.info import com.jakewharton.diffuse.format.Dex import com.jakewharton.diffuse.report.Report +import com.jakewharton.diffuse.report.html.DexInfoHtmlReport import com.jakewharton.diffuse.report.text.DexInfoTextReport class DexInfo( @@ -10,4 +11,8 @@ class DexInfo( override fun toTextReport(): Report { return DexInfoTextReport(dex) } + + override fun toHtmlReport(): Report { + return DexInfoHtmlReport(dex) + } } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/info/DexesInfo.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/info/DexesInfo.kt index 33a4def9..ee43db14 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/info/DexesInfo.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/info/DexesInfo.kt @@ -6,6 +6,14 @@ import com.jakewharton.diffuse.format.Field import com.jakewharton.diffuse.format.Method import com.jakewharton.picnic.TextAlignment import com.jakewharton.picnic.renderText +import kotlinx.html.FlowContent +import kotlinx.html.TBODY +import kotlinx.html.style +import kotlinx.html.table +import kotlinx.html.tbody +import kotlinx.html.td +import kotlinx.html.thead +import kotlinx.html.tr internal fun List.toSummaryTable() = diffuseTable { val isMultidex = size > 1 @@ -53,3 +61,55 @@ internal fun List.toSummaryTable() = diffuseTable { addDexRow("fields") { it.members.filterIsInstance() } } }.renderText() + +internal fun FlowContent.toSummaryTable(dexList: List) { + val isMultidex = dexList.size > 1 + + table { + thead { + tr { + td { + style = "text-align: left; vertical-align: bottom;" + +"DEX" + } + + if (isMultidex) { + td { +"raw" } + td { +"unique" } + } else { + td { +"count" } + } + } + } + + tbody { + style = "text-align: right; vertical-align: center;" + + tr { + td { +"files" } + td { +dexList.size.toString() } + + // todo: is this necessary for HTML tables? Kinda think no... + if (isMultidex) { + td { +"" } + } + } + + fun TBODY.addDexRow(name: String, selector: (Dex) -> List) { + tr { + td { +name } + if (isMultidex) { + td { +dexList.sumOf { selector(it).size } } + } + td { +dexList.flatMapTo(LinkedHashSet(), selector).size.toString() } + } + } + + addDexRow("strings") { it.strings } + addDexRow("types") { it.types } + addDexRow("classes") { it.classes } + addDexRow("methods") { it.members.filterIsInstance() } + addDexRow("fields") { it.members.filterIsInstance() } + } + } +} diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexDiffHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexDiffHtmlReport.kt new file mode 100644 index 00000000..43f4907e --- /dev/null +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexDiffHtmlReport.kt @@ -0,0 +1,12 @@ +package com.jakewharton.diffuse.report.html + +import com.jakewharton.diffuse.diff.DexDiff +import com.jakewharton.diffuse.report.Report + +internal class DexDiffHtmlReport(private val dexDiff: DexDiff) : Report { + override fun write(appendable: Appendable) { + TODO("Not yet implemented") + } + + override fun toString() = buildString { write(this) } +} diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexInfoHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexInfoHtmlReport.kt new file mode 100644 index 00000000..d8d15c7d --- /dev/null +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexInfoHtmlReport.kt @@ -0,0 +1,45 @@ +package com.jakewharton.diffuse.report.html + +import com.jakewharton.diffuse.format.Dex +import com.jakewharton.diffuse.info.toSummaryTable +import com.jakewharton.diffuse.report.Report +import kotlinx.html.body +import kotlinx.html.h2 +import kotlinx.html.head +import kotlinx.html.html +import kotlinx.html.stream.appendHTML +import kotlinx.html.style +import kotlinx.html.unsafe + +internal class DexInfoHtmlReport(private val dex: Dex) : Report { + override fun write(appendable: Appendable) { + appendable.appendHTML().html { + head { + style(type = "text/css") { + unsafe { + raw( + """ + table{ + border-collapse:collapse; + border:1px solid #000; + } + + table td{ + border:1px solid #000; + } + """.trimIndent(), + ) + } + } + } + + body { + h2 { +dex.filename } + + toSummaryTable(listOf(dex)) + } + } + } + + override fun toString() = buildString { write(this) } +} From 34aa7f08dbb93143bb96d5cad48b15c68d67d2bc Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Sun, 8 Sep 2024 17:13:12 -0400 Subject: [PATCH 03/10] Support dex diff html reports --- .../com/jakewharton/diffuse/diff/DexDiff.kt | 10 ++++ .../diffuse/report/html/DexDiffHtmlReport.kt | 47 ++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/DexDiff.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/DexDiff.kt index d9692043..dd341b92 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/DexDiff.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/DexDiff.kt @@ -5,6 +5,7 @@ import com.jakewharton.diffuse.format.Dex import com.jakewharton.diffuse.format.Field import com.jakewharton.diffuse.format.Method import com.jakewharton.diffuse.report.Report +import com.jakewharton.diffuse.report.html.DexDiffHtmlReport import com.jakewharton.diffuse.report.text.DexDiffTextReport import com.jakewharton.diffuse.report.toDiffString import com.jakewharton.picnic.TextAlignment.BottomCenter @@ -12,6 +13,7 @@ import com.jakewharton.picnic.TextAlignment.BottomLeft import com.jakewharton.picnic.TextAlignment.MiddleLeft import com.jakewharton.picnic.TextAlignment.MiddleRight import com.jakewharton.picnic.renderText +import kotlinx.html.FlowContent internal class DexDiff( val oldDexes: List, @@ -32,6 +34,7 @@ internal class DexDiff( val changed = strings.changed || types.changed || methods.changed || fields.changed override fun toTextReport(): Report = DexDiffTextReport(this) + override fun toHtmlReport(): Report = DexDiffHtmlReport(this) } internal fun DexDiff.toSummaryTable() = diffuseTable { @@ -128,3 +131,10 @@ internal fun DexDiff.toDetailReport() = buildString { appendComponentDiff("METHODS", methods) appendComponentDiff("FIELDS", fields) } + +internal fun FlowContent.toDetailReport(diff: DexDiff) { + appendComponentDiff("STRINGS", diff.strings) + appendComponentDiff("TYPES", diff.types) + appendComponentDiff("METHODS", diff.methods) + appendComponentDiff("FIELDS", diff.fields) +} diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexDiffHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexDiffHtmlReport.kt index 43f4907e..eba90c85 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexDiffHtmlReport.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexDiffHtmlReport.kt @@ -1,11 +1,56 @@ package com.jakewharton.diffuse.report.html import com.jakewharton.diffuse.diff.DexDiff +import com.jakewharton.diffuse.diff.toDetailReport import com.jakewharton.diffuse.report.Report +import kotlinx.html.body +import kotlinx.html.br +import kotlinx.html.head +import kotlinx.html.html +import kotlinx.html.span +import kotlinx.html.stream.appendHTML +import kotlinx.html.style +import kotlinx.html.unsafe internal class DexDiffHtmlReport(private val dexDiff: DexDiff) : Report { + private val oldDex = requireNotNull(dexDiff.oldDexes.singleOrNull()) { + "Dex diff report only supports a single old dex. Found: ${dexDiff.oldDexes}" + } + private val newDex = requireNotNull(dexDiff.newDexes.singleOrNull()) { + "Dex diff report only supports a single new dex. Found: ${dexDiff.newDexes}" + } + override fun write(appendable: Appendable) { - TODO("Not yet implemented") + appendable.appendHTML().html { + head { + style(type = "text/css") { + unsafe { + raw( + """ + table{ + border-collapse:collapse; + border:1px solid #000; + } + + table td{ + border:1px solid #000; + } + """.trimIndent(), + ) + } + } + } + + body { + span { +"OLD: ${oldDex.filename}" } + br() + span { +"NEW: ${newDex.filename}" } + br() + br() + + toDetailReport(dexDiff) + } + } } override fun toString() = buildString { write(this) } From 2e126e9bcd2dbea5c2b2f1e81d51ac1b3d08c93a Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Sun, 8 Sep 2024 20:04:38 -0400 Subject: [PATCH 04/10] Support apk info html reports --- .../com/jakewharton/diffuse/info/ApkInfo.kt | 5 ++ .../com/jakewharton/diffuse/info/ArscInfo.kt | 32 ++++++++++ .../diffuse/report/html/ApkDiffHtmlReport.kt | 8 +++ .../diffuse/report/html/ApkInfoHtmlReport.kt | 60 +++++++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkDiffHtmlReport.kt create mode 100644 reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkInfoHtmlReport.kt diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/info/ApkInfo.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/info/ApkInfo.kt index a7fa82d0..e996dfc2 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/info/ApkInfo.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/info/ApkInfo.kt @@ -2,6 +2,7 @@ package com.jakewharton.diffuse.info import com.jakewharton.diffuse.format.Apk import com.jakewharton.diffuse.report.Report +import com.jakewharton.diffuse.report.html.ApkInfoHtmlReport import com.jakewharton.diffuse.report.text.ApkInfoTextReport class ApkInfo( @@ -10,4 +11,8 @@ class ApkInfo( override fun toTextReport(): Report { return ApkInfoTextReport(apk) } + + override fun toHtmlReport(): Report { + return ApkInfoHtmlReport(apk) + } } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/info/ArscInfo.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/info/ArscInfo.kt index 5d6bfe1b..77e449c1 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/info/ArscInfo.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/info/ArscInfo.kt @@ -4,6 +4,13 @@ import com.jakewharton.diffuse.diffuseTable import com.jakewharton.diffuse.format.Arsc import com.jakewharton.picnic.TextAlignment import com.jakewharton.picnic.renderText +import kotlinx.html.FlowContent +import kotlinx.html.style +import kotlinx.html.table +import kotlinx.html.tbody +import kotlinx.html.td +import kotlinx.html.thead +import kotlinx.html.tr internal fun Arsc.toSummaryTable() = diffuseTable { header { @@ -19,3 +26,28 @@ internal fun Arsc.toSummaryTable() = diffuseTable { row("entries", entries.size) } }.renderText() + +internal fun FlowContent.toSummaryTable(arsc: Arsc) { + table { + thead { + tr { + td { +"ARSC" } + td { +"count" } + } + } + + tbody { + style = "text-align: right; vertical-align: center;" + + tr { + td { +"configs" } + td { +arsc.configs.size } + } + + tr { + td { +"entries" } + td { +arsc.entries.size } + } + } + } +} diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkDiffHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkDiffHtmlReport.kt new file mode 100644 index 00000000..42506e6b --- /dev/null +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkDiffHtmlReport.kt @@ -0,0 +1,8 @@ +package com.jakewharton.diffuse.report.html + +import com.jakewharton.diffuse.report.Report + +class ApkDiffHtmlReport : Report { + override fun write(appendable: Appendable) { + } +} diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkInfoHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkInfoHtmlReport.kt new file mode 100644 index 00000000..6ee66b01 --- /dev/null +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkInfoHtmlReport.kt @@ -0,0 +1,60 @@ +package com.jakewharton.diffuse.report.html + +import com.jakewharton.diffuse.format.Apk +import com.jakewharton.diffuse.format.ArchiveFile +import com.jakewharton.diffuse.info.toSummaryTable +import com.jakewharton.diffuse.report.Report +import com.jakewharton.diffuse.report.toSummaryString +import kotlinx.html.body +import kotlinx.html.br +import kotlinx.html.head +import kotlinx.html.html +import kotlinx.html.p +import kotlinx.html.stream.appendHTML +import kotlinx.html.style +import kotlinx.html.unsafe + +class ApkInfoHtmlReport( + private val apk: Apk, +) : Report { + override fun write(appendable: Appendable) { + appendable.appendHTML().html { + head { + style(type = "text/css") { + unsafe { + raw( + """ + table{ + border-collapse:collapse; + border:1px solid #000; + } + + table td{ + border:1px solid #000; + } + """.trimIndent(), + ) + } + } + } + + body { + p { +"${apk.filename} (signature: ${apk.signatures.toSummaryString()})" } + + toSummaryTable( + "APK", + apk.files, + ArchiveFile.Type.APK_TYPES, + skipIfEmptyTypes = setOf(ArchiveFile.Type.Native), + ) + + br() + toSummaryTable(apk.dexes) + br() + toSummaryTable(apk.arsc) + } + } + } + + override fun toString() = buildString { write(this) } +} From cce826cfd2142ffe764eb8f0d153517ebb36c0d1 Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Sun, 8 Sep 2024 21:59:01 -0400 Subject: [PATCH 05/10] Support apk diff html reports --- .../com/jakewharton/diffuse/diff/ApkDiff.kt | 2 + .../com/jakewharton/diffuse/diff/ArscDiff.kt | 130 ++++++++++++++++++ .../com/jakewharton/diffuse/diff/DexDiff.kt | 100 ++++++++++++++ .../jakewharton/diffuse/diff/ManifestDiff.kt | 49 +++++++ .../diffuse/diff/SignaturesDiff.kt | 66 +++++++++ .../diffuse/report/html/ApkDiffHtmlReport.kt | 92 ++++++++++++- .../diffuse/report/html/ApkInfoHtmlReport.kt | 2 +- 7 files changed, 439 insertions(+), 2 deletions(-) diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ApkDiff.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ApkDiff.kt index 7a66c94a..787c8a72 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ApkDiff.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ApkDiff.kt @@ -4,6 +4,7 @@ import com.jakewharton.diffuse.diff.lint.resourcesArscCompression import com.jakewharton.diffuse.format.ApiMapping import com.jakewharton.diffuse.format.Apk import com.jakewharton.diffuse.report.Report +import com.jakewharton.diffuse.report.html.ApkDiffHtmlReport import com.jakewharton.diffuse.report.text.ApkDiffTextReport internal class ApkDiff( @@ -26,4 +27,5 @@ internal class ApkDiff( ) override fun toTextReport(): Report = ApkDiffTextReport(this) + override fun toHtmlReport(): Report = ApkDiffHtmlReport(this) } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ArscDiff.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ArscDiff.kt index 0c3695f3..57339cc6 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ArscDiff.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ArscDiff.kt @@ -6,6 +6,17 @@ import com.jakewharton.diffuse.report.toDiffString import com.jakewharton.picnic.TextAlignment.MiddleLeft import com.jakewharton.picnic.TextAlignment.MiddleRight import com.jakewharton.picnic.renderText +import kotlinx.html.FlowContent +import kotlinx.html.br +import kotlinx.html.div +import kotlinx.html.p +import kotlinx.html.span +import kotlinx.html.style +import kotlinx.html.table +import kotlinx.html.tbody +import kotlinx.html.td +import kotlinx.html.thead +import kotlinx.html.tr internal class ArscDiff( val oldArsc: Arsc, @@ -139,3 +150,122 @@ internal fun ArscDiff.toDetailReport() = buildString { appendComponentDiff("CONFIGS", Arsc::configs, configsAdded, configsRemoved) appendComponentDiff("ENTRIES", { it.entries.values }, entriesAdded, entriesRemoved) } + +internal fun FlowContent.toSummaryTable(diff: ArscDiff) { + table { + thead { + tr { + td { +"ARSC" } + td { +"old" } + td { +"new" } + td { colSpan = "2" + "diff" } + } + } + + tbody { + style = "text-align: right; vertical-align: center;" + + tr { + td { +"configs" } + td { +diff.oldArsc.configs.size } + td { +diff.newArsc.configs.size } + + val configsDelta = diff.configsAdded.size - diff.configsRemoved.size + td { + style = "border-right: none;" + +configsDelta.toDiffString() + } + + val delta = if (diff.configsAdded.isNotEmpty() || diff.configsRemoved.isNotEmpty()) { + val added = diff.configsAdded.size.toDiffString(zeroSign = '+') + val removed = (-diff.configsRemoved.size).toDiffString(zeroSign = '-') + "($added $removed)" + } else { + "" + } + + td { + style = "border-left: none; padding-left: 0; text-align: left; vertical-align: center;" + +delta + } + } + + tr { + td { +"entries" } + td { +diff.oldArsc.entries.size } + td { +diff.newArsc.entries.size } + + val entriesDelta = diff.entriesAdded.size - diff.entriesRemoved.size + td { + style = "border-right: none;" + +entriesDelta.toDiffString() + } + + val delta = if (diff.entriesAdded.isNotEmpty() || diff.entriesRemoved.isNotEmpty()) { + val added = diff.entriesAdded.size.toDiffString(zeroSign = '+') + val removed = (-diff.entriesRemoved.size).toDiffString(zeroSign = '-') + "($added $removed)" + } else { + "" + } + + td { + style = "border-left: none; padding-left: 0; text-align: left; vertical-align: center;" + +delta + } + } + } + } +} + +internal fun FlowContent.toDetailReport(diff: ArscDiff) { + fun FlowContent.appendComponentDiff( + name: String, + diff: ArscDiff, + componentSelector: (Arsc) -> Collection<*>, + ) { + if (diff.configsAdded.isNotEmpty() || diff.configsRemoved.isNotEmpty()) { + p { +"$name:" } + + div { + style = "margin-left: 16pt;" + + table { + thead { + tr { + td { +"old" } + td { +"new" } + td { +"diff" } + } + } + tbody { + val diffSize = (diff.configsAdded.size - diff.configsRemoved.size).toDiffString() + val addedSize = diff.configsAdded.size.toDiffString(zeroSign = '+') + val removedSize = (-diff.configsRemoved.size).toDiffString(zeroSign = '-') + tr { + td { +componentSelector(diff.oldArsc).size } + td { +componentSelector(diff.newArsc).size } + td { +"$diffSize ($addedSize $removedSize)" } + } + } + } + + diff.configsAdded.forEach { + span { +"+ $it" } + br() + } + if (diff.configsAdded.isNotEmpty() && diff.configsRemoved.isNotEmpty()) { + br() + br() + } + diff.configsRemoved.forEach { + span { +"- $it" } + br() + } + } + } + } + + appendComponentDiff("CONFIGS", diff, Arsc::configs) + appendComponentDiff("ENTRIES", diff) { it.entries.values } +} diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/DexDiff.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/DexDiff.kt index dd341b92..b14b8706 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/DexDiff.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/DexDiff.kt @@ -14,6 +14,13 @@ import com.jakewharton.picnic.TextAlignment.MiddleLeft import com.jakewharton.picnic.TextAlignment.MiddleRight import com.jakewharton.picnic.renderText import kotlinx.html.FlowContent +import kotlinx.html.TBODY +import kotlinx.html.style +import kotlinx.html.table +import kotlinx.html.tbody +import kotlinx.html.td +import kotlinx.html.thead +import kotlinx.html.tr internal class DexDiff( val oldDexes: List, @@ -132,6 +139,99 @@ internal fun DexDiff.toDetailReport() = buildString { appendComponentDiff("FIELDS", fields) } +internal fun FlowContent.toSummaryTable(dexDiff: DexDiff) { + table { + thead { + if (dexDiff.isMultidex) { + tr { + td { + style = "text-align: left; vertical-align: bottom;" + rowSpan = "2" + +"DEX" + } + + td { + style = "text-align: center; vertical-align: bottom;" + colSpan = "3" + +"raw" + } + td { + style = "text-align: center; vertical-align: bottom;" + colSpan = "4" + +"unique" + } + } + } + tr { + if (!dexDiff.isMultidex) { + td { +"DEX" } + } else { + td { +"old" } + td { +"new" } + td { +"diff" } + } + td { +"old" } + td { +"new" } + td { + colSpan = "2" + +"diff" + } + } + } + + tbody { + style = "text-align: right; vertical-align: center;" + + tr { + td { +"files" } + td { +dexDiff.oldDexes.size } + td { +dexDiff.newDexes.size } + td { + style = "border-right: none;" + +(dexDiff.newDexes.size - dexDiff.oldDexes.size).toDiffString() + } + if (dexDiff.isMultidex) { + // todo: necessary for html? + // Add empty cells to ensure borders get drawn + td { +"" } + td { +"" } + td { colSpan = "2" + "" } + } + } + + fun TBODY.addDexRow(name: String, diff: ComponentDiff<*>) { + tr { + td { +name } + if (dexDiff.isMultidex) { + td { +diff.oldRawCount } + td { +diff.newRawCount } + td { +(diff.newRawCount - diff.oldRawCount).toDiffString() } + } + td { +diff.oldCount } + td { +diff.newCount } + td { + style = "border-right: none;" + +(diff.added.size - diff.removed.size).toDiffString() + } + + val addedSize = diff.added.size.toDiffString(zeroSign = '+') + val removedSize = (-diff.removed.size).toDiffString(zeroSign = '-') + td { + style = "border-left: none; padding-left: 0; text-align: left; vertical-align: center;" + +"($addedSize $removedSize)" + } + } + } + + addDexRow("strings", dexDiff.strings) + addDexRow("types", dexDiff.types) + addDexRow("classes", dexDiff.classes) + addDexRow("methods", dexDiff.methods) + addDexRow("fields", dexDiff.fields) + } + } +} + internal fun FlowContent.toDetailReport(diff: DexDiff) { appendComponentDiff("STRINGS", diff.strings) appendComponentDiff("TYPES", diff.types) diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ManifestDiff.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ManifestDiff.kt index 042d7001..99c3d644 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ManifestDiff.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ManifestDiff.kt @@ -4,6 +4,14 @@ import com.github.difflib.DiffUtils import com.github.difflib.UnifiedDiffUtils import com.jakewharton.diffuse.diffuseTable import com.jakewharton.diffuse.format.AndroidManifest +import kotlinx.html.FlowContent +import kotlinx.html.br +import kotlinx.html.span +import kotlinx.html.table +import kotlinx.html.tbody +import kotlinx.html.td +import kotlinx.html.thead +import kotlinx.html.tr internal class ManifestDiff( val oldManifest: AndroidManifest, @@ -44,3 +52,44 @@ internal fun ManifestDiff.toDetailReport() = buildString { appendLine() } } + +internal fun FlowContent.toDetailReport(diff: ManifestDiff) { + if (diff.parsedPropertiesChanged) { + table { + thead { + tr { + td { +"" } + td { +"old" } + td { +"new" } + } + } + tbody { + tr { + td { +"package" } + td { +diff.oldManifest.packageName } + td { +diff.newManifest.packageName } + } + + tr { + td { +"version code" } + td { +diff.oldManifest.versionCode.toString() } + td { +diff.newManifest.versionCode.toString() } + } + + tr { + td { +"version name" } + td { +diff.oldManifest.versionName.toString() } + td { +diff.newManifest.versionName.toString() } + } + } + } + } + + if (diff.diff.isNotEmpty()) { + diff.diff.drop(2) // Skip file name headers + .forEach { + span { +it } + br() + } + } +} diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/SignaturesDiff.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/SignaturesDiff.kt index 124ffe55..57d9afdb 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/SignaturesDiff.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/SignaturesDiff.kt @@ -3,6 +3,12 @@ package com.jakewharton.diffuse.diff import com.jakewharton.diffuse.diffuseTable import com.jakewharton.diffuse.format.Signatures import com.jakewharton.picnic.TextAlignment.TopRight +import kotlinx.html.FlowContent +import kotlinx.html.style +import kotlinx.html.table +import kotlinx.html.td +import kotlinx.html.thead +import kotlinx.html.tr import okio.ByteString internal class SignaturesDiff( @@ -58,3 +64,63 @@ internal fun SignaturesDiff.toDetailReport() = buildString { }, ) } + +internal fun FlowContent.toDetailReport(diff: SignaturesDiff) { + table { + thead { + tr { + td { +"SIGNATURES" } + td { +"old" } + td { +"new" } + } + } + + if (diff.oldSignatures.v1.isNotEmpty() || diff.newSignatures.v1.isNotEmpty()) { + tr { + td { + style = "text-align: right; vertical-align: top;" + +"V1" + } + + td { +diff.oldSignatures.v1.joinToString("\n", transform = ByteString::hex) } + td { +diff.newSignatures.v1.joinToString("\n", transform = ByteString::hex) } + } + } + + if (diff.oldSignatures.v2.isNotEmpty() || diff.newSignatures.v2.isNotEmpty()) { + tr { + td { + style = "text-align: right; vertical-align: top;" + +"V2" + } + + td { +diff.oldSignatures.v2.joinToString("\n", transform = ByteString::hex) } + td { +diff.newSignatures.v2.joinToString("\n", transform = ByteString::hex) } + } + } + + if (diff.oldSignatures.v3.isNotEmpty() || diff.newSignatures.v3.isNotEmpty()) { + tr { + td { + style = "text-align: right; vertical-align: top;" + +"V3" + } + + td { +diff.oldSignatures.v3.joinToString("\n", transform = ByteString::hex) } + td { +diff.newSignatures.v3.joinToString("\n", transform = ByteString::hex) } + } + } + + if (diff.oldSignatures.v4.isNotEmpty() || diff.newSignatures.v4.isNotEmpty()) { + tr { + td { + style = "text-align: right; vertical-align: top;" + +"V4" + } + + td { +diff.oldSignatures.v4.joinToString("\n", transform = ByteString::hex) } + td { +diff.newSignatures.v4.joinToString("\n", transform = ByteString::hex) } + } + } + } +} diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkDiffHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkDiffHtmlReport.kt index 42506e6b..a93bcc57 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkDiffHtmlReport.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkDiffHtmlReport.kt @@ -1,8 +1,98 @@ package com.jakewharton.diffuse.report.html +import com.jakewharton.diffuse.diff.ApkDiff +import com.jakewharton.diffuse.diff.toDetailReport +import com.jakewharton.diffuse.diff.toSummaryTable +import com.jakewharton.diffuse.format.ArchiveFile.Type import com.jakewharton.diffuse.report.Report +import com.jakewharton.diffuse.report.toSummaryString +import kotlinx.html.body +import kotlinx.html.br +import kotlinx.html.details +import kotlinx.html.head +import kotlinx.html.html +import kotlinx.html.span +import kotlinx.html.stream.appendHTML +import kotlinx.html.style +import kotlinx.html.summary +import kotlinx.html.unsafe -class ApkDiffHtmlReport : Report { +internal class ApkDiffHtmlReport( + private val diff: ApkDiff, +) : Report { override fun write(appendable: Appendable) { + appendable.appendHTML().html { + head { + style(type = "text/css") { + unsafe { + raw( + """ + table{ + border-collapse:collapse; + border:1px solid #000; + } + + table td{ + border:1px solid #000; + } + """.trimIndent(), + ) + } + } + } + + body { + span { +"OLD: ${diff.oldApk.filename} (signature: ${diff.oldApk.signatures.toSummaryString()})" } + span { +"NEW: ${diff.newApk.filename} (signature: ${diff.newApk.signatures.toSummaryString()})" } + br() + br() + + toSummaryTable("APK", diff.archive, Type.APK_TYPES, skipIfEmptyTypes = setOf(Type.Native)) + br() + + toSummaryTable(diff.dex) + br() + + toSummaryTable(diff.arsc) + br() + + if (diff.archive.changed) { + details { + summary { +"Archive" } + toDetailReport(diff.archive) + } + } + + if (diff.signatures.changed) { + details { + summary { +"Signatures" } + toDetailReport(diff.signatures) + } + } + + if (diff.manifest.changed) { + details { + summary { +"Manifest" } + toDetailReport(diff.manifest) + } + } + + if (diff.dex.changed) { + details { + summary { +"Dex" } + toDetailReport(diff.dex) + } + } + + if (diff.arsc.changed) { + details { + summary { +"ARSC" } + toDetailReport(diff.arsc) + } + } + } + } } + + override fun toString() = buildString { write(this) } } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkInfoHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkInfoHtmlReport.kt index 6ee66b01..6203065e 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkInfoHtmlReport.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkInfoHtmlReport.kt @@ -14,7 +14,7 @@ import kotlinx.html.stream.appendHTML import kotlinx.html.style import kotlinx.html.unsafe -class ApkInfoHtmlReport( +internal class ApkInfoHtmlReport( private val apk: Apk, ) : Report { override fun write(appendable: Appendable) { From ab3508516085e1c160702504cdf6ef9b38ffdb56 Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Mon, 9 Sep 2024 08:49:18 -0400 Subject: [PATCH 06/10] Support aar info html reports --- .../com/jakewharton/diffuse/info/AarInfo.kt | 5 ++ .../diffuse/report/html/AarInfoHtmlReport.kt | 57 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AarInfoHtmlReport.kt diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/info/AarInfo.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/info/AarInfo.kt index 8d288bc8..26bdeb54 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/info/AarInfo.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/info/AarInfo.kt @@ -2,6 +2,7 @@ package com.jakewharton.diffuse.info import com.jakewharton.diffuse.format.Aar import com.jakewharton.diffuse.report.Report +import com.jakewharton.diffuse.report.html.AarInfoHtmlReport import com.jakewharton.diffuse.report.text.AarInfoTextReport class AarInfo( @@ -10,4 +11,8 @@ class AarInfo( override fun toTextReport(): Report { return AarInfoTextReport(aar) } + + override fun toHtmlReport(): Report { + return AarInfoHtmlReport(aar) + } } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AarInfoHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AarInfoHtmlReport.kt new file mode 100644 index 00000000..09797891 --- /dev/null +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AarInfoHtmlReport.kt @@ -0,0 +1,57 @@ +package com.jakewharton.diffuse.report.html + +import com.jakewharton.diffuse.format.Aar +import com.jakewharton.diffuse.format.ArchiveFile.Type +import com.jakewharton.diffuse.info.toSummaryTable +import com.jakewharton.diffuse.report.Report +import kotlinx.html.body +import kotlinx.html.h2 +import kotlinx.html.head +import kotlinx.html.html +import kotlinx.html.stream.appendHTML +import kotlinx.html.style +import kotlinx.html.unsafe + +class AarInfoHtmlReport( + private val aar: Aar, +) : Report { + override fun write(appendable: Appendable) { + appendable.appendHTML().html { + head { + style(type = "text/css") { + unsafe { + raw( + """ + table{ + border-collapse:collapse; + border:1px solid #000; + } + + table td{ + border:1px solid #000; + } + """.trimIndent(), + ) + } + } + } + + body { + appendable.apply { + h2 { +aar.filename.toString() } + + toSummaryTable( + "AAR", + aar.files, + Type.AAR_TYPES, + skipIfEmptyTypes = setOf(Type.JarLibs, Type.ApiJar, Type.LintJar, Type.Native, Type.Res), + ) + + toSummaryTable("JAR", aar.jars) + } + } + } + } + + override fun toString() = buildString { write(this) } +} From 6f0d070c70882762a2e1961c6225d8ce34f9626a Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Wed, 11 Sep 2024 20:31:59 -0400 Subject: [PATCH 07/10] Support aar diff html reports --- .../com/jakewharton/diffuse/diff/AarDiff.kt | 2 + .../diffuse/report/html/AarDiffHtmlReport.kt | 83 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AarDiffHtmlReport.kt diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/AarDiff.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/AarDiff.kt index 7a82a6a5..705a110c 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/AarDiff.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/AarDiff.kt @@ -3,6 +3,7 @@ package com.jakewharton.diffuse.diff import com.jakewharton.diffuse.format.Aar import com.jakewharton.diffuse.format.ApiMapping import com.jakewharton.diffuse.report.Report +import com.jakewharton.diffuse.report.html.AarDiffHtmlReport import com.jakewharton.diffuse.report.text.AarDiffTextReport internal class AarDiff( @@ -16,4 +17,5 @@ internal class AarDiff( val manifest = ManifestDiff(oldAar.manifest, newAar.manifest) override fun toTextReport(): Report = AarDiffTextReport(this) + override fun toHtmlReport(): Report = AarDiffHtmlReport(this) } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AarDiffHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AarDiffHtmlReport.kt new file mode 100644 index 00000000..bdeaa229 --- /dev/null +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AarDiffHtmlReport.kt @@ -0,0 +1,83 @@ +package com.jakewharton.diffuse.report.html + +import com.jakewharton.diffuse.diff.AarDiff +import com.jakewharton.diffuse.diff.toDetailReport +import com.jakewharton.diffuse.diff.toHtmlSummary +import com.jakewharton.diffuse.diff.toSummaryTable +import com.jakewharton.diffuse.format.ArchiveFile.Type +import com.jakewharton.diffuse.report.Report +import kotlinx.html.body +import kotlinx.html.br +import kotlinx.html.details +import kotlinx.html.h2 +import kotlinx.html.head +import kotlinx.html.html +import kotlinx.html.span +import kotlinx.html.stream.appendHTML +import kotlinx.html.style +import kotlinx.html.summary +import kotlinx.html.unsafe + +internal class AarDiffHtmlReport(private val aarDiff: AarDiff) : Report { + override fun write(appendable: Appendable) { + appendable.appendHTML().html { + head { + style(type = "text/css") { + unsafe { + raw( + """ + table{ + border-collapse:collapse; + border:1px solid #000; + } + + table td{ + border:1px solid #000; + } + """.trimIndent(), + ) + } + } + } + + body { + h2 { +"Summary" } + + span { +"OLD: ${aarDiff.oldAar.filename}" } + br() + span { +"NEW: ${aarDiff.newAar.filename}" } + br() + br() + + toSummaryTable( + "AAR", + aarDiff.archive, + Type.AAR_TYPES, + skipIfEmptyTypes = setOf(Type.JarLibs, Type.ApiJar, Type.LintJar, Type.Native, Type.Res), + ) + + br() + + toHtmlSummary("", aarDiff.jars) + + if (aarDiff.archive.changed) { + br() + details { + summary { +"JAR" } + toDetailReport(aarDiff.archive) + } + } + + if (aarDiff.jars.changed) { + br() + details { + summary { +"CLASSFILES" } + toDetailReport(aarDiff.jars) + } + } + } + } + } + + override fun toString() = buildString { write(this) } +} From ce0257e5b85f7397dc93a8cbbbe9dd4692e65779 Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Wed, 11 Sep 2024 21:46:50 -0400 Subject: [PATCH 08/10] Support aab html reports --- .../com/jakewharton/diffuse/diff/AabDiff.kt | 2 + .../com/jakewharton/diffuse/info/AabInfo.kt | 5 + .../com/jakewharton/diffuse/report/Report.kt | 4 +- .../diffuse/report/html/AabDiffHtmlReport.kt | 123 ++++++++++++++++++ .../diffuse/report/html/AabInfoHtmlReport.kt | 93 +++++++++++++ 5 files changed, 224 insertions(+), 3 deletions(-) create mode 100644 reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AabDiffHtmlReport.kt create mode 100644 reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AabInfoHtmlReport.kt diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/AabDiff.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/AabDiff.kt index 0d093730..7edadc2b 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/AabDiff.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/AabDiff.kt @@ -2,6 +2,7 @@ package com.jakewharton.diffuse.diff import com.jakewharton.diffuse.format.Aab import com.jakewharton.diffuse.report.Report +import com.jakewharton.diffuse.report.html.AabDiffHtmlReport import com.jakewharton.diffuse.report.text.AabDiffTextReport internal class AabDiff( @@ -33,4 +34,5 @@ internal class AabDiff( } override fun toTextReport(): Report = AabDiffTextReport(this) + override fun toHtmlReport(): Report = AabDiffHtmlReport(this) } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/info/AabInfo.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/info/AabInfo.kt index c07e5a06..98151467 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/info/AabInfo.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/info/AabInfo.kt @@ -3,6 +3,7 @@ package com.jakewharton.diffuse.info import com.jakewharton.diffuse.diff.BinaryDiff import com.jakewharton.diffuse.format.Aab import com.jakewharton.diffuse.report.Report +import com.jakewharton.diffuse.report.html.AabInfoHtmlReport import com.jakewharton.diffuse.report.text.AabInfoTextReport class AabInfo( @@ -11,4 +12,8 @@ class AabInfo( override fun toTextReport(): Report { return AabInfoTextReport(aab) } + + override fun toHtmlReport(): Report { + return AabInfoHtmlReport(aab) + } } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/Report.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/Report.kt index afe9a4f4..0a9cb5ba 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/Report.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/Report.kt @@ -5,8 +5,6 @@ interface Report { interface Factory { fun toTextReport(): Report - fun toHtmlReport(): Report { - TODO("Implement HTML reporting") - } + fun toHtmlReport(): Report } } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AabDiffHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AabDiffHtmlReport.kt new file mode 100644 index 00000000..b7113fad --- /dev/null +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AabDiffHtmlReport.kt @@ -0,0 +1,123 @@ +package com.jakewharton.diffuse.report.html + +import com.jakewharton.diffuse.diff.AabDiff +import com.jakewharton.diffuse.diff.toDetailReport +import com.jakewharton.diffuse.report.Report +import kotlinx.html.body +import kotlinx.html.br +import kotlinx.html.details +import kotlinx.html.h2 +import kotlinx.html.head +import kotlinx.html.html +import kotlinx.html.span +import kotlinx.html.stream.appendHTML +import kotlinx.html.style +import kotlinx.html.summary +import kotlinx.html.table +import kotlinx.html.td +import kotlinx.html.thead +import kotlinx.html.tr +import kotlinx.html.unsafe + +internal class AabDiffHtmlReport(private val aabDiff: AabDiff) : Report { + override fun write(appendable: Appendable) { + appendable.appendHTML().html { + head { + style(type = "text/css") { + unsafe { + raw( + """ + table{ + border-collapse:collapse; + border:1px solid #000; + } + + table td{ + border:1px solid #000; + } + """.trimIndent(), + ) + } + } + } + + body { + span { +"OLD: ${aabDiff.oldAab.filename}" } + span { +"NEW: ${aabDiff.newAab.filename}" } + br() + br() + + table { + style = "text-align: center; vertical-align: middle;" + + thead { + style = "text-align: left; vertical-align: bottom;" + tr { + td { +"MODULES" } + td { +"old" } + td { +"new" } + } + + tr { + td { + style = "text-align: right; vertical-align: center;" + +"base" + } + td { +"✓" } + td { +"✓" } + } + + for (name in aabDiff.featureModuleNames) { + tr { + td { + style = "text-align: right; vertical-align: center;" + +name + } + td { if (name in aabDiff.oldAab.featureModules) +"✓" else +"" } + td { if (name in aabDiff.newAab.featureModules) +"✓" else +"" } + } + } + } + } + + h2 { +"base" } + if (aabDiff.baseModule.archive.changed) { + toDetailReport(aabDiff.baseModule.archive) + } + if (aabDiff.baseModule.dex.changed) { + toDetailReport(aabDiff.baseModule.dex) + } + if (aabDiff.baseModule.manifest.changed) { + toDetailReport(aabDiff.baseModule.manifest) + } + + for (name in (aabDiff.featureModuleNames - aabDiff.removedFeatureModules.keys)) { + details { + summary { +name } + + val addedModule = aabDiff.addedFeatureModules[name] + val changedModule = aabDiff.changedFeatureModules[name] + assert((addedModule != null) xor (changedModule != null)) + + if (addedModule != null) { + // TODO + } + if (changedModule != null) { + if (changedModule.archive.changed) { + toDetailReport(changedModule.archive) + } + if (changedModule.dex.changed) { + toDetailReport(changedModule.dex) + } + if (changedModule.manifest.changed) { + toDetailReport(changedModule.manifest) + } + } + } + } + } + } + } + + override fun toString() = buildString { write(this) } +} diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AabInfoHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AabInfoHtmlReport.kt new file mode 100644 index 00000000..fe6b4023 --- /dev/null +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AabInfoHtmlReport.kt @@ -0,0 +1,93 @@ +package com.jakewharton.diffuse.report.html + +import com.jakewharton.diffuse.format.Aab +import com.jakewharton.diffuse.format.ArchiveFile +import com.jakewharton.diffuse.info.toSummaryTable +import com.jakewharton.diffuse.report.Report +import kotlinx.html.BODY +import kotlinx.html.body +import kotlinx.html.details +import kotlinx.html.head +import kotlinx.html.html +import kotlinx.html.p +import kotlinx.html.stream.appendHTML +import kotlinx.html.style +import kotlinx.html.summary +import kotlinx.html.table +import kotlinx.html.tbody +import kotlinx.html.td +import kotlinx.html.thead +import kotlinx.html.tr +import kotlinx.html.unsafe + +class AabInfoHtmlReport(private val aab: Aab) : Report { + override fun write(appendable: Appendable) { + appendable.appendHTML().html { + head { + style(type = "text/css") { + unsafe { + raw( + """ + table{ + border-collapse:collapse; + border:1px solid #000; + } + + table td{ + border:1px solid #000; + } + """.trimIndent(), + ) + } + } + } + + body { + p { +aab.filename.toString() } + + table { + thead { + tr { + td { +"MODULES" } + } + } + + tbody { + tr { + td { +"base" } + } + + for (name in aab.featureModules.keys) { + tr { td { +name } } + } + } + } + + appendModule("base", aab.baseModule) + + for ((name, module) in aab.featureModules) { + appendModule(name, module) + } + } + } + } + + override fun toString() = buildString { write(this) } + + private fun BODY.appendModule( + name: String, + module: Aab.Module, + ) { + details { + summary { +name } + toSummaryTable( + "AAB", + module.files, + ArchiveFile.Type.AAB_TYPES, + skipIfEmptyTypes = setOf(ArchiveFile.Type.Native), + ) + + toSummaryTable(module.dexes) + } + } +} From 8627068728fe4e7292babdd586361e7f42487449 Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Wed, 11 Sep 2024 22:13:40 -0400 Subject: [PATCH 09/10] Add bootstrap --- .../diffuse/diff/ArchiveFilesDiff.kt | 24 +++++++------- .../com/jakewharton/diffuse/diff/ArscDiff.kt | 6 ++-- .../com/jakewharton/diffuse/diff/DexDiff.kt | 4 +-- .../com/jakewharton/diffuse/info/ArscInfo.kt | 2 +- .../com/jakewharton/diffuse/info/DexesInfo.kt | 2 +- .../diffuse/report/html/AabDiffHtmlReport.kt | 24 ++------------ .../diffuse/report/html/AabInfoHtmlReport.kt | 21 +------------ .../diffuse/report/html/AarDiffHtmlReport.kt | 21 +------------ .../diffuse/report/html/AarInfoHtmlReport.kt | 21 +------------ .../diffuse/report/html/ApkDiffHtmlReport.kt | 21 +------------ .../diffuse/report/html/ApkInfoHtmlReport.kt | 21 +------------ .../diffuse/report/html/DexDiffHtmlReport.kt | 21 +------------ .../diffuse/report/html/DexInfoHtmlReport.kt | 21 +------------ .../diffuse/report/html/JarDiffHtmlReport.kt | 21 +------------ .../diffuse/report/html/JarInfoHtmlReport.kt | 21 +------------ .../jakewharton/diffuse/report/html/styles.kt | 31 +++++++++++++++++++ 16 files changed, 62 insertions(+), 220 deletions(-) create mode 100644 reports/src/main/kotlin/com/jakewharton/diffuse/report/html/styles.kt diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ArchiveFilesDiff.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ArchiveFilesDiff.kt index 8b97a093..61ffdafa 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ArchiveFilesDiff.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ArchiveFilesDiff.kt @@ -329,7 +329,7 @@ internal fun FlowContent.toSummaryTable( } tbody { - style = "text-align: right; vertical-align: center;" + style = "text-align: right; vertical-align: middle;" for (type in displayTypes) { tr { @@ -339,7 +339,7 @@ internal fun FlowContent.toSummaryTable( } tfoot { - style = "text-align: right; vertical-align: center;" + style = "text-align: right; vertical-align: middle;" tr { addArchiveRow("total") } } } @@ -351,12 +351,12 @@ internal fun FlowContent.toDetailReport(diff: ArchiveFilesDiff) { if (diff.includeCompressed) { tr { td { - style = "text-align: center; vertical-align: center;" + style = "text-align: center; vertical-align: middle;" colSpan = "2" +"compressed" } td { - style = "text-align: center; vertical-align: center;" + style = "text-align: center; vertical-align: middle;" colSpan = "2" +"uncompressed" } @@ -390,22 +390,22 @@ internal fun FlowContent.toDetailReport(diff: ArchiveFilesDiff) { val totalSize = diff.changes.fold(Size.ZERO) { acc, change -> acc + change.size } val totalDiff = diff.changes.fold(Size.ZERO) { acc, change -> acc + change.sizeDiff } td { - style = "text-align: right; vertical-align: center;" + style = "text-align: right; vertical-align: middle;" +totalSize.toString() } td { - style = "text-align: right; vertical-align: center;" + style = "text-align: right; vertical-align: middle;" +totalDiff.toDiffString() } } val totalUncompressedSize = diff.changes.fold(Size.ZERO) { acc, change -> acc + change.uncompressedSize } val totalUncompressedDiff = diff.changes.fold(Size.ZERO) { acc, change -> acc + change.uncompressedSizeDiff } td { - style = "text-align: right; vertical-align: center;" + style = "text-align: right; vertical-align: middle;" +totalUncompressedSize.toString() } td { - style = "text-align: right; vertical-align: center;" + style = "text-align: right; vertical-align: middle;" +totalUncompressedDiff.toDiffString() } td { +"(total)" } @@ -420,20 +420,20 @@ internal fun FlowContent.toDetailReport(diff: ArchiveFilesDiff) { tr { if (diff.includeCompressed) { td { - style = "text-align: right; vertical-align: center;" + style = "text-align: right; vertical-align: middle;" if (type != Change.Type.Removed) +size.toString() else +"" } td { - style = "text-align: right; vertical-align: center;" + style = "text-align: right; vertical-align: middle;" +sizeDiff.toDiffString() } } td { - style = "text-align: right; vertical-align: center;" + style = "text-align: right; vertical-align: middle;" if (type != Change.Type.Removed) +uncompressedSize.toString() else +"" } td { - style = "text-align: right; vertical-align: center;" + style = "text-align: right; vertical-align: middle;" +uncompressedSizeDiff.toDiffString() } td { unsafe { raw("$typeChar $path".htmlEncoded) } } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ArscDiff.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ArscDiff.kt index 57339cc6..fc633069 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ArscDiff.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ArscDiff.kt @@ -163,7 +163,7 @@ internal fun FlowContent.toSummaryTable(diff: ArscDiff) { } tbody { - style = "text-align: right; vertical-align: center;" + style = "text-align: right; vertical-align: middle;" tr { td { +"configs" } @@ -185,7 +185,7 @@ internal fun FlowContent.toSummaryTable(diff: ArscDiff) { } td { - style = "border-left: none; padding-left: 0; text-align: left; vertical-align: center;" + style = "border-left: none; padding-left: 0; text-align: left; vertical-align: middle;" +delta } } @@ -210,7 +210,7 @@ internal fun FlowContent.toSummaryTable(diff: ArscDiff) { } td { - style = "border-left: none; padding-left: 0; text-align: left; vertical-align: center;" + style = "border-left: none; padding-left: 0; text-align: left; vertical-align: middle;" +delta } } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/DexDiff.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/DexDiff.kt index b14b8706..ed95b8df 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/DexDiff.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/DexDiff.kt @@ -180,7 +180,7 @@ internal fun FlowContent.toSummaryTable(dexDiff: DexDiff) { } tbody { - style = "text-align: right; vertical-align: center;" + style = "text-align: right; vertical-align: middle;" tr { td { +"files" } @@ -217,7 +217,7 @@ internal fun FlowContent.toSummaryTable(dexDiff: DexDiff) { val addedSize = diff.added.size.toDiffString(zeroSign = '+') val removedSize = (-diff.removed.size).toDiffString(zeroSign = '-') td { - style = "border-left: none; padding-left: 0; text-align: left; vertical-align: center;" + style = "border-left: none; padding-left: 0; text-align: left; vertical-align: middle;" +"($addedSize $removedSize)" } } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/info/ArscInfo.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/info/ArscInfo.kt index 77e449c1..8394d17d 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/info/ArscInfo.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/info/ArscInfo.kt @@ -37,7 +37,7 @@ internal fun FlowContent.toSummaryTable(arsc: Arsc) { } tbody { - style = "text-align: right; vertical-align: center;" + style = "text-align: right; vertical-align: middle;" tr { td { +"configs" } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/info/DexesInfo.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/info/DexesInfo.kt index ee43db14..359e158c 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/info/DexesInfo.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/info/DexesInfo.kt @@ -83,7 +83,7 @@ internal fun FlowContent.toSummaryTable(dexList: List) { } tbody { - style = "text-align: right; vertical-align: center;" + style = "text-align: right; vertical-align: middle;" tr { td { +"files" } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AabDiffHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AabDiffHtmlReport.kt index b7113fad..e6fcb6bd 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AabDiffHtmlReport.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AabDiffHtmlReport.kt @@ -17,29 +17,11 @@ import kotlinx.html.table import kotlinx.html.td import kotlinx.html.thead import kotlinx.html.tr -import kotlinx.html.unsafe internal class AabDiffHtmlReport(private val aabDiff: AabDiff) : Report { override fun write(appendable: Appendable) { appendable.appendHTML().html { - head { - style(type = "text/css") { - unsafe { - raw( - """ - table{ - border-collapse:collapse; - border:1px solid #000; - } - - table td{ - border:1px solid #000; - } - """.trimIndent(), - ) - } - } - } + head { applyStyles() } body { span { +"OLD: ${aabDiff.oldAab.filename}" } @@ -60,7 +42,7 @@ internal class AabDiffHtmlReport(private val aabDiff: AabDiff) : Report { tr { td { - style = "text-align: right; vertical-align: center;" + style = "text-align: right; vertical-align: middle;" +"base" } td { +"✓" } @@ -70,7 +52,7 @@ internal class AabDiffHtmlReport(private val aabDiff: AabDiff) : Report { for (name in aabDiff.featureModuleNames) { tr { td { - style = "text-align: right; vertical-align: center;" + style = "text-align: right; vertical-align: middle;" +name } td { if (name in aabDiff.oldAab.featureModules) +"✓" else +"" } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AabInfoHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AabInfoHtmlReport.kt index fe6b4023..1a5518a3 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AabInfoHtmlReport.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AabInfoHtmlReport.kt @@ -11,36 +11,17 @@ import kotlinx.html.head import kotlinx.html.html import kotlinx.html.p import kotlinx.html.stream.appendHTML -import kotlinx.html.style import kotlinx.html.summary import kotlinx.html.table import kotlinx.html.tbody import kotlinx.html.td import kotlinx.html.thead import kotlinx.html.tr -import kotlinx.html.unsafe class AabInfoHtmlReport(private val aab: Aab) : Report { override fun write(appendable: Appendable) { appendable.appendHTML().html { - head { - style(type = "text/css") { - unsafe { - raw( - """ - table{ - border-collapse:collapse; - border:1px solid #000; - } - - table td{ - border:1px solid #000; - } - """.trimIndent(), - ) - } - } - } + head { applyStyles() } body { p { +aab.filename.toString() } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AarDiffHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AarDiffHtmlReport.kt index bdeaa229..e79e631d 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AarDiffHtmlReport.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AarDiffHtmlReport.kt @@ -14,31 +14,12 @@ import kotlinx.html.head import kotlinx.html.html import kotlinx.html.span import kotlinx.html.stream.appendHTML -import kotlinx.html.style import kotlinx.html.summary -import kotlinx.html.unsafe internal class AarDiffHtmlReport(private val aarDiff: AarDiff) : Report { override fun write(appendable: Appendable) { appendable.appendHTML().html { - head { - style(type = "text/css") { - unsafe { - raw( - """ - table{ - border-collapse:collapse; - border:1px solid #000; - } - - table td{ - border:1px solid #000; - } - """.trimIndent(), - ) - } - } - } + head { applyStyles() } body { h2 { +"Summary" } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AarInfoHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AarInfoHtmlReport.kt index 09797891..5f9feb3c 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AarInfoHtmlReport.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AarInfoHtmlReport.kt @@ -9,32 +9,13 @@ import kotlinx.html.h2 import kotlinx.html.head import kotlinx.html.html import kotlinx.html.stream.appendHTML -import kotlinx.html.style -import kotlinx.html.unsafe class AarInfoHtmlReport( private val aar: Aar, ) : Report { override fun write(appendable: Appendable) { appendable.appendHTML().html { - head { - style(type = "text/css") { - unsafe { - raw( - """ - table{ - border-collapse:collapse; - border:1px solid #000; - } - - table td{ - border:1px solid #000; - } - """.trimIndent(), - ) - } - } - } + head { applyStyles() } body { appendable.apply { diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkDiffHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkDiffHtmlReport.kt index a93bcc57..16c326c2 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkDiffHtmlReport.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkDiffHtmlReport.kt @@ -13,33 +13,14 @@ import kotlinx.html.head import kotlinx.html.html import kotlinx.html.span import kotlinx.html.stream.appendHTML -import kotlinx.html.style import kotlinx.html.summary -import kotlinx.html.unsafe internal class ApkDiffHtmlReport( private val diff: ApkDiff, ) : Report { override fun write(appendable: Appendable) { appendable.appendHTML().html { - head { - style(type = "text/css") { - unsafe { - raw( - """ - table{ - border-collapse:collapse; - border:1px solid #000; - } - - table td{ - border:1px solid #000; - } - """.trimIndent(), - ) - } - } - } + head { applyStyles() } body { span { +"OLD: ${diff.oldApk.filename} (signature: ${diff.oldApk.signatures.toSummaryString()})" } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkInfoHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkInfoHtmlReport.kt index 6203065e..dbe5999d 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkInfoHtmlReport.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkInfoHtmlReport.kt @@ -11,32 +11,13 @@ import kotlinx.html.head import kotlinx.html.html import kotlinx.html.p import kotlinx.html.stream.appendHTML -import kotlinx.html.style -import kotlinx.html.unsafe internal class ApkInfoHtmlReport( private val apk: Apk, ) : Report { override fun write(appendable: Appendable) { appendable.appendHTML().html { - head { - style(type = "text/css") { - unsafe { - raw( - """ - table{ - border-collapse:collapse; - border:1px solid #000; - } - - table td{ - border:1px solid #000; - } - """.trimIndent(), - ) - } - } - } + head { applyStyles() } body { p { +"${apk.filename} (signature: ${apk.signatures.toSummaryString()})" } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexDiffHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexDiffHtmlReport.kt index eba90c85..096d68a8 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexDiffHtmlReport.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexDiffHtmlReport.kt @@ -9,8 +9,6 @@ import kotlinx.html.head import kotlinx.html.html import kotlinx.html.span import kotlinx.html.stream.appendHTML -import kotlinx.html.style -import kotlinx.html.unsafe internal class DexDiffHtmlReport(private val dexDiff: DexDiff) : Report { private val oldDex = requireNotNull(dexDiff.oldDexes.singleOrNull()) { @@ -22,24 +20,7 @@ internal class DexDiffHtmlReport(private val dexDiff: DexDiff) : Report { override fun write(appendable: Appendable) { appendable.appendHTML().html { - head { - style(type = "text/css") { - unsafe { - raw( - """ - table{ - border-collapse:collapse; - border:1px solid #000; - } - - table td{ - border:1px solid #000; - } - """.trimIndent(), - ) - } - } - } + head { applyStyles() } body { span { +"OLD: ${oldDex.filename}" } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexInfoHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexInfoHtmlReport.kt index d8d15c7d..962c1d71 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexInfoHtmlReport.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexInfoHtmlReport.kt @@ -8,30 +8,11 @@ import kotlinx.html.h2 import kotlinx.html.head import kotlinx.html.html import kotlinx.html.stream.appendHTML -import kotlinx.html.style -import kotlinx.html.unsafe internal class DexInfoHtmlReport(private val dex: Dex) : Report { override fun write(appendable: Appendable) { appendable.appendHTML().html { - head { - style(type = "text/css") { - unsafe { - raw( - """ - table{ - border-collapse:collapse; - border:1px solid #000; - } - - table td{ - border:1px solid #000; - } - """.trimIndent(), - ) - } - } - } + head { applyStyles() } body { h2 { +dex.filename } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/JarDiffHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/JarDiffHtmlReport.kt index 2d11b2ae..15f3a5a4 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/JarDiffHtmlReport.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/JarDiffHtmlReport.kt @@ -14,31 +14,12 @@ import kotlinx.html.head import kotlinx.html.html import kotlinx.html.span import kotlinx.html.stream.appendHTML -import kotlinx.html.style import kotlinx.html.summary -import kotlinx.html.unsafe internal class JarDiffHtmlReport(private val jarDiff: JarDiff) : Report { override fun write(appendable: Appendable) { appendable.appendHTML().html { - head { - style(type = "text/css") { - unsafe { - raw( - """ - table{ - border-collapse:collapse; - border:1px solid #000; - } - - table td{ - border:1px solid #000; - } - """.trimIndent(), - ) - } - } - } + head { applyStyles() } body { h2 { +"Summary" } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/JarInfoHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/JarInfoHtmlReport.kt index 9bef9a37..38270b2d 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/JarInfoHtmlReport.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/JarInfoHtmlReport.kt @@ -11,31 +11,12 @@ import kotlinx.html.head import kotlinx.html.html import kotlinx.html.p import kotlinx.html.stream.appendHTML -import kotlinx.html.style import kotlinx.html.summary -import kotlinx.html.unsafe internal class JarInfoHtmlReport(private val jar: Jar) : Report { override fun write(appendable: Appendable) { appendable.appendHTML().html { - head { - style(type = "text/css") { - unsafe { - raw( - """ - table{ - border-collapse:collapse; - border:1px solid #000; - } - - table td{ - border:1px solid #000; - } - """.trimIndent(), - ) - } - } - } + head { applyStyles() } body { p { +jar.filename!! } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/styles.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/styles.kt new file mode 100644 index 00000000..74d0a042 --- /dev/null +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/styles.kt @@ -0,0 +1,31 @@ +package com.jakewharton.diffuse.report.html + +import kotlinx.html.HEAD +import kotlinx.html.link +import kotlinx.html.style +import kotlinx.html.unsafe + +internal fun HEAD.applyStyles() { + link("https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css", "stylesheet") { + integrity = "sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" + attributes["crossorigin"] = "anonymous" + } + + style("text/css") { + unsafe { + raw( + """ + table{ + border-collapse:collapse; + border:1px solid var(--bs-body-color); + } + + table td{ + border:1px solid var(--bs-body-color); + padding: 4px; + } + """.trimIndent(), + ) + } + } +} From 992ac3749e775308943abbcd6eee24fca122c04e Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Wed, 11 Sep 2024 22:30:11 -0400 Subject: [PATCH 10/10] Tweak styles and layout --- .../jakewharton/diffuse/diff/ComponentDiff.kt | 42 ++++++++++++------- .../diffuse/report/html/AabDiffHtmlReport.kt | 1 - .../diffuse/report/html/AarDiffHtmlReport.kt | 4 -- .../diffuse/report/html/ApkDiffHtmlReport.kt | 1 - .../diffuse/report/html/DexDiffHtmlReport.kt | 1 - .../diffuse/report/html/JarDiffHtmlReport.kt | 3 -- .../jakewharton/diffuse/report/html/styles.kt | 22 ++++++---- 7 files changed, 39 insertions(+), 35 deletions(-) diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ComponentDiff.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ComponentDiff.kt index da2ff817..70816496 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ComponentDiff.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ComponentDiff.kt @@ -6,10 +6,12 @@ import com.jakewharton.diffuse.report.toDiffString import com.jakewharton.picnic.renderText import kotlinx.html.FlowContent import kotlinx.html.br +import kotlinx.html.details import kotlinx.html.div -import kotlinx.html.h3 +import kotlinx.html.h4 import kotlinx.html.span import kotlinx.html.style +import kotlinx.html.summary import kotlinx.html.table import kotlinx.html.tbody import kotlinx.html.td @@ -87,10 +89,10 @@ internal fun StringBuilder.appendComponentDiff(name: String, diff: ComponentDiff internal fun FlowContent.appendComponentDiff(name: String, diff: ComponentDiff<*>) { if (diff.changed) { - h3 { +"$name:" } - div { - style = "margin-left: 16pt;" + style = "margin: 24px 0;" + + h4 { +name } table { thead { @@ -113,19 +115,27 @@ internal fun FlowContent.appendComponentDiff(name: String, diff: ComponentDiff<* } } - if (diff.added.isNotEmpty()) { - br() - diff.added.forEach { - span { unsafe { raw("+ $it".htmlEncoded) } } - br() - } - } + details { + summary { +"diff" } + + div { + style = "margin-left: 16pt;" - if (diff.removed.isNotEmpty()) { - br() - diff.removed.forEach { - span { unsafe { raw("- $it".htmlEncoded) } } - br() + if (diff.added.isNotEmpty()) { + br() + diff.added.forEach { + span { unsafe { raw("+ $it".htmlEncoded) } } + br() + } + } + + if (diff.removed.isNotEmpty()) { + br() + diff.removed.forEach { + span { unsafe { raw("- $it".htmlEncoded) } } + br() + } + } } } } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AabDiffHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AabDiffHtmlReport.kt index e6fcb6bd..08647768 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AabDiffHtmlReport.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AabDiffHtmlReport.kt @@ -27,7 +27,6 @@ internal class AabDiffHtmlReport(private val aabDiff: AabDiff) : Report { span { +"OLD: ${aabDiff.oldAab.filename}" } span { +"NEW: ${aabDiff.newAab.filename}" } br() - br() table { style = "text-align: center; vertical-align: middle;" diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AarDiffHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AarDiffHtmlReport.kt index e79e631d..22a5dd46 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AarDiffHtmlReport.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/AarDiffHtmlReport.kt @@ -9,7 +9,6 @@ import com.jakewharton.diffuse.report.Report import kotlinx.html.body import kotlinx.html.br import kotlinx.html.details -import kotlinx.html.h2 import kotlinx.html.head import kotlinx.html.html import kotlinx.html.span @@ -22,13 +21,10 @@ internal class AarDiffHtmlReport(private val aarDiff: AarDiff) : Report { head { applyStyles() } body { - h2 { +"Summary" } - span { +"OLD: ${aarDiff.oldAar.filename}" } br() span { +"NEW: ${aarDiff.newAar.filename}" } br() - br() toSummaryTable( "AAR", diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkDiffHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkDiffHtmlReport.kt index 16c326c2..4012db3d 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkDiffHtmlReport.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/ApkDiffHtmlReport.kt @@ -26,7 +26,6 @@ internal class ApkDiffHtmlReport( span { +"OLD: ${diff.oldApk.filename} (signature: ${diff.oldApk.signatures.toSummaryString()})" } span { +"NEW: ${diff.newApk.filename} (signature: ${diff.newApk.signatures.toSummaryString()})" } br() - br() toSummaryTable("APK", diff.archive, Type.APK_TYPES, skipIfEmptyTypes = setOf(Type.Native)) br() diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexDiffHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexDiffHtmlReport.kt index 096d68a8..3a88512c 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexDiffHtmlReport.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/DexDiffHtmlReport.kt @@ -27,7 +27,6 @@ internal class DexDiffHtmlReport(private val dexDiff: DexDiff) : Report { br() span { +"NEW: ${newDex.filename}" } br() - br() toDetailReport(dexDiff) } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/JarDiffHtmlReport.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/JarDiffHtmlReport.kt index 15f3a5a4..4dd421d4 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/JarDiffHtmlReport.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/JarDiffHtmlReport.kt @@ -9,7 +9,6 @@ import com.jakewharton.diffuse.report.Report import kotlinx.html.body import kotlinx.html.br import kotlinx.html.details -import kotlinx.html.h2 import kotlinx.html.head import kotlinx.html.html import kotlinx.html.span @@ -22,8 +21,6 @@ internal class JarDiffHtmlReport(private val jarDiff: JarDiff) : Report { head { applyStyles() } body { - h2 { +"Summary" } - span { +"OLD: ${jarDiff.oldJar.filename}" } br() span { +"NEW: ${jarDiff.newJar.filename}" } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/styles.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/styles.kt index 74d0a042..2e3e46c5 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/styles.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/html/styles.kt @@ -15,15 +15,19 @@ internal fun HEAD.applyStyles() { unsafe { raw( """ - table{ - border-collapse:collapse; - border:1px solid var(--bs-body-color); - } - - table td{ - border:1px solid var(--bs-body-color); - padding: 4px; - } + table { + border-collapse:collapse; + border:1px solid var(--bs-body-color); + } + + table td { + border:1px solid var(--bs-body-color); + padding: 4px; + } + + body { + margin: 0 16pt; + } """.trimIndent(), ) }