Skip to content

Commit

Permalink
Add Jacoco test coverage report
Browse files Browse the repository at this point in the history
  • Loading branch information
spetrov committed May 2, 2024
1 parent 5a81c56 commit a71b98e
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 8 deletions.
25 changes: 20 additions & 5 deletions .github/workflows/build-and-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,32 +33,47 @@ jobs:

# Execute forgerock-core debug unit tests
- name: Run forgerock-core debug unit tests
run: ./gradlew :forgerock-core:testDebugUnitTest --stacktrace --no-daemon
run: ./gradlew :forgerock-core:testDebugUnitTestCoverage --stacktrace --no-daemon

# Execute forgerock-auth debug unit tests
- name: Run forgerock-auth debug unit tests
run: ./gradlew :forgerock-auth:testDebugUnitTest --stacktrace --no-daemon
run: ./gradlew :forgerock-auth:testDebugUnitTestCoverage --stacktrace --no-daemon

# Execute forgerock-authenticator debug unit tests
- name: Run forgerock-authenticator debug unit tests
run: ./gradlew :forgerock-authenticator:testDebugUnitTest --stacktrace --no-daemon
run: ./gradlew :forgerock-authenticator:testDebugUnitTestCoverage --stacktrace --no-daemon

# Execute forgerock-authenticator debug unit tests
- name: Run ping-protect debug unit tests
run: ./gradlew :ping-protect:testDebugUnitTest --stacktrace --no-daemon
run: ./gradlew :ping-protect:testDebugUnitTestCoverage --stacktrace --no-daemon

# Publish test reports for the unit tests
- name: Publish test results
if: success() || failure()
uses: dorny/test-reporter@v1
with:
name: Unit tests results
path: 'forgerock-core/build/test-results/**/TEST-*.xml,forgerock-auth/build/test-results/**/TEST-*.xml,forgerock-authenticator/build/test-results/**/TEST-*.xml,ping-protect/build/test-results/**/TEST-*.xml'
path: |
'forgerock-core/build/test-results/**/TEST-*.xml,
forgerock-auth/build/test-results/**/TEST-*.xml,
forgerock-authenticator/build/test-results/**/TEST-*.xml,
ping-protect/build/test-results/**/TEST-*.xml'
list-suites: 'all'
list-tests: 'all'
fail-on-error: 'true'
reporter: java-junit

# Publish test coverage report
- name: Jacoco Report to PR
id: jacoco
uses: madrapps/jacoco-report@v1.6.1
with:
paths: |
${{ github.workspace }}/**/build/reports/jacoco/testDebugUnitTestCoverage/testDebugUnitTestCoverage.xml
token: ${{ secrets.GITHUB_TOKEN }}
min-coverage-overall: 40
min-coverage-changed-files: 60

# Send slack notification with result status
- uses: 8398a7/action-slack@v3
with:
Expand Down
6 changes: 3 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ subprojects {


tasks.withType<Test>().configureEach {
jvmArgs = jvmArgs?.plus("--add-opens=java.base/java.lang=ALL-UNNAMED")
jvmArgs = jvmArgs?.plus("--add-opens=java.base/java.security=ALL-UNNAMED")
jvmArgs = jvmArgs?.plus("--add-opens=java.base/java.security.cert=ALL-UNNAMED")
jvmArgs = jvmArgs?.plus("--add-opens=java.base/java.lang=ALL-UNNAMED") as MutableList<String>
jvmArgs = jvmArgs?.plus("--add-opens=java.base/java.security=ALL-UNNAMED") as MutableList<String>
jvmArgs = jvmArgs?.plus("--add-opens=java.base/java.security.cert=ALL-UNNAMED") as MutableList<String>
}

}
Expand Down
151 changes: 151 additions & 0 deletions config/jacoco.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
apply plugin: 'jacoco'

jacoco {
toolVersion = "0.8.12"
}

ext {
limits = [
'instruction': 0,
'branch' : 0,
'line' : 0,
'complexity' : 0,
'method' : 0,
'class' : 0
]
}

