diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-08-24 07:54:01 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-08-24 07:54:01 +0000 |
| commit | da942624a4abd82797f563cf8c57f1124c5de435 (patch) | |
| tree | f7e4325a4783a77544e6ed1f0ac44ea844757fe8 /app | |
| parent | b299d57d6cc240381aed51f91e5a26f1e633d78d (diff) | |
Add TDD quality gate: detekt, coverage tracking, pre-commit hook; fix theme crash risk
CLAUDE.md now documents mandatory red-green-refactor TDD, a per-commit
quality gate (build, tests, lint, detekt, coverage-non-decrease,
WebView-JS justification, manifest permission diff, secret scan), and
why each check exists (tracing back to real bugs this audit found).
Wires up detekt (static analysis) and a standalone Jacoco setup
(scoped to data/readability/opml/work/stash -- ui/ Compose screens
are impractical to unit-test for coverage) via app/build.gradle.kts.
Bumped Kotlin/KSP to 2.0.21/2.0.21-1.0.28 to match detekt 1.23.8's
compatibility requirement.
Also fixes the themes.xml bug the lint gate immediately caught:
Theme.FeedReader's parent required API 29 while minSdk is 26 -- a
real crash risk on Android 8.0-9.0 devices. Swapped to the plain
(non-DayNight) DeviceDefault parent, since Compose already owns all
real theming via a hardcoded darkColorScheme() regardless of system
day/night state.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017GMEkHeqKz6FLkmizowBTK
Diffstat (limited to 'app')
| -rw-r--r-- | app/build.gradle.kts | 55 | ||||
| -rw-r--r-- | app/src/main/res/values/themes.xml | 6 |
2 files changed, 60 insertions, 1 deletions
diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 32313ab..03b240e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,6 +4,8 @@ plugins { id("org.jetbrains.kotlin.plugin.serialization") id("org.jetbrains.kotlin.plugin.compose") id("com.google.devtools.ksp") + id("io.gitlab.arturbosch.detekt") + id("jacoco") } android { @@ -19,6 +21,9 @@ android { } buildTypes { + debug { + enableUnitTestCoverage = true + } release { isMinifyEnabled = false } @@ -70,3 +75,53 @@ dependencies { testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.0") testImplementation("androidx.room:room-testing:2.6.1") } + +detekt { + buildUponDefaultConfig = true + allRules = false + config.setFrom(files("$rootDir/config/detekt/detekt.yml")) + baseline = file("$rootDir/config/detekt/baseline.xml") +} + +tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach { + reports { + xml.required.set(true) + html.required.set(true) + txt.required.set(false) + sarif.required.set(false) + } +} + +// JVM unit-test line coverage over testable business logic. Compose screens under ui/ are +// excluded -- they're impractical to unit-test for coverage and would make the metric +// meaningless (see CLAUDE.md). Room- and KSP-generated code is excluded too, since coverage +// there measures generated boilerplate, not anything we wrote. +tasks.register<JacocoReport>("jacocoTestReport") { + dependsOn("testDebugUnitTest") + group = "verification" + description = "Generates a Jacoco coverage report for the debug unit tests." + + reports { + xml.required.set(true) + html.required.set(true) + } + + val fileFilter = listOf( + "**/R.class", "**/R$*.class", "**/BuildConfig.*", "**/Manifest*.*", + "android/**/*.*", + "**/ui/**", + "**/*_Impl.class", "**/*_Impl$*.class", + "**/di/**" + ) + val kotlinDebugTree = fileTree("${layout.buildDirectory.get()}/tmp/kotlin-classes/debug") { + exclude(fileFilter) + } + val javaDebugTree = fileTree("${layout.buildDirectory.get()}/intermediates/javac/debug/classes") { + exclude(fileFilter) + } + classDirectories.setFrom(files(kotlinDebugTree, javaDebugTree)) + sourceDirectories.setFrom(files("${project.projectDir}/src/main/java")) + executionData.setFrom(fileTree(layout.buildDirectory.get()) { + include("outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec", "jacoco/testDebugUnitTest.exec") + }) +} diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 9c5f0ff..b1eb502 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,6 +1,10 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <style name="Theme.FeedReader" parent="android:Theme.DeviceDefault.DayNight"> + <!-- DayNight requires API 29 (minSdk here is 26) and buys nothing anyway: Compose owns all + real theming via darkColorScheme() regardless of system day/night state (see + MainActivity.kt), so this base window theme only ever needs to supply window chrome + (no title/action bar), not day/night-aware colors. --> + <style name="Theme.FeedReader" parent="android:Theme.DeviceDefault"> <item name="android:windowActionBar">false</item> <item name="android:windowNoTitle">true</item> </style> |
