import groovy.transform.Memoized apply plugin: 'jacoco' jacoco { toolVersion = "0.8.8" } android { testCoverage { jacocoVersion '0.8.8' } } @Memoized Properties getLocalProperties() { final propertiesFile = project.rootProject.file("local.properties") final properties = new Properties() if (propertiesFile.exists()) { properties.load(propertiesFile.newDataInputStream()) } return properties } def openReport(htmlOutDir) { final reportPath = "$htmlOutDir/index.html" println "HTML Report: $reportPath" if (!project.hasProperty("open-report")) { println "to open the report automatically in your default browser add '-Popen-report' cli argument" return } def os = org.gradle.internal.os.OperatingSystem.current() if (os.isWindows()) { exec { commandLine 'cmd', '/c', "start $reportPath" } } else if (os.isMacOsX()) { exec { commandLine 'open', "$reportPath" } } else if (os.isLinux()) { try { exec { commandLine 'xdg-open', "$reportPath" } } catch (Exception ignored) { if (localProperties.containsKey("linux-html-cmd")) { exec { commandLine properties.get("linux-html-cmd"), "$reportPath" } } else { println "'linux-html-cmd' property could not be found in 'local.properties'" } } } } tasks.withType(Test) { jacoco.includeNoLocationClasses = true jacoco.excludes = ['jdk.internal.*'] } // source: https://medium.com/jamf-engineering/android-kotlin-code-coverage-with-jacoco-sonar-and-gradle-plugin-6-x-3933ed503a6e def fileFilter = [ // android '**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*', // kotlin '**/*MapperImpl*.*', '**/*$ViewInjector*.*', '**/*$ViewBinder*.*', '**/BuildConfig.*', '**/*Component*.*', '**/*BR*.*', '**/Manifest*.*', '**/*$Lambda$*.*', '**/*Companion*.*', '**/*Module*.*', '**/*Dagger*.*', '**/*Hilt*.*', '**/*MembersInjector*.*', '**/*_MembersInjector.class', '**/*_Factory*.*', '**/*_Provide*Factory*.*', '**/*Extensions*.*', // sealed and data classes '**/*$Result.*', '**/*$Result$*.*' ] // Our merge report task task jacocoTestReport(type: JacocoReport, dependsOn: ['testPlayDebugUnitTest', 'connectedPlayDebugAndroidTest']) { def htmlOutDir = layout.buildDirectory.dir("reports/jacoco/$name/html").get().asFile doLast { openReport htmlOutDir } reports { xml.required = true html.destination htmlOutDir } def kotlinClasses = fileTree(dir: "$project.buildDir/tmp/kotlin-classes/playDebug", excludes: fileFilter) def mainSrc = "$project.projectDir/src/main/java" sourceDirectories.from = files([mainSrc]) classDirectories.from = files([kotlinClasses]) executionData.from = fileTree(dir: project.buildDir, includes: [ '**/*.exec', '**/*.ec' ]) } // A unit-test only report task task jacocoUnitTestReport(type: JacocoReport, dependsOn: ['testPlayDebugUnitTest']) { def htmlOutDir = layout.buildDirectory.dir("reports/jacoco/$name/html").get().asFile doLast { openReport htmlOutDir } reports { xml.required = true html.destination htmlOutDir } def kotlinClasses = fileTree(dir: "$project.buildDir/tmp/kotlin-classes/playDebug", excludes: fileFilter) def mainSrc = "$project.projectDir/src/main/java" sourceDirectories.from = files([mainSrc]) classDirectories.from = files([kotlinClasses]) executionData.from = fileTree(dir: project.buildDir, includes: [ '**/*.exec' ]) } // A connected android tests only report task task jacocoAndroidTestReport(type: JacocoReport, dependsOn: ['connectedPlayDebugAndroidTest']) { def htmlOutDir = layout.buildDirectory.dir("reports/jacoco/$name/html").get().asFile doLast { openReport htmlOutDir } reports { xml.required = true html.destination htmlOutDir } def kotlinClasses = fileTree(dir: "$project.buildDir/tmp/kotlin-classes/playDebug", excludes: fileFilter) def mainSrc = "$project.projectDir/src/main/java" sourceDirectories.from = files([mainSrc]) classDirectories.from = files([kotlinClasses]) executionData.from = fileTree(dir: project.buildDir, includes: [ '**/*.ec' ]) }