From ccc67dbcb7ee044b6c7ff19cdb545fb6a33f29da Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Wed, 18 Jan 2023 11:26:23 +0200 Subject: [PATCH] Issue#35 Add documentation into the gradle file --- gradlescripts/jacoco.config.gradle | 216 +++++++++++++++++------------ 1 file changed, 125 insertions(+), 91 deletions(-) diff --git a/gradlescripts/jacoco.config.gradle b/gradlescripts/jacoco.config.gradle index c3e4410..d6cd8a6 100644 --- a/gradlescripts/jacoco.config.gradle +++ b/gradlescripts/jacoco.config.gradle @@ -43,105 +43,120 @@ def androidFileFilter = '**/hilt_aggregated_deps/*' ] -subprojects { module -> - plugins.withType(JavaPlugin).whenPluginAdded { - configure(module) { - apply plugin: "jacoco" - jacocoTestReport { - dependsOn test // tests are required to run before generating the report +/ ** + * Setup Jacoco for Android module. + * - applies Plugin to the module. + * - sets up the "debug" build type for jacoco + * - creates tasks: + * - if androidTest folder exists creates a jacocoAndroidTestReport alias for the jacoco coverageReport, otherwise an empty task. + * - if test folder exists creates a jacocoUnitTestReport alias for the jacoco coverageReport, otherwise an empty task. + * - creates jacocoTestReport which runs both alias tasks created before. + * + * Note: "jacocoTestReport" is the task name which is default for Java Modules. + * / - afterEvaluate { - classDirectories.setFrom(files(classDirectories.files.collect { - fileTree(dir: it, exclude: androidFileFilter) - })) - } - } +def setupAndroidJacoco(Project module, ArrayList fileFilter, String jacocoVersion) { + configure(module) { + apply plugin: "jacoco" - task unitTest(dependsOn: ["test"]) { - group = "verification" - } + module.android.testOptions.unitTests.all { + jacoco.includeNoLocationClasses = true + jacoco.excludes = fileFilter } - } + android.buildTypes.debug.enableAndroidTestCoverage true + android.buildTypes.debug.enableUnitTestCoverage true - plugins.withId("com.android.application") { - configure(module) { - apply plugin: "jacoco" + jacoco.toolVersion = "$jacocoVersion" - module.android.testOptions.unitTests.all { - jacoco.includeNoLocationClasses = true - jacoco.excludes = androidFileFilter - } - android.buildTypes.debug.enableAndroidTestCoverage true - android.buildTypes.debug.enableUnitTestCoverage true - - jacoco.toolVersion = "$jacoco_version" - - task jacocoTestReport(dependsOn: ["createDebugUnitTestCoverageReport", "createDebugAndroidTestCoverageReport"]) { - group = "verification" - } + def hasAndroidTests = new File("${module.projectDir}/src/androidTest").exists() + def hasUnitTests = new File("${module.projectDir}/src/test").exists() + if (hasUnitTests) { task jacocoUnitTestReport(dependsOn: ["createDebugUnitTestCoverageReport"]) { group = "verification" } - task jacocoAndroidTestReport(dependsOn: ["createDebugAndroidTestCoverageReport"]) { + } else { + task jacocoUnitTestReport() { group = "verification" } } - } - - plugins.withId("com.android.library") { - configure(module) { - apply plugin: "jacoco" - - module.android.testOptions.unitTests.all { - jacoco.includeNoLocationClasses = true - jacoco.excludes = androidFileFilter - } - android.buildTypes.debug.enableAndroidTestCoverage true - android.buildTypes.debug.enableUnitTestCoverage true - - jacoco.toolVersion = "$jacoco_version" - - println(module.projectDir) - def hasAndroidTests = new File("${module.projectDir}/src/androidTest").exists() - def hasUnitTests = new File("${module.projectDir}/src/test").exists() - if (hasUnitTests) { - task jacocoUnitTestReport(dependsOn: ["createDebugUnitTestCoverageReport"]) { - group = "verification" - } - } else { - task jacocoUnitTestReport() { - group = "verification" - } - } - if (hasAndroidTests) { - task jacocoAndroidTestReport(dependsOn: ["createDebugAndroidTestCoverageReport"]) { - group = "verification" - } - } else { - task jacocoAndroidTestReport() { - group = "verification" - } - } - - task jacocoTestReport(dependsOn: ["jacocoUnitTestReport", "jacocoAndroidTestReport"]) { + if (hasAndroidTests) { + task jacocoAndroidTestReport(dependsOn: ["createDebugAndroidTestCoverageReport"]) { group = "verification" } + } else { + task jacocoAndroidTestReport() { + group = "verification" + } + } + + task jacocoTestReport(dependsOn: ["jacocoUnitTestReport", "jacocoAndroidTestReport"]) { + group = "verification" + } + } +} + +/ ** + * Setup Jacoco for Java module. + * - applies Plugin to the module. + * - updates the `jacocoTestReport` task to ignore the `androidFileFilter` + * - ensures tests run before `jacocoTestReport` + * / + +def setupJavaJacoco(Project module, ArrayList fileFilter) { + configure(module) { + apply plugin: "jacoco" + + jacocoTestReport { + dependsOn test // tests are required to run before generating the report + + afterEvaluate { + classDirectories.setFrom(files(classDirectories.files.collect { + fileTree(dir: it, exclude: fileFilter) + })) + } } } } +/ ** + * Setup Jacoco for submodules based on their android or java module type + */ +subprojects { module -> + plugins.withType(JavaPlugin).whenPluginAdded { + setupJavaJacoco(module, androidFileFilter) + } + + plugins.withId("com.android.application") { + setupAndroidJacoco(module, androidFileFilter, "$jacoco_version") + } + + plugins.withId("com.android.library") { + setupAndroidJacoco(module, androidFileFilter, "$jacoco_version") + } +} + +/ ** + * Setup Aggregation tasks for Jacoco. + * - jacocoRootReport: can be used to generate the report after submodules `jacocoTestReport` has been ran at least once. + * - runTestAndJacocoRootReport: calls the `jacocoTestReport` of each submodule then calls `jacocoRootReport` for aggregation. + * + * Context, how the aggregated report works: + * The jacoco tasks created by the plugin generate .ec and .exec Execution-Data files in specific locations. + * - These Execution-Data files are all pulled into one `JacocoReport` task (`executionData.from`). + * - All the source files from all the submodules are pulled into the same `JacocoReport` task (`sourceDirectories.from`) + * - All the class files from all the submodules are pulled into the same `JacocoReport` task (`classDirectories.from`) + * Then finally the report is configured to be generated at root `build\coverage-report` + * / configure(rootProject) { apply plugin: "jacoco" - def testTypeName = "debug" - if (rootProject.extensions.extraProperties.has("testVariant")) { - testTypeName = rootProject.extensions.extraProperties.getByName("testVariant") - } task runTestAndJacocoRootReport(type: JacocoReport, group: 'Coverage reports') { description = 'Run Tests and Generates report from all subprojects' + // add all non empty subProjects `jacocoTestReport` task as a dependency. + // note: these tasks are default Jacoco Task for Java and have been added above for Android modules. def codeProjects = subprojects.findAll({ it.subprojects.isEmpty() }) codeProjects.forEach { dependsOn += ["$it.path:jacocoTestReport"] @@ -155,24 +170,43 @@ configure(rootProject) { def codeProjects = subprojects.findAll({ it.subprojects.isEmpty() }) sourceDirectories.from = files(codeProjects.collect { "${it.projectDir}/src/main/java" }) - def filetrees = codeProjects.collect { - def javaTree = fileTree(dir: "${it.buildDir}/classes/java/main", excludes: androidFileFilter) - def kotlinTree = fileTree(dir: "${it.buildDir}/classes/kotlin/main", excludes: androidFileFilter) - def androidJavaTree = fileTree(dir: "${it.buildDir}/intermediates/javac/${testTypeName}/classes", excludes: androidFileFilter) - def androidKotlinTree = fileTree(dir: "${it.buildDir}/tmp/kotlin-classes/${testTypeName}", excludes: androidFileFilter) - files([javaTree, kotlinTree, androidJavaTree, androidKotlinTree]) - }.flatten() - classDirectories.from = files(filetrees) - executionData.from = files( - codeProjects.collect { - [ - fileTree(dir: "${it.buildDir}/outputs/code_coverage/${testTypeName}AndroidTest/connected/", includes: ["**/*.ec", "**/*.exec"]), // androidTest - fileTree(dir: "${it.buildDir}/outputs/unit_test_code_coverage/", includes: ["**/*.ec", "**/*.exec"]), // android unitTest - "${it.buildDir}/jacoco/test.exec" // java - ] - }.flatten() - ) + def classFileTrees = codeProjects.collect { + def javaClassFilesInJavaModuleTree = fileTree( + dir: "${it.buildDir}/classes/java/main", + excludes: androidFileFilter + ) + def kotlinClassFilesInJavaModuleTree = fileTree( + dir: "${it.buildDir}/classes/kotlin/main", + excludes: androidFileFilter + ) + def javaClassFilesInAndroidModuleTree = fileTree( + dir: "${it.buildDir}/intermediates/javac/${testTypeName}/classes", + excludes: androidFileFilter + ) + def kotlinClassFilesInAndroidModuleTree = fileTree( + dir: "${it.buildDir}/tmp/kotlin-classes/${testTypeName}", + excludes: androidFileFilter + ) + + files([javaClassFilesInJavaModuleTree, kotlinClassFilesInJavaModuleTree, javaClassFilesInAndroidModuleTree, kotlinClassFilesInAndroidModuleTree]) + }.flatten() + classDirectories.from = files(classFileTrees) + + def executionDataFiles = codeProjects.collect { + def androidTestExecutionData = fileTree( + dir: "${it.buildDir}/outputs/code_coverage/${testTypeName}AndroidTest/connected/", + includes: ["**/*.ec", "**/*.exec"] + ) + def androidUnitTestExecutionData = fileTree( + dir: "${it.buildDir}/outputs/unit_test_code_coverage/", + includes: ["**/*.ec", "**/*.exec"] + ) + def javaUnitTestExecutionData = "${it.buildDir}/jacoco/test.exec" + + [androidTestExecutionData, androidUnitTestExecutionData, javaUnitTestExecutionData] + }.flatten() + executionData.from = files(executionDataFiles) reports { html {