Skip to content

Commit

Permalink
MINOR: Reduce build time by gating test coverage plugins behind a flag (
Browse files Browse the repository at this point in the history
apache#8899)

Most builds don't require test coverage output, so it's wasteful
to spend cycles tracking coverage information for each method
invoked.

I ran a quick test in a fast desktop machine, the absolute
difference will be larger in a slower machine. The tests were
executed after `./gradlew clean` and with a gradle daemon
that was started just before the test (and mildly warmed up
by running `./gradlew clean` again).

`./gradlew unitTest --continue --profile`:
* With coverage enabled: 6m32s
* With coverage disabled: 5m47s

I ran the same test twice and the results were within 2s of
each other, so reasonably consistent.

16% reduction in the time taken to run the unit tests is a
reasonable gain with little downside, so I think this is a
good change.

Reviewers: Manikumar Reddy <[email protected]>
  • Loading branch information
ijuma authored Jun 19, 2020
1 parent 1e5fa9e commit 5ecd094
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 43 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ See [Test Retry Gradle Plugin](https://github.com/gradle/test-retry-gradle-plugi
### Generating test coverage reports ###
Generate coverage reports for the whole project:

./gradlew reportCoverage
./gradlew reportCoverage -PenableTestCoverage=true

Generate coverage for a single module, i.e.:

./gradlew clients:reportCoverage
./gradlew clients:reportCoverage -PenableTestCoverage=true

### Building a binary release gzipped tar ball ###
./gradlew clean releaseTarGz
Expand Down Expand Up @@ -208,6 +208,9 @@ The following options should be set with a `-P` switch, for example `./gradlew -
* `xmlSpotBugsReport`: enable XML reports for spotBugs. This also disables HTML reports as only one can be enabled at a time.
* `maxTestRetries`: the maximum number of retries for a failing test case.
* `maxTestRetryFailures`: maximum number of test failures before retrying is disabled for subsequent tests.
* `enableTestCoverage`: enables test coverage plugins and tasks, including bytecode enhancement of classes required to track said
coverage. Note that this introduces some overhead when running tests and hence why it's disabled by default (the overhead
varies, but 15-20% is a reasonable estimate).

### Dependency Analysis ###

Expand Down
96 changes: 55 additions & 41 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ ext {

userTestLoggingEvents = project.hasProperty("testLoggingEvents") ? Arrays.asList(testLoggingEvents.split(",")) : null

userEnableTestCoverage = project.hasProperty("enableTestCoverage") ? enableTestCoverage : false

generatedDocsDir = new File("${project.rootDir}/docs/generated")

commitId = project.hasProperty('commitId') ? commitId : null
Expand Down Expand Up @@ -552,28 +554,33 @@ subprojects {

// Ignore core since its a scala project
if (it.path != ':core') {
apply plugin: "jacoco"

jacoco {
toolVersion = versions.jacoco
}

// NOTE: Jacoco Gradle plugin does not support "offline instrumentation" this means that classes mocked by PowerMock
// may report 0 coverage, since the source was modified after initial instrumentation.
// See https://github.com/jacoco/jacoco/issues/51
jacocoTestReport {
dependsOn tasks.test
sourceSets sourceSets.main
reports {
html.enabled = true
xml.enabled = true
csv.enabled = false
if (userEnableTestCoverage) {
apply plugin: "jacoco"

jacoco {
toolVersion = versions.jacoco
}

// NOTE: Jacoco Gradle plugin does not support "offline instrumentation" this means that classes mocked by PowerMock
// may report 0 coverage, since the source was modified after initial instrumentation.
// See https://github.com/jacoco/jacoco/issues/51
jacocoTestReport {
dependsOn tasks.test
sourceSets sourceSets.main
reports {
html.enabled = true
xml.enabled = true
csv.enabled = false
}
}

}
}

def coverageGen = it.path == ':core' ? 'reportScoverage' : 'jacocoTestReport'
task reportCoverage(dependsOn: [coverageGen])
if (userEnableTestCoverage) {
def coverageGen = it.path == ':core' ? 'reportScoverage' : 'jacocoTestReport'
task reportCoverage(dependsOn: [coverageGen])
}

task determineCommitId {
def takeFromHash = 16
Expand Down Expand Up @@ -640,30 +647,34 @@ def checkstyleConfigProperties(configFileName) {
}

// Aggregates all jacoco results into the root project directory
task jacocoRootReport(type: org.gradle.testing.jacoco.tasks.JacocoReport) {
def javaProjects = subprojects.findAll { it.path != ':core' }
if (userEnableTestCoverage) {
task jacocoRootReport(type: org.gradle.testing.jacoco.tasks.JacocoReport) {
def javaProjects = subprojects.findAll { it.path != ':core' }

description = 'Generates an aggregate report from all subprojects'
dependsOn(javaProjects.test)
description = 'Generates an aggregate report from all subprojects'
dependsOn(javaProjects.test)

additionalSourceDirs.from = javaProjects.sourceSets.main.allSource.srcDirs
sourceDirectories.from = javaProjects.sourceSets.main.allSource.srcDirs
classDirectories.from = javaProjects.sourceSets.main.output
executionData.from = javaProjects.jacocoTestReport.executionData
additionalSourceDirs.from = javaProjects.sourceSets.main.allSource.srcDirs
sourceDirectories.from = javaProjects.sourceSets.main.allSource.srcDirs
classDirectories.from = javaProjects.sourceSets.main.output
executionData.from = javaProjects.jacocoTestReport.executionData

reports {
html.enabled = true
xml.enabled = true
}
reports {
html.enabled = true
xml.enabled = true
}

// workaround to ignore projects that don't have any tests at all
onlyIf = { true }
doFirst {
executionData = files(executionData.findAll { it.exists() })
// workaround to ignore projects that don't have any tests at all
onlyIf = { true }
doFirst {
executionData = files(executionData.findAll { it.exists() })
}
}
}

task reportCoverage(dependsOn: ['jacocoRootReport', 'core:reportCoverage'])
if (userEnableTestCoverage) {
task reportCoverage(dependsOn: ['jacocoRootReport', 'core:reportCoverage'])
}

def connectPkgs = [
'connect:api',
Expand All @@ -684,7 +695,8 @@ project(':core') {
println "Building project 'core' with Scala version ${versions.scala}"

apply plugin: 'scala'
apply plugin: "org.scoverage"
if (userEnableTestCoverage)
apply plugin: "org.scoverage"
archivesBaseName = "kafka_${versions.baseScala}"

dependencies {
Expand Down Expand Up @@ -736,11 +748,13 @@ project(':core') {
testCompile libs.jfreechart
}

scoverage {
scoverageVersion = versions.scoverage
reportDir = file("${rootProject.buildDir}/scoverage")
highlighting = false
minimumRate = 0.0
if (userEnableTestCoverage) {
scoverage {
scoverageVersion = versions.scoverage
reportDir = file("${rootProject.buildDir}/scoverage")
highlighting = false
minimumRate = 0.0
}
}

configurations {
Expand Down

0 comments on commit 5ecd094

Please sign in to comment.