From f39bf587ea070a462104a38175765ee997c5cdde Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Mon, 30 Jun 2025 21:19:39 +0300 Subject: [PATCH 01/30] add gitHub action for test coverage check --- .github/workflows/ci.yml | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..5c53ef91b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,42 @@ +name: CI - Test Coverage Check + +on: + pull_request: + branches: + - development + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up JDK + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + - name: Grant execute permission for Gradlew + run: chmod +x ./gradlew + + - name: Run tests with coverage + run: ./gradlew test jacocoTestReport + + - name: Check if coverage is >= 80% + run: | + MISSED=$(grep -oPm1 "(?<= Date: Mon, 30 Jun 2025 21:20:05 +0300 Subject: [PATCH 02/30] add JaCoCo for code coverage analysis --- app/build.gradle.kts | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5a9d35f7c..e315bf22c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -2,6 +2,7 @@ plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.compose) + id("jacoco") } android { @@ -37,6 +38,14 @@ android { buildFeatures { compose = true } + jacoco { + the().toolVersion = "0.8.10" + } + + tasks.withType().configureEach { + finalizedBy("jacocoTestReport") + } + } dependencies { @@ -56,4 +65,32 @@ dependencies { androidTestImplementation(libs.androidx.ui.test.junit4) debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) -} \ No newline at end of file +} +tasks.register("jacocoTestReport") { + dependsOn("testDebugUnitTest") + + reports { + xml.required.set(true) + html.required.set(true) + } + + val fileFilter = listOf( + "**/R.class", "**/R$*.class", + "**/BuildConfig.*", + "**/Manifest*.*", + "**/*Test*.*", + "android/**/*.*" + ) + + val kotlinDebugTree = fileTree("${buildDir}/tmp/kotlin-classes/debug") { + exclude(fileFilter) + } + + val mainSrc = "$projectDir/src/main/java" + + classDirectories.setFrom(files(kotlinDebugTree)) + sourceDirectories.setFrom(files(mainSrc)) + executionData.setFrom(fileTree(buildDir) { + include("jacoco/testDebugUnitTest.exec") + }) +} From 175a20b592439f3e0d1215e5ed39aa32f73e6c25 Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Mon, 30 Jun 2025 21:38:28 +0300 Subject: [PATCH 03/30] rename build job to test-coverage in ci workflow --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c53ef91b..f784757cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: - development jobs: - build: + test-coverage: runs-on: ubuntu-latest steps: From 3a07510cd80a4573cd208d726de9001bff90dff4 Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Mon, 30 Jun 2025 22:12:18 +0300 Subject: [PATCH 04/30] disable XML report for test coverage --- app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e315bf22c..1f418e2dc 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -70,7 +70,7 @@ tasks.register("jacocoTestReport") { dependsOn("testDebugUnitTest") reports { - xml.required.set(true) + xml.required.set(false) html.required.set(true) } From c19078c72b054e050f1a72ff68cf1f6e2a9e5742 Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Mon, 30 Jun 2025 22:19:47 +0300 Subject: [PATCH 05/30] fix Jacoco report path for github actions --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f784757cf..49b355232 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,8 +27,8 @@ jobs: - name: Check if coverage is >= 80% run: | - MISSED=$(grep -oPm1 "(?<= Date: Mon, 30 Jun 2025 22:20:13 +0300 Subject: [PATCH 06/30] enable XML report for JaCoCo test coverage --- app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1f418e2dc..e315bf22c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -70,7 +70,7 @@ tasks.register("jacocoTestReport") { dependsOn("testDebugUnitTest") reports { - xml.required.set(false) + xml.required.set(true) html.required.set(true) } From 97be819d5a7ad8636668421306ca12e420cc18cc Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Mon, 30 Jun 2025 22:30:12 +0300 Subject: [PATCH 07/30] verify test tasks in ci workflow --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 49b355232..527526c65 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,8 @@ jobs: run: chmod +x ./gradlew - name: Run tests with coverage - run: ./gradlew test jacocoTestReport + run: ./gradlew :app:testDebugUnitTest :app:jacocoTestReport + - name: Check if coverage is >= 80% run: | From 80ed24c302b11409f265b4edb928ab7ab8a892ab Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Mon, 30 Jun 2025 22:30:31 +0300 Subject: [PATCH 08/30] add JacocoCoverageVerification for code coverage --- app/build.gradle.kts | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e315bf22c..26454b250 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -69,10 +69,35 @@ dependencies { tasks.register("jacocoTestReport") { dependsOn("testDebugUnitTest") + val fileFilter = listOf( + "**/R.class", "**/R$*.class", + "**/BuildConfig.*", + "**/Manifest*.*", + "**/*Test*.*", + "android/**/*.*" + ) + + val debugTree = fileTree("${buildDir}/tmp/kotlin-classes/debug") { + exclude(fileFilter) + } + + val mainSrc = "$projectDir/src/main/java" + + classDirectories.setFrom(files(debugTree)) + sourceDirectories.setFrom(files(mainSrc)) + executionData.setFrom(fileTree(buildDir) { + include("jacoco/testDebugUnitTest.exec") + }) + reports { xml.required.set(true) + xml.outputLocation.set(file("${buildDir}/reports/jacoco/jacocoTestReport/jacocoTestReport.xml")) html.required.set(true) } +} + +tasks.register("jacocoTestCoverageVerification") { + dependsOn("jacocoTestReport") val fileFilter = listOf( "**/R.class", "**/R$*.class", @@ -82,15 +107,23 @@ tasks.register("jacocoTestReport") { "android/**/*.*" ) - val kotlinDebugTree = fileTree("${buildDir}/tmp/kotlin-classes/debug") { + val debugTree = fileTree("${buildDir}/tmp/kotlin-classes/debug") { exclude(fileFilter) } val mainSrc = "$projectDir/src/main/java" - classDirectories.setFrom(files(kotlinDebugTree)) + classDirectories.setFrom(files(debugTree)) sourceDirectories.setFrom(files(mainSrc)) executionData.setFrom(fileTree(buildDir) { include("jacoco/testDebugUnitTest.exec") }) -} + + violationRules { + rule { + limit { + minimum = "0.80".toBigDecimal() + } + } + } +} \ No newline at end of file From 33041f9519e4b0a1511717facc76b02d25746cc6 Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Mon, 30 Jun 2025 22:56:45 +0300 Subject: [PATCH 09/30] update GitHub workflow Java version to 22 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 527526c65..5d237e5f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v3 with: - java-version: '17' + java-version: '22' distribution: 'temurin' - name: Grant execute permission for Gradlew From fbcb0ea216c1599462003ae48e970b3a105625cb Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Mon, 30 Jun 2025 22:57:20 +0300 Subject: [PATCH 10/30] Update Java version to 22 and Jacoco version --- app/build.gradle.kts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 26454b250..1b34e4906 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -29,17 +29,17 @@ android { } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_22 + targetCompatibility = JavaVersion.VERSION_22 } kotlinOptions { - jvmTarget = "11" + jvmTarget = "22" } buildFeatures { compose = true } jacoco { - the().toolVersion = "0.8.10" + the().toolVersion = "0.8.12" } tasks.withType().configureEach { @@ -74,10 +74,11 @@ tasks.register("jacocoTestReport") { "**/BuildConfig.*", "**/Manifest*.*", "**/*Test*.*", - "android/**/*.*" + "android/**/*.*", + "sun/security/smartcardio/**" ) - val debugTree = fileTree("${buildDir}/tmp/kotlin-classes/debug") { + val debugTree = fileTree("${layout.buildDirectory.get()}/tmp/kotlin-classes/debug") { exclude(fileFilter) } @@ -85,13 +86,13 @@ tasks.register("jacocoTestReport") { classDirectories.setFrom(files(debugTree)) sourceDirectories.setFrom(files(mainSrc)) - executionData.setFrom(fileTree(buildDir) { + executionData.setFrom(fileTree(layout.buildDirectory.get()) { include("jacoco/testDebugUnitTest.exec") }) reports { xml.required.set(true) - xml.outputLocation.set(file("${buildDir}/reports/jacoco/jacocoTestReport/jacocoTestReport.xml")) + xml.outputLocation.set(file("${layout.buildDirectory.get()}/reports/jacoco/jacocoTestReport/jacocoTestReport.xml")) html.required.set(true) } } @@ -104,10 +105,11 @@ tasks.register("jacocoTestCoverageVerification") { "**/BuildConfig.*", "**/Manifest*.*", "**/*Test*.*", - "android/**/*.*" + "android/**/*.*", + "sun/security/smartcardio/**" ) - val debugTree = fileTree("${buildDir}/tmp/kotlin-classes/debug") { + val debugTree = fileTree("${layout.buildDirectory.get()}/tmp/kotlin-classes/debug") { exclude(fileFilter) } @@ -115,7 +117,7 @@ tasks.register("jacocoTestCoverageVerification") { classDirectories.setFrom(files(debugTree)) sourceDirectories.setFrom(files(mainSrc)) - executionData.setFrom(fileTree(buildDir) { + executionData.setFrom(fileTree(layout.buildDirectory.get()) { include("jacoco/testDebugUnitTest.exec") }) From 44698efb5ca841ef74091395a607a3cb5f3dc225 Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Mon, 30 Jun 2025 23:41:06 +0300 Subject: [PATCH 11/30] update excludes in app/build.gradle.kts --- app/build.gradle.kts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1b34e4906..911123882 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -75,7 +75,13 @@ tasks.register("jacocoTestReport") { "**/Manifest*.*", "**/*Test*.*", "android/**/*.*", - "sun/security/smartcardio/**" + "sun/security/smartcardio/**", + "**/MainActivity.*", + "**/MainActivity\$*.*", + "**/*Activity.*", + "**/*\$WhenMappings.*", + "**/ui/theme/**", + "**/ComposableSingletons*.*" ) val debugTree = fileTree("${layout.buildDirectory.get()}/tmp/kotlin-classes/debug") { @@ -106,7 +112,13 @@ tasks.register("jacocoTestCoverageVerification") { "**/Manifest*.*", "**/*Test*.*", "android/**/*.*", - "sun/security/smartcardio/**" + "sun/security/smartcardio/**", + "**/MainActivity.*", + "**/MainActivity\$*.*", + "**/*Activity.*", + "**/*\$WhenMappings.*", + "**/ui/theme/**", + "**/ComposableSingletons*.*" ) val debugTree = fileTree("${layout.buildDirectory.get()}/tmp/kotlin-classes/debug") { From e25199884d3f6e56d2acca67822a5ade5ed806b4 Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Tue, 1 Jul 2025 02:00:17 +0300 Subject: [PATCH 12/30] return Java version to 11 --- app/build.gradle.kts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 911123882..fdc28089d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -27,13 +27,16 @@ android { "proguard-rules.pro" ) } + debug { + enableUnitTestCoverage = true + } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_22 - targetCompatibility = JavaVersion.VERSION_22 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } kotlinOptions { - jvmTarget = "22" + jvmTarget = "11" } buildFeatures { compose = true @@ -140,4 +143,4 @@ tasks.register("jacocoTestCoverageVerification") { } } } -} \ No newline at end of file +} From 4d6b1d37424baa601e5636a9e76b829c690f6b51 Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Tue, 1 Jul 2025 02:03:32 +0300 Subject: [PATCH 13/30] rename ci workflow file --- .github/workflows/{ci.yml => ci_test_coverage.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{ci.yml => ci_test_coverage.yml} (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci_test_coverage.yml similarity index 100% rename from .github/workflows/ci.yml rename to .github/workflows/ci_test_coverage.yml From 1826fc7b6ab93aa18ce168d8b680f4794fcdadba Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Tue, 1 Jul 2025 02:03:47 +0300 Subject: [PATCH 14/30] remove JaCoCo from debug build type --- app/build.gradle.kts | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index fdc28089d..227173de6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -27,9 +27,6 @@ android { "proguard-rules.pro" ) } - debug { - enableUnitTestCoverage = true - } } compileOptions { sourceCompatibility = JavaVersion.VERSION_11 From 37d623fbc72dd8ad90c262a362064dc2c8869d95 Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Tue, 1 Jul 2025 17:13:08 +0300 Subject: [PATCH 15/30] simplify and remove duplication in Jacoco coverage check step --- .github/workflows/ci_test_coverage.yml | 38 ++++++++++++++++---------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci_test_coverage.yml b/.github/workflows/ci_test_coverage.yml index 5d237e5f4..e3701e633 100644 --- a/.github/workflows/ci_test_coverage.yml +++ b/.github/workflows/ci_test_coverage.yml @@ -9,6 +9,9 @@ jobs: test-coverage: runs-on: ubuntu-latest + env: + COVERAGE_THRESHOLD: 80 + steps: - name: Checkout code uses: actions/checkout@v3 @@ -16,28 +19,35 @@ jobs: - name: Set up JDK uses: actions/setup-java@v3 with: - java-version: '22' + java-version: '11' distribution: 'temurin' - - name: Grant execute permission for Gradlew + - name: Grant permission for Gradle run: chmod +x ./gradlew - - name: Run tests with coverage + - name: Run Unit Tests with Coverage run: ./gradlew :app:testDebugUnitTest :app:jacocoTestReport - - name: Check if coverage is >= 80% + - name: Parse & Evaluate Line Coverage Report run: | - MISSED=$(grep -oPm1 "(?<= Date: Tue, 1 Jul 2025 17:13:33 +0300 Subject: [PATCH 16/30] refactor Jacoco file filter for reuse --- app/build.gradle.kts | 45 ++++++++++++++------------------------------ 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 227173de6..bac0012fc 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -66,24 +66,23 @@ dependencies { debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) } +val fileFilter = listOf( + "**/R.class", "**/R$*.class", + "**/BuildConfig.*", + "**/Manifest*.*", + "**/*Test*.*", + "android/**/*.*", + "sun/security/smartcardio/**", + "**/MainActivity.*", + "**/MainActivity\$*.*", + "**/*Activity.*", + "**/*\$WhenMappings.*", + "**/ui/theme/**", + "**/ComposableSingletons*.*" +) tasks.register("jacocoTestReport") { dependsOn("testDebugUnitTest") - val fileFilter = listOf( - "**/R.class", "**/R$*.class", - "**/BuildConfig.*", - "**/Manifest*.*", - "**/*Test*.*", - "android/**/*.*", - "sun/security/smartcardio/**", - "**/MainActivity.*", - "**/MainActivity\$*.*", - "**/*Activity.*", - "**/*\$WhenMappings.*", - "**/ui/theme/**", - "**/ComposableSingletons*.*" - ) - val debugTree = fileTree("${layout.buildDirectory.get()}/tmp/kotlin-classes/debug") { exclude(fileFilter) } @@ -102,25 +101,9 @@ tasks.register("jacocoTestReport") { html.required.set(true) } } - tasks.register("jacocoTestCoverageVerification") { dependsOn("jacocoTestReport") - val fileFilter = listOf( - "**/R.class", "**/R$*.class", - "**/BuildConfig.*", - "**/Manifest*.*", - "**/*Test*.*", - "android/**/*.*", - "sun/security/smartcardio/**", - "**/MainActivity.*", - "**/MainActivity\$*.*", - "**/*Activity.*", - "**/*\$WhenMappings.*", - "**/ui/theme/**", - "**/ComposableSingletons*.*" - ) - val debugTree = fileTree("${layout.buildDirectory.get()}/tmp/kotlin-classes/debug") { exclude(fileFilter) } From 623343ebc8b257d2d400e1acd6cac258fdb76017 Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Tue, 1 Jul 2025 19:02:10 +0300 Subject: [PATCH 17/30] integrate Codecov for test coverage reporting --- .github/workflows/ci_test_coverage.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci_test_coverage.yml b/.github/workflows/ci_test_coverage.yml index e3701e633..0c384248b 100644 --- a/.github/workflows/ci_test_coverage.yml +++ b/.github/workflows/ci_test_coverage.yml @@ -51,3 +51,11 @@ jobs: else echo "Coverage passed with ${PERCENT}%" fi + + - name: Upload Coverage to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: app/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml + flags: unittests + name: novix-coverage-report From 6026827555063b30e98235c7a89bc91f063b7738 Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Tue, 1 Jul 2025 19:07:06 +0300 Subject: [PATCH 18/30] update Java version for test coverage workflow --- .github/workflows/ci_test_coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_test_coverage.yml b/.github/workflows/ci_test_coverage.yml index 0c384248b..2b43220bd 100644 --- a/.github/workflows/ci_test_coverage.yml +++ b/.github/workflows/ci_test_coverage.yml @@ -19,7 +19,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v3 with: - java-version: '11' + java-version: '22' distribution: 'temurin' - name: Grant permission for Gradle From 509cab3f3aee21a9154201875efed5419edf854e Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Tue, 1 Jul 2025 21:39:30 +0300 Subject: [PATCH 19/30] remove Greeting composable and its preview --- .../main/java/com/sanaa/novix/MainActivity.kt | 25 ++----------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/sanaa/novix/MainActivity.kt b/app/src/main/java/com/sanaa/novix/MainActivity.kt index 632765359..b5c0a3b0a 100644 --- a/app/src/main/java/com/sanaa/novix/MainActivity.kt +++ b/app/src/main/java/com/sanaa/novix/MainActivity.kt @@ -18,30 +18,9 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { - NovixTheme { - Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> - Greeting( - name = "Android", - modifier = Modifier.padding(innerPadding) - ) - } + } - } - } -} -@Composable -fun Greeting(name: String, modifier: Modifier = Modifier) { - Text( - text = "Hello $name!", - modifier = modifier - ) + } } -@Preview(showBackground = true) -@Composable -fun GreetingPreview() { - NovixTheme { - Greeting("Android") - } -} \ No newline at end of file From d839394e9c407cc4f1b318f2125903928288d6ea Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Wed, 2 Jul 2025 00:09:54 +0300 Subject: [PATCH 20/30] replace JaCoCo with Kover for code coverage --- app/build.gradle.kts | 70 ++------------------------------------------ build.gradle.kts | 31 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 68 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index bac0012fc..30ed9b90a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -2,7 +2,7 @@ plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.compose) - id("jacoco") + alias(libs.plugins.kover) } android { @@ -38,18 +38,10 @@ android { buildFeatures { compose = true } - jacoco { - the().toolVersion = "0.8.12" - } - - tasks.withType().configureEach { - finalizedBy("jacocoTestReport") - } } dependencies { - implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.activity.compose) @@ -65,62 +57,4 @@ dependencies { androidTestImplementation(libs.androidx.ui.test.junit4) debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) -} -val fileFilter = listOf( - "**/R.class", "**/R$*.class", - "**/BuildConfig.*", - "**/Manifest*.*", - "**/*Test*.*", - "android/**/*.*", - "sun/security/smartcardio/**", - "**/MainActivity.*", - "**/MainActivity\$*.*", - "**/*Activity.*", - "**/*\$WhenMappings.*", - "**/ui/theme/**", - "**/ComposableSingletons*.*" -) -tasks.register("jacocoTestReport") { - dependsOn("testDebugUnitTest") - - val debugTree = fileTree("${layout.buildDirectory.get()}/tmp/kotlin-classes/debug") { - exclude(fileFilter) - } - - val mainSrc = "$projectDir/src/main/java" - - classDirectories.setFrom(files(debugTree)) - sourceDirectories.setFrom(files(mainSrc)) - executionData.setFrom(fileTree(layout.buildDirectory.get()) { - include("jacoco/testDebugUnitTest.exec") - }) - - reports { - xml.required.set(true) - xml.outputLocation.set(file("${layout.buildDirectory.get()}/reports/jacoco/jacocoTestReport/jacocoTestReport.xml")) - html.required.set(true) - } -} -tasks.register("jacocoTestCoverageVerification") { - dependsOn("jacocoTestReport") - - val debugTree = fileTree("${layout.buildDirectory.get()}/tmp/kotlin-classes/debug") { - exclude(fileFilter) - } - - val mainSrc = "$projectDir/src/main/java" - - classDirectories.setFrom(files(debugTree)) - sourceDirectories.setFrom(files(mainSrc)) - executionData.setFrom(fileTree(layout.buildDirectory.get()) { - include("jacoco/testDebugUnitTest.exec") - }) - - violationRules { - rule { - limit { - minimum = "0.80".toBigDecimal() - } - } - } -} +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 952b93066..662d63e8c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,4 +3,35 @@ plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.kotlin.compose) apply false + alias(libs.plugins.kover) +} +subprojects { + apply(plugin = "org.jetbrains.kotlinx.kover") + kover { + reports { + filters { + excludes { + classes( + "*.R", + "*.R_*", + "*.BuildConfig*", + "*.Manifest*", + "com.sanaa.novix.ui.theme.*", + "*.ComposableSingletons*", + "*.MainActivity*" + ) + } + } + + + + verify { + rule { + bound { + minValue = 80 + } + } + } + } + } } \ No newline at end of file From d34e8a9135d814a757725c1db6b8d6cafdcde87a Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Wed, 2 Jul 2025 00:10:10 +0300 Subject: [PATCH 21/30] add kover to libs.versions.toml --- gradle/libs.versions.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f21f561d8..3adaa1a9b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ espressoCore = "3.6.1" lifecycleRuntimeKtx = "2.9.1" activityCompose = "1.10.1" composeBom = "2024.09.00" - +kover = "0.9.1" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } junit = { group = "junit", name = "junit", version.ref = "junit" } @@ -29,4 +29,5 @@ androidx-material3 = { group = "androidx.compose.material3", name = "material3" android-application = { id = "com.android.application", version.ref = "agp" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } +kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" } From fe4b88a257a37ffe651cdd5fd43a8bb0fdfad8f5 Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Wed, 2 Jul 2025 00:12:51 +0300 Subject: [PATCH 22/30] replaced Jacoco with Kover and automated coverage check using koverVerify --- .github/workflows/ci_test_coverage.yml | 32 ++++---------------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ci_test_coverage.yml b/.github/workflows/ci_test_coverage.yml index 2b43220bd..f50dd1abb 100644 --- a/.github/workflows/ci_test_coverage.yml +++ b/.github/workflows/ci_test_coverage.yml @@ -25,37 +25,13 @@ jobs: - name: Grant permission for Gradle run: chmod +x ./gradlew - - name: Run Unit Tests with Coverage - run: ./gradlew :app:testDebugUnitTest :app:jacocoTestReport - - - - name: Parse & Evaluate Line Coverage Report - run: | - COVERAGE_XML="app/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml" - MISSED=$(grep -oPm1 "(?<= Date: Thu, 3 Jul 2025 21:52:46 +0300 Subject: [PATCH 23/30] use all project instead of sub project --- build.gradle.kts | 68 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 662d63e8c..17fba122f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,7 @@ plugins { alias(libs.plugins.kotlin.compose) apply false alias(libs.plugins.kover) } -subprojects { +allprojects { apply(plugin = "org.jetbrains.kotlinx.kover") kover { reports { @@ -22,16 +22,66 @@ subprojects { ) } } - - - - verify { - rule { - bound { - minValue = 80 + total { + verify { + rule { + bound { + minValue = 80 + } } } } } } -} \ No newline at end of file +} +/* +dependencies { + kover(projects.domain.authentication.repository) + kover(projects.domain.movies.repository) + kover(projects.domain.actors.repository) + kover(projects.domain.series) + kover(projects.domain.savedContent.repository) + kover(projects.domain.search.repository) + kover(projects.data.search.repository) + kover(projects.data.savedContent.repository) + kover(projects.data.userProfile.repository) + kover(projects.data.remoteDataSource) + kover(projects.data.localDataSource) + kover(projects.data.actors.dataSource) + kover(projects.data.actors.repository) + kover(projects.data.authentication.dataSource.local) + kover(projects.data.movies.dataSource.remote) + kover(projects.data.savedContent.dataSource.remote) + kover(projects.data.search.dataSource.remote) + kover(projects.data.series.dataSource.remote) + kover(projects.data.savedContent.dataSource.remote) + kover(projects.data.actors.dataSource.local) + kover(projects.data.authentication.dataSource.local) + kover(projects.data.movies.dataSource.local) + kover(projects.data.savedContent.dataSource.local) + kover(projects.data.search.dataSource.local) + kover(projects.data.series.dataSource.local) + kover(projects.data.userProfile.dataSource.local) + kover(projects.app) + kover(projects.domain) + kover(projects.feature.authentication.presentation) + kover(projects.feature.authentication.api) + kover(projects.feature.home.api) + kover(projects.feature.home.presentation) + kover(projects.feature.userProfile.api) + kover(projects.feature.userProfile.presentation) + kover(projects.feature.savedContent.api) + kover(projects.feature.savedContent.presentation) + kover(projects.feature.playlists.api) + kover(projects.feature.playlists.presentation) + kover(projects.feature.search.api) + kover(projects.feature.search.presentation) + kover(projects.feature.mediaDetails.api) + kover(projects.feature.mediaDetails.presentation) + kover(projects.feature.onboarding.api) + kover(projects.feature.onboarding.presentation) + kover(projects.data.authentication.repository) + kover(projects.data.movies.repository) + kover(projects.data.series.repository) + kover(projects.data.search.repository) +}*/ \ No newline at end of file From 0456e61b50397c605d1d583c219335652d0f3b97 Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Thu, 3 Jul 2025 21:54:15 +0300 Subject: [PATCH 24/30] use v4 instead of v3 , cache gradle,add main branch and use java 17 --- .github/workflows/ci_test_coverage.yml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci_test_coverage.yml b/.github/workflows/ci_test_coverage.yml index f50dd1abb..c4c135875 100644 --- a/.github/workflows/ci_test_coverage.yml +++ b/.github/workflows/ci_test_coverage.yml @@ -1,37 +1,35 @@ + name: CI - Test Coverage Check on: pull_request: - branches: - - development + branches: [main, development] jobs: test-coverage: runs-on: ubuntu-latest - env: - COVERAGE_THRESHOLD: 80 - steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: - java-version: '22' + java-version: '17' distribution: 'temurin' + cache: 'gradle' - name: Grant permission for Gradle run: chmod +x ./gradlew - name: Run Unit Tests with Kover Coverage - run: ./gradlew :app:testDebugUnitTest :app:koverVerify :app:koverXmlReport :app:koverHtmlReport + run: ./gradlew test koverVerify koverXmlReport koverHtmlReport - name: Upload Coverage to Codecov uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} - files: app/build/reports/kover/report.xml + files: build/reports/kover/merged/report.xml flags: unittests name: novix-coverage-report \ No newline at end of file From 403bba55874986c58ce0072705f3f59103119e75 Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Thu, 3 Jul 2025 22:01:08 +0300 Subject: [PATCH 25/30] use v4 instead of v3 , cache gradle,add main branch and use java 17 --- .github/workflows/ci_test_coverage.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci_test_coverage.yml b/.github/workflows/ci_test_coverage.yml index c4c135875..085c3ea5f 100644 --- a/.github/workflows/ci_test_coverage.yml +++ b/.github/workflows/ci_test_coverage.yml @@ -9,6 +9,7 @@ jobs: test-coverage: runs-on: ubuntu-latest + steps: - name: Checkout code uses: actions/checkout@v4 From ebc7da0cdd25617b4f57ca984358717c401b7feb Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Thu, 3 Jul 2025 22:04:23 +0300 Subject: [PATCH 26/30] use v4 instead of v3 , cache gradle,add main branch and use java 17 --- app/build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 30ed9b90a..9ae834b2e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -2,7 +2,6 @@ plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.compose) - alias(libs.plugins.kover) } android { From 3ef0fe5d3cccba710103d8f6c2cae62fe7ea0a93 Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Thu, 3 Jul 2025 22:04:29 +0300 Subject: [PATCH 27/30] use v4 instead of v3 , cache gradle,add main branch and use java 17 --- .github/workflows/ci_test_coverage.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci_test_coverage.yml b/.github/workflows/ci_test_coverage.yml index 085c3ea5f..ef8ede05b 100644 --- a/.github/workflows/ci_test_coverage.yml +++ b/.github/workflows/ci_test_coverage.yml @@ -1,4 +1,3 @@ - name: CI - Test Coverage Check on: From 5c7fd674a4b9c7108f81a661e449f4cbcf59b455 Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Thu, 3 Jul 2025 22:07:46 +0300 Subject: [PATCH 28/30] use v4 instead of v3 , cache gradle,add main branch and use java 17 --- .github/workflows/ci_test_coverage.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci_test_coverage.yml b/.github/workflows/ci_test_coverage.yml index ef8ede05b..aefaa886a 100644 --- a/.github/workflows/ci_test_coverage.yml +++ b/.github/workflows/ci_test_coverage.yml @@ -8,7 +8,6 @@ jobs: test-coverage: runs-on: ubuntu-latest - steps: - name: Checkout code uses: actions/checkout@v4 From d8e075cd99b98a5fa7609b7c975105273ad22886 Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Thu, 3 Jul 2025 22:11:30 +0300 Subject: [PATCH 29/30] refactor kover excluded packages in build.gradle.kts --- build.gradle.kts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 17fba122f..66f5c8504 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,6 +5,15 @@ plugins { alias(libs.plugins.kotlin.compose) apply false alias(libs.plugins.kover) } +val excludedPackages = listOf( + "*.R", + "*.R_*", + "*.BuildConfig*", + "*.Manifest*", + "com.sanaa.novix.ui.theme.*", + "*.ComposableSingletons*", + "*.MainActivity*" +) allprojects { apply(plugin = "org.jetbrains.kotlinx.kover") kover { @@ -12,13 +21,7 @@ allprojects { filters { excludes { classes( - "*.R", - "*.R_*", - "*.BuildConfig*", - "*.Manifest*", - "com.sanaa.novix.ui.theme.*", - "*.ComposableSingletons*", - "*.MainActivity*" + excludedPackages ) } } From bf3175c2ad4a4dcbf955a83c05c36f3026b636d1 Mon Sep 17 00:00:00 2001 From: Radwa85 Date: Fri, 4 Jul 2025 01:50:30 +0300 Subject: [PATCH 30/30] enable typesafe project accessors --- build.gradle.kts | 55 +++++++++++++++++++++------------------------ settings.gradle.kts | 2 +- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3b3a13cd1..4f52b6bc0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -40,38 +40,39 @@ allprojects { } } } -/* + dependencies { + kover(projects.app) kover(projects.domain.authentication.repository) kover(projects.domain.movies.repository) kover(projects.domain.actors.repository) kover(projects.domain.series) kover(projects.domain.savedContent.repository) kover(projects.domain.search.repository) - kover(projects.data.search.repository) - kover(projects.data.savedContent.repository) - kover(projects.data.userProfile.repository) - kover(projects.data.remoteDataSource) - kover(projects.data.localDataSource) - kover(projects.data.actors.dataSource) - kover(projects.data.actors.repository) - kover(projects.data.authentication.dataSource.local) - kover(projects.data.movies.dataSource.remote) - kover(projects.data.savedContent.dataSource.remote) - kover(projects.data.search.dataSource.remote) - kover(projects.data.series.dataSource.remote) - kover(projects.data.savedContent.dataSource.remote) - kover(projects.data.actors.dataSource.local) - kover(projects.data.authentication.dataSource.local) - kover(projects.data.movies.dataSource.local) - kover(projects.data.savedContent.dataSource.local) - kover(projects.data.search.dataSource.local) - kover(projects.data.series.dataSource.local) - kover(projects.data.userProfile.dataSource.local) - kover(projects.app) - kover(projects.domain) - kover(projects.feature.authentication.presentation) + kover(projects.domain.userProfile.repository) + kover(projects.data.remoteDataSource.actors) + kover(projects.data.remoteDataSource.authentication) + kover(projects.data.remoteDataSource.movies) + kover(projects.data.remoteDataSource.savedContent) + kover(projects.data.remoteDataSource.search) + kover(projects.data.remoteDataSource.series) + kover(projects.data.remoteDataSource.userProfile) + kover(projects.data.localDataSource.actors) + kover(projects.data.localDataSource.authentication) + kover(projects.data.localDataSource.movies) + kover(projects.data.localDataSource.savedContent) + kover(projects.data.localDataSource.search) + kover(projects.data.localDataSource.series) + kover(projects.data.localDataSource.userProfile) + kover(projects.data.repositories.actors) + kover(projects.data.repositories.authentication) + kover(projects.data.repositories.movies) + kover(projects.data.repositories.savedContent) + kover(projects.data.repositories.search) + kover(projects.data.repositories.series) + kover(projects.data.repositories.userProfile) kover(projects.feature.authentication.api) + kover(projects.feature.authentication.presentation) kover(projects.feature.home.api) kover(projects.feature.home.presentation) kover(projects.feature.userProfile.api) @@ -86,8 +87,4 @@ dependencies { kover(projects.feature.mediaDetails.presentation) kover(projects.feature.onboarding.api) kover(projects.feature.onboarding.presentation) - kover(projects.data.authentication.repository) - kover(projects.data.movies.repository) - kover(projects.data.series.repository) - kover(projects.data.search.repository) -}*/ \ No newline at end of file +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 2d6c069fb..4108789e3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,7 +18,7 @@ dependencyResolutionManagement { mavenCentral() } } - +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") rootProject.name = "Novix" include(":app") include(":feature:authentication:presentation")