From 88a720b4bbc3668dae672945c4f0203669c7dce9 Mon Sep 17 00:00:00 2001 From: ShiftHackZ Date: Thu, 22 Aug 2024 11:22:59 +0300 Subject: [PATCH 1/2] Implemented multi-module Jacoco Unit Test code coverage reporting --- gradle/common.gradle | 86 +++++++++++++++++++ .../detail/GalleryDetailViewModelTest.kt | 12 +-- 2 files changed, 90 insertions(+), 8 deletions(-) diff --git a/gradle/common.gradle b/gradle/common.gradle index da416508..269a8d2c 100755 --- a/gradle/common.gradle +++ b/gradle/common.gradle @@ -1,6 +1,19 @@ +apply plugin: 'jacoco' + apply from: "$project.rootDir/gradle/ci.gradle" apply from: "$project.rootDir/dependencies.gradle" +tasks.withType(Test).configureEach { + jacoco { + includeNoLocationClasses = true + excludes = ['jdk.internal.*'] + } +} + +afterEvaluate { + jacocoCodeCoverageReporting() +} + android { compileSdk rootProject.ext.targetSdk @@ -12,16 +25,89 @@ android { } buildTypes { + debug { + enableUnitTestCoverage = true + enableAndroidTestCoverage = false + } release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + compileOptions { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } + kotlinOptions { jvmTarget = '17' } } + +def jacocoCodeCoverageReporting() { + def exclusions = [ + '**/R.class', + '**/R\$*.class', + '**/BuildConfig.*', + '**/Manifest*.*', + '**/*Test*.*' + ] + + def buildTypes = android.buildTypes.collect { type -> + type.name + } + def productFlavors = android.productFlavors.collect { flavor -> + flavor.name + } + + if (!productFlavors) productFlavors.add('') + + productFlavors.each { productFlavorName -> + buildTypes.each { buildTypeName -> + def sourceName, sourcePath + if (!productFlavorName) { + sourceName = sourcePath = "${buildTypeName}" + } else { + sourceName = "${productFlavorName}${buildTypeName.capitalize()}" + sourcePath = "${productFlavorName}/${buildTypeName}" + } + + def testTaskName = "test${sourceName.capitalize()}UnitTest" + def jacocoTaskName = "jacoco${testTaskName.capitalize()}Coverage" + + System.out.println("[Jacoco] Task -> ${project.displayName.replace('\'', '')}:${jacocoTaskName}") + + tasks.register(jacocoTaskName, JacocoReport) { + dependsOn([testTaskName]) + + group = "Reporting" + description = "Generate Jacoco coverage reports on the ${sourceName.capitalize()} build." + + reports { + xml.required = true + html.required = true + } + + sourceDirectories.setFrom(layout.projectDirectory.dir("src/main")) + + classDirectories.setFrom( + files( + fileTree(layout.buildDirectory.dir("intermediates/javac/")) { + exclude(exclusions) + }, + fileTree(layout.buildDirectory.dir("tmp/kotlin-classes/")) { + exclude(exclusions) + } + ) + ) + + executionData.setFrom( + files( + fileTree(layout.buildDirectory) { include("**/*.exec", "**/*.ec") } + ) + ) + } + } + } +} diff --git a/presentation/src/test/java/com/shifthackz/aisdv1/presentation/screen/gallery/detail/GalleryDetailViewModelTest.kt b/presentation/src/test/java/com/shifthackz/aisdv1/presentation/screen/gallery/detail/GalleryDetailViewModelTest.kt index 3d0efcb6..a1621725 100644 --- a/presentation/src/test/java/com/shifthackz/aisdv1/presentation/screen/gallery/detail/GalleryDetailViewModelTest.kt +++ b/presentation/src/test/java/com/shifthackz/aisdv1/presentation/screen/gallery/detail/GalleryDetailViewModelTest.kt @@ -19,14 +19,13 @@ import com.shifthackz.aisdv1.presentation.navigation.router.main.MainRouter import com.shifthackz.aisdv1.presentation.stub.stubSchedulersProvider import io.mockk.every import io.mockk.mockk -import io.mockk.unmockkAll import io.mockk.verify import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.core.Single import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest -import org.junit.After import org.junit.Assert import org.junit.Before import org.junit.Test @@ -73,12 +72,6 @@ class GalleryDetailViewModelTest : CoreViewModelTest() { } returns Single.just(Base64ToBitmapConverter.Output(stubBitmap)) } - @After - override fun finalize() { - super.finalize() - unmockkAll() - } - @Test fun `initialized, loaded ai generation result, expected UI state is Content`() { val expected = GalleryDetailState.Content( @@ -109,6 +102,7 @@ class GalleryDetailViewModelTest : CoreViewModelTest() { fun `given received CopyToClipboard intent, expected ShareClipBoard effect delivered to effect collector`() { viewModel.processIntent(GalleryDetailIntent.CopyToClipboard("text")) runTest { + advanceUntilIdle() val expected = GalleryDetailEffect.ShareClipBoard("text") val actual = viewModel.effect.firstOrNull() Assert.assertEquals(expected, actual) @@ -153,6 +147,7 @@ class GalleryDetailViewModelTest : CoreViewModelTest() { viewModel.processIntent(GalleryDetailIntent.Export.Image) runTest { + advanceUntilIdle() viewModel.effect.test { Assert.assertEquals(GalleryDetailEffect.ShareImageFile(stubFile), awaitItem()) } @@ -166,6 +161,7 @@ class GalleryDetailViewModelTest : CoreViewModelTest() { fun `given received Export Params intent, expected ShareGenerationParams effect delivered to effect collector`() { viewModel.processIntent(GalleryDetailIntent.Export.Params) runTest { + advanceUntilIdle() val expected = GalleryDetailEffect.ShareGenerationParams(viewModel.state.value) val actual = viewModel.effect.firstOrNull() Assert.assertEquals(expected, actual) From 1f6323e83f125159e40ab34014acb1baae885138 Mon Sep 17 00:00:00 2001 From: ShiftHackZ Date: Thu, 22 Aug 2024 11:30:24 +0300 Subject: [PATCH 2/2] Add reporting configuration --- gradle/common.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gradle/common.gradle b/gradle/common.gradle index 269a8d2c..c9718672 100755 --- a/gradle/common.gradle +++ b/gradle/common.gradle @@ -85,7 +85,8 @@ def jacocoCodeCoverageReporting() { description = "Generate Jacoco coverage reports on the ${sourceName.capitalize()} build." reports { - xml.required = true + xml.required = false + csv.required = false html.required = true }