From 04e029aacbecaa713d9ae380af6e5b6036a042e0 Mon Sep 17 00:00:00 2001 From: Manu Sridharan Date: Thu, 23 Dec 2021 14:08:12 -0800 Subject: Support for Coveralls on multiple modules (#521) Support is based on the official example from the Gradle documentation: https://docs.gradle.org/current/samples/sample_jvm_multi_project_with_code_coverage.html The overall coverage percentage goes down with this PR, since we are measuring on new code that had lower coverage beforehand. After this PR lands, though, the relative comparisons that Coveralls performs should be accurate. After this change, you can run `./gradlew codeCoverageReport` and then `open code-coverage-report/build/reports/jacoco/codeCoverageReport/html/index.html` to see the coverage report locally. --- .github/workflows/continuous-integration.yml | 4 +- build.gradle | 4 +- buildSrc/build.gradle | 19 +++++ .../main/groovy/nullaway.jacoco-conventions.gradle | 62 ++++++++++++++++ code-coverage-report/build.gradle | 84 ++++++++++++++++++++++ jar-infer/jar-infer-lib/build.gradle | 16 +++++ jdk17-unit-tests/build.gradle | 15 +--- jmh/build.gradle | 16 +++++ nullaway/build.gradle | 18 +---- settings.gradle | 6 ++ 10 files changed, 211 insertions(+), 33 deletions(-) create mode 100644 buildSrc/build.gradle create mode 100644 buildSrc/src/main/groovy/nullaway.jacoco-conventions.gradle create mode 100644 code-coverage-report/build.gradle diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index a96a148..9db301f 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -66,9 +66,9 @@ jobs: env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} with: - arguments: jacocoTestReport coverallsJacoco + arguments: codeCoverageReport coveralls continue-on-error: true - if: runner.os == 'Linux' && matrix.java == '8' && matrix.epVersion == '2.4.0' && github.repository == 'uber/NullAway' + if: runner.os == 'Linux' && matrix.java == '11' && matrix.epVersion == '2.4.0' && github.repository == 'uber/NullAway' - name: Check that Git tree is clean after build and test run: ./.buildscript/check_git_clean.sh publish_snapshot: diff --git a/build.gradle b/build.gradle index aad03ea..67afc51 100644 --- a/build.gradle +++ b/build.gradle @@ -30,7 +30,7 @@ plugins { id "com.github.sherter.google-java-format" version "0.9" id "net.ltgt.errorprone" version "2.0.1" apply false id "com.github.johnrengelman.shadow" version "6.1.0" apply false - id "com.github.nbaztec.coveralls-jacoco" version "1.2.5" apply false + id "com.github.kt3k.coveralls" version "2.12.0" apply false id "com.android.application" version "3.5.0" apply false id "me.champeau.jmh" version "0.6.6" apply false } @@ -82,6 +82,8 @@ subprojects { project -> googleJavaFormat { toolVersion = "1.6" + // don't enforce formatting for generated Java code under buildSrc + exclude 'buildSrc/build/**/*.java' } //////////////////////////////////////////////////////////////////////// diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle new file mode 100644 index 0000000..7e9a5c9 --- /dev/null +++ b/buildSrc/build.gradle @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021. Uber Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id 'groovy-gradle-plugin' +} diff --git a/buildSrc/src/main/groovy/nullaway.jacoco-conventions.gradle b/buildSrc/src/main/groovy/nullaway.jacoco-conventions.gradle new file mode 100644 index 0000000..1c7c97c --- /dev/null +++ b/buildSrc/src/main/groovy/nullaway.jacoco-conventions.gradle @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021. Uber Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Mostly taken from official Gradle sample: https://docs.gradle.org/current/samples/sample_jvm_multi_project_with_code_coverage.html +plugins { + id 'jacoco' +} + +jacoco { + toolVersion = "0.8.7" +} + +// Do not generate reports for individual projects +tasks.named("jacocoTestReport") { + enabled = false +} + +// Share sources folder with other projects for aggregated JaCoCo reports +configurations.create('transitiveSourcesElements') { + visible = false + canBeResolved = false + canBeConsumed = true + extendsFrom(configurations.implementation) + attributes { + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME)) + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION)) + attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, 'source-folders')) + } + sourceSets.main.java.srcDirs.forEach { + outgoing.artifact(it) + } +} + +// Share the coverage data to be aggregated for the whole product +configurations.create('coverageDataElements') { + visible = false + canBeResolved = false + canBeConsumed = true + extendsFrom(configurations.implementation) + attributes { + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME)) + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION)) + attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, 'jacoco-coverage-data')) + } + // This will cause the test task to run if the coverage data is requested by the aggregation task + outgoing.artifact(tasks.named("test").map { task -> + task.extensions.getByType(JacocoTaskExtension).destinationFile + }) +} diff --git a/code-coverage-report/build.gradle b/code-coverage-report/build.gradle new file mode 100644 index 0000000..42011f7 --- /dev/null +++ b/code-coverage-report/build.gradle @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2021. Uber Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Mostly taken from official Gradle sample: https://docs.gradle.org/current/samples/sample_jvm_multi_project_with_code_coverage.html +plugins { + id 'java' + id 'jacoco' + id 'com.github.kt3k.coveralls' +} + +// A resolvable configuration to collect source code +def sourcesPath = configurations.create("sourcesPath") { + visible = false + canBeResolved = true + canBeConsumed = false + extendsFrom(configurations.implementation) + attributes { + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME)) + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION)) + attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, 'source-folders')) + } +} + +// A resolvable configuration to collect JaCoCo coverage data +def coverageDataPath = configurations.create("coverageDataPath") { + visible = false + canBeResolved = true + canBeConsumed = false + extendsFrom(configurations.implementation) + attributes { + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME)) + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION)) + attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, 'jacoco-coverage-data')) + } +} + +// Task to gather code coverage from multiple subprojects +def codeCoverageReport = tasks.register('codeCoverageReport', JacocoReport) { + additionalClassDirs(configurations.runtimeClasspath.filter{it.path.contains(rootProject.name) }) + additionalSourceDirs(sourcesPath.incoming.artifactView { lenient(true) }.files) + executionData(coverageDataPath.incoming.artifactView { lenient(true) }.files.filter { it.exists() }) + + reports { + // xml is usually used to integrate code coverage with + // other tools like SonarQube, Coveralls or Codecov + xml.required = true + + // HTML reports can be used to see code coverage + // without any external tools + html.required = true + } +} + +coveralls { + jacocoReportPath = "build/reports/jacoco/codeCoverageReport/codeCoverageReport.xml" + afterEvaluate { + sourceDirs = sourcesPath.incoming.artifactView { lenient(true) }.files as List + } +} + +// These dependencies indicate which projects have tests or tested code we want to include +// when computing overall coverage. We aim to measure coverage for all code that actually ships +// in a Maven artifact (so, e.g., we do not measure coverage for the jmh module) +dependencies { + implementation project(':nullaway') + implementation project(':jar-infer:jar-infer-lib') + // We cannot include the JarInfer NullAway integration test yet since it does not run on JDK 11. + // To include it we would have to also add dependencies on the code being tested + //implementation project(':jar-infer:nullaway-integration-test') + implementation project(':jdk17-unit-tests') +} diff --git a/jar-infer/jar-infer-lib/build.gradle b/jar-infer/jar-infer-lib/build.gradle index 388a3be..e150daa 100644 --- a/jar-infer/jar-infer-lib/build.gradle +++ b/jar-infer/jar-infer-lib/build.gradle @@ -1,5 +1,21 @@ +/* + * Copyright (C) 2021. Uber Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ plugins { id "java-library" + id 'nullaway.jacoco-conventions' } sourceCompatibility = 1.8 diff --git a/jdk17-unit-tests/build.gradle b/jdk17-unit-tests/build.gradle index 3b229e2..9d4c1b2 100644 --- a/jdk17-unit-tests/build.gradle +++ b/jdk17-unit-tests/build.gradle @@ -14,9 +14,8 @@ * limitations under the License. */ plugins { - id 'java-library' - // For code coverage: - id 'jacoco' + id 'java-library' + id 'nullaway.jacoco-conventions' } // Use JDK 17 for this module, via a toolchain @@ -48,13 +47,3 @@ test { ] } -jacoco { - toolVersion = "0.8.7" -} - -jacocoTestReport { - reports { - xml.enabled = true // coveralls plugin depends on xml format report - html.enabled = true - } -} diff --git a/jmh/build.gradle b/jmh/build.gradle index ab25b2f..94b92c1 100644 --- a/jmh/build.gradle +++ b/jmh/build.gradle @@ -1,5 +1,21 @@ +/* + * Copyright (C) 2021. Uber Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ plugins { id 'java-library' + id 'nullaway.jacoco-conventions' id 'me.champeau.jmh' } diff --git a/nullaway/build.gradle b/nullaway/build.gradle index 62c19a6..d379406 100644 --- a/nullaway/build.gradle +++ b/nullaway/build.gradle @@ -17,9 +17,7 @@ import net.ltgt.gradle.errorprone.CheckSeverity plugins { id 'java-library' - // For code coverage: - id 'jacoco' - id 'com.github.nbaztec.coveralls-jacoco' + id 'nullaway.jacoco-conventions' } sourceCompatibility = "1.8" @@ -87,17 +85,3 @@ test { apply plugin: 'com.vanniktech.maven.publish' -jacoco { - toolVersion = "0.8.7" -} - -jacocoTestReport { - reports { - xml.enabled = true // coveralls plugin depends on xml format report - html.enabled = true - } -} - -coverallsJacoco { - reportPath = "nullaway/build/reports/jacoco/test/jacocoTestReport.xml" -} diff --git a/settings.gradle b/settings.gradle index 29e026e..eed5bb1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -31,3 +31,9 @@ include ':jar-infer:nullaway-integration-test' include ':jar-infer:test-android-lib-jarinfer' include ':jmh' include ':jdk17-unit-tests' + +// On Java 8, the code-coverage-report module fails during Gradle configuration. So, exclude it +// on pre-JDK-11 JVMs +if (JavaVersion.current() >= JavaVersion.VERSION_11) { + include ':code-coverage-report' +} -- cgit v1.2.3