project.afterEvaluate {
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"

task "${testTaskName}Coverage"(type: JacocoReport, dependsOn: "$testTaskName") {
group = "Reporting"
description = "Generate Jacoco coverage reports on the ${sourceName.capitalize()} build."


def javaDirectories = fileTree(
dir: "${project.buildDir}/intermediates/javac/${sourcePath}/compile${sourceName.capitalize()}JavaWithJavac/classes",
excludes: ['**/R.class',
'**/R$*.class',
'**/*$ViewInjector*.*',
'**/*$ViewBinder*.*',
'**/BuildConfig.*',
'**/Manifest*.*',
'**/*Factory*',
'**/*_MembersInjector*',
'**/*Module*',
'**/*Component*',
'**android**',
'**/BR.class']
)
def kotlinDirectories = fileTree(
dir: "${project.buildDir}/tmp/kotlin-classes/${sourcePath}",
excludes: ['**/R.class',
'**/R$*.class',
'**/*$ViewInjector*.*',
'**/*$ViewBinder*.*',
'**/BuildConfig.*',
'**/Manifest*.*',
'**/*Factory*',
'**/*_MembersInjector*',
'**/*Module*',
'**/*Component*',
'**android**',
'**/BR.class']
)

// logger.lifecycle("javaDirectories: $javaDirectories")
// logger.lifecycle("kotlinDirectories: $kotlinDirectories")


getClassDirectories().setFrom(files(javaDirectories, kotlinDirectories))

def coverageSourceDirs = [
"src/main/java",
"src/$productFlavorName/java",
"src/$buildTypeName/java"
]
getAdditionalSourceDirs().setFrom(files(coverageSourceDirs))
getSourceDirectories().setFrom(files(coverageSourceDirs))
getExecutionData().setFrom(files("${project.buildDir}/jacoco/${testTaskName}.exec"))

reports {
xml.required.set(true)
html.required.set(false)
}

doLast {
jacocoTestReport("${testTaskName}Coverage")
}
}
}
}
}


def jacocoTestReport(testTaskName) {
def report = file("${buildDir}/reports/jacoco/${testTaskName}/${testTaskName}.xml")
logger.lifecycle("Checking coverage results: ${report}")

def parser = new XmlParser()
parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
parser.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false)
def results = parser.parse(report)

def percentage = {
def covered = it.'@covered' as Double
def missed = it.'@missed' as Double
((covered / (covered + missed)) * 100).round(2)
}

def counters = results.counter
def metrics = [:]
metrics << [
'instruction': percentage(counters.find { it.'@type'.equals('INSTRUCTION') }),
'branch' : percentage(counters.find { it.'@type'.equals('BRANCH') }),
'line' : percentage(counters.find { it.'@type'.equals('LINE') }),
'complexity' : percentage(counters.find { it.'@type'.equals('COMPLEXITY') }),
'method' : percentage(counters.find { it.'@type'.equals('METHOD') }),
'class' : percentage(counters.find { it.'@type'.equals('CLASS') })
]

def failures = []
def success = []
metrics.each {
def limit = limits[it.key]
if (it.value < limit) {
failures.add("- ${it.key} coverage rate is: ${it.value}%, minimum is ${limit}%")
}else{
success.add("- ${it.key} coverage rate is: ${it.value}%")
}
}

if (failures) {
logger.quiet("------------------ Code Coverage Failed -----------------------")
failures.each {
logger.quiet(it)
}
logger.quiet("---------------------------------------------------------------")
throw new GradleException("Code coverage failed")
} else {
logger.quiet("------------------ Code Coverage Success -----------------------")
success.each {
logger.quiet(it)
}
logger.quiet("---------------------------------------------------------------")
}
}
1 change: 1 addition & 0 deletions forgerock-auth/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ android {

apply("../config/logger.gradle")
apply("../config/kdoc.gradle")
apply("../config/jacoco.gradle")
apply("../config/publish.gradle")
/**
* Dependencies
Expand Down
1 change: 1 addition & 0 deletions forgerock-authenticator/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ tasks {
}
}

apply("../config/jacoco.gradle")
apply("../config/logger.gradle")
apply("../config/publish.gradle")

Expand Down
1 change: 1 addition & 0 deletions forgerock-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ android {

apply("../config/logger.gradle")
apply("../config/kdoc.gradle")
apply("../config/jacoco.gradle")
apply("../config/publish.gradle")

val delombok by configurations.creating {
Expand Down
1 change: 1 addition & 0 deletions ping-protect/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ tasks {
}
}

apply("../config/jacoco.gradle")
apply("../config/logger.gradle")
apply("../config/publish.gradle")

Expand Down

0 comments on commit a71b98e

Please sign in to comment.