-
Notifications
You must be signed in to change notification settings - Fork 0
Code Coverage Plugin #110
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Code Coverage Plugin #110
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
71e3905
initial version of the code coverage plugin
paulkagiri f1df0f8
creating code coverage tasks for unit tests done
paulkagiri 8afb2f5
added ability to exclude some flavours
paulkagiri 4b6a4e8
added ability to include android instrumentation tests
paulkagiri b117ef2
configured includeNoLocationClasses
paulkagiri 7954665
excludeFlavors rename
paulkagiri 439ce5b
initial code coverage docs
paulkagiri 9aa3e88
renamed the code coverage class
paulkagiri 61d7865
Update Code Coverage.md
paulkagiri 4253b94
added java projects support
paulkagiri c0bc975
Merge branch 'paul/code-coverage-plugin' of https://github.com/AzureA…
paulkagiri 06a8db8
newline
paulkagiri a926fec
Merge branch 'master' into paul/code-coverage-plugin
paulkagiri 6e0422e
fixed issue of having code coverage as 0
paulkagiri 59f4a13
java code coverage task fix
paulkagiri c1145c5
decoupled tests from java code coverage generation
paulkagiri 30c8ceb
centralized versioning for plugins
paulkagiri 808dc89
minor updates
paulkagiri fe80235
dependencies added to gradle/versions.gradle because of running in an…
paulkagiri 8547c9e
Update plugins/buildsystem/src/main/java/com/microsoft/identity/build…
paulkagiri File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,4 +43,6 @@ ehthumbs.db | |
| ehthumbs_vista.db | ||
|
|
||
| # Folder config file | ||
| [Dd]esktop.ini | ||
| [Dd]esktop.ini | ||
|
|
||
| out/ | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| # Code Coverage Plugin | ||
|
|
||
|
|
||
| ## Intro | ||
| - [Code coverage](https://en.wikipedia.org/wiki/Code_coverage) is a software metric used to measure how many lines of our code are executed during automated tests. | ||
|
|
||
| - [JaCoCo](https://www.eclemma.org/jacoco/trunk/index.html) is a free Java code coverage tool and the [JaCoCo plugin](https://docs.gradle.org/current/userguide/jacoco_plugin.html) provides code coverage metrics for Java code via integration with JaCoCo. | ||
|
|
||
| ## Why | ||
| In order to generate [JaCoCo](https://www.jacoco.org/jacoco/trunk/doc/index.html) unit test coverage reports for Android projects you need to create `JacocoReport` tasks and configure them by providing paths to source code, execution data and compiled classes. It's not straightforward since Android projects can have different flavors and build types thus requiring additional paths to be set. This plugin configures these `JacocoReport` tasks automatically. | ||
|
|
||
| ## Usage | ||
| ```groovy | ||
| plugins { | ||
| ... | ||
| id 'com.gradle.plugin-publish' version '0.14.0' // or whatever version is most recent | ||
| } | ||
|
|
||
| codeCoverageReport{ | ||
| html.enabled = true // by default it's true | ||
| xml.enabled = true // by default it's true | ||
| csv.enabled = true // by default it's true | ||
|
|
||
| unitTests.enabled = true // whether code coverage tasks for unit tests will be generated | ||
| androidTests.enabled = true // whether code coverage tasks for instrumentation tests will be generated | ||
|
|
||
| excludeFlavors = [''] // the product flavors to exclude when generating the code coverage tasks | ||
|
|
||
| excludeClasses = [''] // additional classes to exclude - most are already catered for | ||
|
|
||
| destination = '/some/other/directory' // if you want to configure a custom path to save the code coverage reports, by default your report gets saved in `[project]/build/jacoco/{flavor}{build type}{project}{test type}CoverageReport` | ||
|
|
||
| includeNoLocationClasses = true // To include Robolectric tests in the Jacoco report this needs to be true | ||
| } | ||
|
|
||
| android { | ||
| buildTypes { | ||
| debug { | ||
| testCoverageEnabled true // this instructs the plugin to generate code coverage reports for this build type | ||
| ... | ||
| } | ||
| release { | ||
| testCoverageEnabled false // this instructs the plugin to NOT generate code coverage reports for this build type | ||
| ... | ||
| } | ||
| } | ||
| ... | ||
| productFlavors { | ||
| local {} | ||
| dist {} | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| The above configuration creates a `JacocoReport` task for each variant in the form of `{flavor}{build type}{project}{test type}CoverageReport` | ||
| ``` | ||
| distDebugAppAndroidTestCoverageReport | ||
| distDebugAppUnitTestCoverageReport | ||
| localDebugAppAndroidTestCoverageReport | ||
| localDebugAppUnitTestCoverageReport | ||
| ``` | ||
|
|
||
| By default these are the excluded classes under [Constants](https://github.com/AzureAD/android-complete/blob/paul/code-coverage-plugin/plugins/buildsystem/src/main/java/com/microsoft/identity/buildsystem/codecov/Constants.kt#L25) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| // Variables for plugins project | ||
| ext { | ||
| kotlinVersion = '1.5.30' | ||
| spotBugsGradlePluginVersion = '4.7.1' | ||
| jupiterApiVersion = '5.6.0' | ||
| gradleVersion = '4.1.0' | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
200 changes: 200 additions & 0 deletions
200
plugins/buildsystem/src/main/java/com/microsoft/identity/buildsystem/codecov/CodeCoverage.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,200 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // All rights reserved. | ||
| // | ||
| // This code is licensed under the MIT License. | ||
| // | ||
| // Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| // of this software and associated documentation files(the "Software"), to deal | ||
| // in the Software without restriction, including without limitation the rights | ||
| // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell | ||
| // copies of the Software, and to permit persons to whom the Software is | ||
| // furnished to do so, subject to the following conditions : | ||
| // | ||
| // The above copyright notice and this permission notice shall be included in | ||
| // all copies or substantial portions of the Software. | ||
| // | ||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| // THE SOFTWARE. | ||
| package com.microsoft.identity.buildsystem.codecov | ||
|
|
||
| import com.android.build.gradle.api.BaseVariant | ||
| import com.android.build.gradle.api.SourceKind | ||
| import org.gradle.api.Project | ||
| import org.gradle.api.tasks.testing.Test | ||
| import org.gradle.testing.jacoco.plugins.JacocoPlugin | ||
| import org.gradle.testing.jacoco.plugins.JacocoPluginExtension | ||
| import org.gradle.testing.jacoco.plugins.JacocoTaskExtension | ||
| import org.gradle.testing.jacoco.tasks.JacocoReport | ||
| import java.io.File | ||
|
|
||
| /** | ||
| * This class creates code coverage tasks in the given project | ||
| */ | ||
| object CodeCoverage { | ||
|
|
||
| private lateinit var reportExtension: CodeCoverageReportExtension | ||
|
|
||
| /** | ||
| * Gets the codeCoverageReport configurations and uses them to create the code coverage tasks | ||
| */ | ||
| @JvmStatic | ||
| fun applyCodeCoveragePlugin(project: Project) { | ||
| // get the configurations under codeCoverageReport | ||
| reportExtension = project.extensions.create("codeCoverageReport", CodeCoverageReportExtension::class.java) | ||
|
|
||
| // after build file has been evaluated ... add tasks | ||
| project.afterEvaluate { evaluatedProject -> | ||
| evaluatedProject.configure() | ||
|
|
||
| if (isAndroidProject(evaluatedProject)) { | ||
| addJacocoToAndroid(evaluatedProject) | ||
| } else if (isJavaProject(evaluatedProject) || isKotlinMultiplatform(evaluatedProject)) { | ||
| addJacocoToJava(evaluatedProject) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Apply some plugin configuration from [CodeCoverageReportExtension] to the project. | ||
| */ | ||
| private fun Project.configure() { | ||
| project.plugins.apply(JacocoPlugin::class.java) | ||
|
|
||
| project.extensions.findByType(JacocoPluginExtension::class.java)?.apply { | ||
| toolVersion = reportExtension.jacocoVersion | ||
| } | ||
|
|
||
| tasks.withType(Test::class.java) { testTask -> | ||
| testTask.extensions.findByType(JacocoTaskExtension::class.java)?.apply { | ||
| // To include Robolectric tests in the Jacoco report, flag -> "includeNolocationClasses" should be set to true | ||
| isIncludeNoLocationClasses = reportExtension.includeNoLocationClasses | ||
| if (isIncludeNoLocationClasses) { | ||
| // This needs to be excluded for JDK 11 | ||
| // SEE: https://stackoverflow.com/questions/68065743/cannot-run-gradle-test-tasks-because-of-java-lang-noclassdeffounderror-jdk-inte | ||
| excludes = listOf("jdk.internal.*") | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * add jacoco tasks to an android project | ||
| */ | ||
| private fun addJacocoToAndroid(project: Project) { | ||
| project.android().jacoco.version = reportExtension.jacocoVersion | ||
|
|
||
| if (reportExtension.unitTests.enabled) { | ||
| createTasks(project, TestTypes.UnitTest) | ||
| } | ||
|
|
||
| if (reportExtension.androidTests.enabled) { | ||
| createTasks(project, TestTypes.AndroidTest) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Creates the code coverage tasks for the different build variants | ||
| */ | ||
| private fun createTasks(project: Project, testType: String) { | ||
| val excludeFlavors = (reportExtension.excludeFlavors ?: emptyList()).map { it.toLowerCase() } | ||
| project.android().variants().all { variant -> | ||
| if (shouldCreateTaskForVariant(excludeFlavors, variant, testType)) { | ||
| createReportTask(project, variant, testType) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Creates the code coverage task for the given build variant and adds it to a group (Reporting) | ||
| */ | ||
| private fun createReportTask(project: Project, variant: BaseVariant, testType: String): JacocoReport { | ||
| // get the sources | ||
| val sourceDirs = variant.getSourceFolders(SourceKind.JAVA).map { file -> file.dir } | ||
| // get the classes | ||
| val classesDir = variant.javaCompileProvider.get().destinationDir | ||
| // get the test task for this variant | ||
| val testTask = getAndroidTestTask(project.tasks, variant, testType) | ||
| // get JacocoTaskExtension execution destination | ||
| val executionData = getAndroidExecutionDataFile(testTask, variant, testType) | ||
|
|
||
| val taskName = "${variant.name}${project.name.capitalize()}${testType}CoverageReport" | ||
| return project.tasks.create(taskName, JacocoReport::class.java) { reportTask -> | ||
| // set the task attributes | ||
| reportTask.dependsOn(testTask) | ||
| reportTask.group = "Reporting" | ||
| reportTask.description = "Generates Jacoco coverage reports for the ${variant.name} variant." | ||
| reportTask.executionData.setFrom(project.filesTree(project.buildDir, includes = setOf(executionData))) | ||
| reportTask.sourceDirectories.setFrom(project.files(sourceDirs)) | ||
|
|
||
| // get the java project tree and exclude the defined excluded classes | ||
| val javaTree = project.filesTree(classesDir, excludes = reportExtension.getFileFilterPatterns) | ||
|
|
||
| // if kotlin is available, get the kotlin project tree and exclude the defined excluded classes | ||
| if (hasKotlin(project.plugins)) { | ||
| val kotlinClassesDir = "${project.buildDir}/tmp/kotlin-classes/${variant.name}" | ||
| val kotlinTree = project.filesTree(kotlinClassesDir, excludes = reportExtension.getFileFilterPatterns) | ||
| reportTask.classDirectories.setFrom(javaTree + kotlinTree) | ||
| } else { | ||
| reportTask.classDirectories.setFrom(javaTree) | ||
| } | ||
|
|
||
| configureReport(project, reportTask, taskName) | ||
| } | ||
| } | ||
|
|
||
| private fun addJacocoToJava(project: Project) { | ||
| val testTask = project.tasks.getByName("test") | ||
| val jacocoTestReportTask = project.tasks.getByName("jacocoTestReport") as JacocoReport | ||
|
|
||
| val taskName = "${project.name.decapitalize()}UnitTestCoverageReport" | ||
| project.tasks.create(taskName, JacocoReport::class.java) { reportTask -> | ||
| // set the task attributes | ||
| reportTask.dependsOn(testTask) | ||
| reportTask.group = "Reporting" | ||
| reportTask.description = "Generates Jacoco coverage reports" | ||
| reportTask.executionData.setFrom(jacocoTestReportTask.executionData) | ||
| reportTask.sourceDirectories.setFrom(jacocoTestReportTask.sourceDirectories) | ||
| reportTask.additionalSourceDirs.setFrom(jacocoTestReportTask.additionalSourceDirs) | ||
| reportTask.classDirectories.setFrom(jacocoTestReportTask.classDirectories) | ||
|
|
||
| // set destination | ||
| configureReport(project, reportTask, taskName) | ||
| } | ||
| } | ||
|
|
||
| private fun configureReport(project: Project, reportTask: JacocoReport, taskName: String) { | ||
| reportTask.reports { task -> | ||
| // set the outputs enabled according to configs | ||
| task.html.isEnabled = reportExtension.html.enabled | ||
| task.xml.isEnabled = reportExtension.xml.enabled | ||
| task.csv.isEnabled = reportExtension.csv.enabled | ||
|
|
||
| // default reports path | ||
| val defaultCommonPath = "${project.buildDir}/reports/jacoco/$taskName" | ||
| val configuredDestination = reportExtension.destination | ||
|
|
||
| // configure destination for html code coverage output | ||
| if (reportExtension.html.enabled) { | ||
| val path = File(if (configuredDestination.isNullOrBlank()) "$defaultCommonPath/html" else "${configuredDestination.trim()}/html") | ||
| task.html.destination = path | ||
| } | ||
|
|
||
| // configure destination for xml code coverage output | ||
| if (reportExtension.xml.enabled) { | ||
| val path = File(if (configuredDestination.isNullOrBlank()) "$defaultCommonPath/${taskName}.xml" else "${configuredDestination.trim()}/${taskName}.xml") | ||
| task.xml.destination = path | ||
| } | ||
|
|
||
| // configure destination for csv code coverage output | ||
| if (reportExtension.csv.enabled) { | ||
| val path = File(if (configuredDestination.isNullOrBlank()) "$defaultCommonPath/${taskName}.csv" else "${configuredDestination.trim()}/${taskName}.csv") | ||
| task.csv.destination = path | ||
| } | ||
| } | ||
| } | ||
| } |
55 changes: 55 additions & 0 deletions
55
...m/src/main/java/com/microsoft/identity/buildsystem/codecov/CodeCoverageReportExtension.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // All rights reserved. | ||
| // | ||
| // This code is licensed under the MIT License. | ||
| // | ||
| // Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| // of this software and associated documentation files(the "Software"), to deal | ||
| // in the Software without restriction, including without limitation the rights | ||
| // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell | ||
| // copies of the Software, and to permit persons to whom the Software is | ||
| // furnished to do so, subject to the following conditions : | ||
| // | ||
| // The above copyright notice and this permission notice shall be included in | ||
| // all copies or substantial portions of the Software. | ||
| // | ||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| // THE SOFTWARE. | ||
| package com.microsoft.identity.buildsystem.codecov | ||
|
|
||
| /** | ||
| * This class controls some of the configurations used by the code coverage plugin. | ||
| */ | ||
| open class CodeCoverageReportExtension { | ||
|
|
||
| var html = ReportConfig(true) // whether code coverage html output is enabled | ||
| var xml = ReportConfig(true) // whether code coverage xml output is enabled | ||
| var csv = ReportConfig(true) // whether code coverage csv output is enabled | ||
|
|
||
| var unitTests = ReportConfig(true) // whether code coverage targets unit tests | ||
| var androidTests = ReportConfig(false) // whether code coverage targets android tests | ||
|
|
||
| var destination: String? = null // the destination of the reports - by default it's buildDir/reports/jacoco | ||
| var excludeFlavors: Set<String>? = null // add some product flavours to exclude | ||
| var excludeClasses: Set<String>? = null // add some classes to exclude | ||
|
|
||
| var includeNoLocationClasses: Boolean = true // To include Robolectric tests in the Jacoco report, flag -> "includeNolocationClasses" is set to true | ||
|
|
||
| var jacocoVersion: String = "0.8.7" // jacoco version | ||
|
|
||
| /** | ||
| * get files to exclude | ||
| */ | ||
| val getFileFilterPatterns: Set<String> | ||
| get() { | ||
| return DEFAULT_EXCLUDES + (excludeClasses ?: emptySet()) | ||
| } | ||
|
|
||
| } | ||
|
|
||
| open class ReportConfig(var enabled: Boolean) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.