├── mise.toml
├── .github
├── CODEOWNERS
├── workflows
│ ├── pr-cleanup.yml
│ ├── releasability.yaml
│ ├── release.yml
│ ├── mark-prs-stale.yml
│ ├── ToggleLockBranch.yml
│ ├── PullRequestClosed.yml
│ ├── RequestReview.yml
│ ├── SubmitReview.yml
│ ├── PullRequestCreated.yml
│ ├── unified-dogfooding.yml
│ └── build.yml
└── PULL_REQUEST_TEMPLATE.md
├── its
├── src
│ └── test
│ │ ├── resources
│ │ ├── aggregate-and-module-based-mixed-coverage
│ │ │ ├── .gitignore
│ │ │ ├── self-covered
│ │ │ │ ├── src
│ │ │ │ │ ├── main
│ │ │ │ │ │ └── java
│ │ │ │ │ │ │ └── org
│ │ │ │ │ │ │ └── example
│ │ │ │ │ │ │ └── Squarer.java
│ │ │ │ │ └── test
│ │ │ │ │ │ └── java
│ │ │ │ │ │ └── org
│ │ │ │ │ │ └── example
│ │ │ │ │ │ └── SquarerTest.java
│ │ │ │ └── pom.xml
│ │ │ ├── library
│ │ │ │ ├── src
│ │ │ │ │ ├── main
│ │ │ │ │ │ └── java
│ │ │ │ │ │ │ └── org
│ │ │ │ │ │ │ └── example
│ │ │ │ │ │ │ └── Library.java
│ │ │ │ │ └── test
│ │ │ │ │ │ └── java
│ │ │ │ │ │ └── org
│ │ │ │ │ │ └── example
│ │ │ │ │ │ └── LibraryTest.java
│ │ │ │ └── pom.xml
│ │ │ ├── library-test
│ │ │ │ ├── src
│ │ │ │ │ └── test
│ │ │ │ │ │ └── java
│ │ │ │ │ │ └── org
│ │ │ │ │ │ └── example
│ │ │ │ │ │ └── LibraryTest.java
│ │ │ │ └── pom.xml
│ │ │ ├── README.md
│ │ │ ├── pom.xml
│ │ │ └── report
│ │ │ │ └── pom.xml
│ │ ├── simple-project-jacoco
│ │ │ ├── jacoco.exec
│ │ │ ├── src
│ │ │ │ ├── main
│ │ │ │ │ └── java
│ │ │ │ │ │ └── org
│ │ │ │ │ │ └── sonarsource
│ │ │ │ │ │ └── test
│ │ │ │ │ │ ├── CalcNoCoverage.java
│ │ │ │ │ │ └── Calc.java
│ │ │ │ └── test
│ │ │ │ │ └── java
│ │ │ │ │ └── org
│ │ │ │ │ └── sonarsource
│ │ │ │ │ └── test
│ │ │ │ │ └── CalcTest.java
│ │ │ ├── jacoco-with-invalid-format.xml
│ │ │ ├── pom.xml
│ │ │ ├── jacoco.xml
│ │ │ ├── jacoco-with-invalid-lines.xml
│ │ │ ├── target
│ │ │ │ └── site
│ │ │ │ │ └── jacoco-it
│ │ │ │ │ └── jacoco.xml
│ │ │ └── jacoco-with-invalid-sources.xml
│ │ └── kotlin-jacoco-project
│ │ │ ├── src
│ │ │ ├── main
│ │ │ │ └── kotlin
│ │ │ │ │ └── CoverMe.kt
│ │ │ └── test
│ │ │ │ └── kotlin
│ │ │ │ └── CoverMeTest.kt
│ │ │ └── pom.xml
│ │ └── java
│ │ └── org
│ │ └── sonar
│ │ └── plugins
│ │ └── jacoco
│ │ └── its
│ │ └── JacocoTest.java
└── build.gradle
├── src
├── test
│ ├── resources
│ │ ├── search
│ │ │ ├── f1.xml
│ │ │ ├── f2.xml
│ │ │ └── subfolder
│ │ │ │ ├── g1.xml
│ │ │ │ └── g2.xml
│ │ ├── name_missing_in_package.xml
│ │ ├── sourcefile_not_within_package.xml
│ │ ├── line_not_within_sourcefile.xml
│ │ ├── line_without_mi_ci_mb_cb.xml
│ │ ├── nr_missing_in_line.xml
│ │ ├── invalid_nr_in_line.xml
│ │ ├── simple.xml
│ │ ├── name_missing_in_sourcefile.xml
│ │ ├── invalid_ci_in_line.xml
│ │ └── invalid_line_number.xml
│ └── java
│ │ └── org
│ │ └── sonar
│ │ └── plugins
│ │ └── jacoco
│ │ ├── SensorUtilsTest.java
│ │ ├── JacocoPluginTest.java
│ │ ├── ReportImporterTest.java
│ │ ├── JacocoAggregateSensorTest.java
│ │ ├── FileLocatorTest.java
│ │ ├── WildcardPatternFileScannerTest.java
│ │ ├── JacocoSensorTest.java
│ │ ├── XmlReportParserTest.java
│ │ ├── ReportPathsProviderTest.java
│ │ └── KotlinFileLocatorTest.java
└── main
│ └── java
│ └── org
│ └── sonar
│ └── plugins
│ └── jacoco
│ ├── package-info.java
│ ├── SensorUtils.java
│ ├── ReportImporter.java
│ ├── ReversePathTree.java
│ ├── JacocoPlugin.java
│ ├── FileLocator.java
│ ├── JacocoAggregateSensor.java
│ ├── JacocoSensor.java
│ ├── ReportPathsProvider.java
│ ├── KotlinFileLocator.java
│ ├── WildcardPatternFileScanner.java
│ └── XmlReportParser.java
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── settings.gradle
├── .gitignore
├── LICENSE_HEADER
├── SECURITY.md
├── README.md
├── renovate.json
├── gradlew.bat
├── LICENSE
└── gradlew
/mise.toml:
--------------------------------------------------------------------------------
1 | [tools]
2 | java = "17.0"
3 | gradle = "9.2"
4 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | .github/CODEOWNERS @SonarSource/quality-jvm-squad
2 |
--------------------------------------------------------------------------------
/its/src/test/resources/aggregate-and-module-based-mixed-coverage/.gitignore:
--------------------------------------------------------------------------------
1 | **/target/**
--------------------------------------------------------------------------------
/src/test/resources/search/f1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/test/resources/search/f2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SonarSource/sonar-jacoco/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/src/test/resources/search/subfolder/g1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/test/resources/search/subfolder/g2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/its/src/test/resources/simple-project-jacoco/jacoco.exec:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SonarSource/sonar-jacoco/HEAD/its/src/test/resources/simple-project-jacoco/jacoco.exec
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | group=org.sonarsource.jacoco
2 | version=1.5.0-SNAPSHOT
3 | description=SonarQube plugin to import JaCoCo XML coverage reports
4 | projectTitle=SonarQube JaCoCo plugin
5 |
--------------------------------------------------------------------------------
/its/src/test/resources/aggregate-and-module-based-mixed-coverage/self-covered/src/main/java/org/example/Squarer.java:
--------------------------------------------------------------------------------
1 | package org.example;
2 |
3 | public class Squarer {
4 | public int square(int a) {
5 | return a * a;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/test/resources/name_missing_in_package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/its/src/test/resources/kotlin-jacoco-project/src/main/kotlin/CoverMe.kt:
--------------------------------------------------------------------------------
1 | package org.example.cover
2 |
3 | class CoverMe {
4 |
5 | fun f(a: Int) {
6 | if (a < 5) {
7 | println("Hello, World!")
8 | }
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/its/src/test/resources/kotlin-jacoco-project/src/test/kotlin/CoverMeTest.kt:
--------------------------------------------------------------------------------
1 | package org.example.cover
2 |
3 | import org.junit.Test
4 |
5 | class CoverMeTest {
6 |
7 | @Test
8 | fun test() {
9 | val coverMe = CoverMe()
10 | coverMe.f(10)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/its/src/test/resources/aggregate-and-module-based-mixed-coverage/library/src/main/java/org/example/Library.java:
--------------------------------------------------------------------------------
1 | package org.example;
2 |
3 | public class Library {
4 | public Integer div(int a, int b) {
5 | if (b == 0) {
6 | return null;
7 | }
8 | return a / b;
9 | }
10 | }
--------------------------------------------------------------------------------
/.github/workflows/pr-cleanup.yml:
--------------------------------------------------------------------------------
1 | name: Cleanup PR Resources
2 | on:
3 | pull_request:
4 | types: [closed]
5 |
6 | jobs:
7 | cleanup:
8 | runs-on: github-ubuntu-latest-s
9 | permissions:
10 | actions: write
11 | steps:
12 | - uses: SonarSource/ci-github-actions/pr_cleanup@v1
13 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/src/test/resources/sourcefile_not_within_package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | mavenCentral()
5 | }
6 | }
7 |
8 | dependencyResolutionManagement {
9 | repositories {
10 | mavenCentral()
11 | }
12 | }
13 |
14 | rootProject.name = 'sonar-jacoco-plugin'
15 |
16 | include 'its'
17 |
18 |
--------------------------------------------------------------------------------
/src/test/resources/line_not_within_sourcefile.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Part of
2 |
8 |
--------------------------------------------------------------------------------
/src/test/resources/line_without_mi_ci_mb_cb.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/test/resources/nr_missing_in_line.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/test/resources/invalid_nr_in_line.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/test/resources/simple.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/test/resources/name_missing_in_sourcefile.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/its/src/test/resources/aggregate-and-module-based-mixed-coverage/library-test/src/test/java/org/example/LibraryTest.java:
--------------------------------------------------------------------------------
1 | package org.example;
2 |
3 | import org.junit.jupiter.api.Assertions;
4 | import org.junit.jupiter.api.Test;
5 |
6 | class LibraryTest {
7 | @Test
8 | void incompleteTest() {
9 | Library library = new Library();
10 | Assertions.assertEquals(2, library.div(2, 1));
11 | }
12 | }
--------------------------------------------------------------------------------
/src/test/resources/invalid_ci_in_line.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/test/resources/invalid_line_number.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/its/src/test/resources/aggregate-and-module-based-mixed-coverage/library/src/test/java/org/example/LibraryTest.java:
--------------------------------------------------------------------------------
1 | package org.example;
2 |
3 | import org.junit.jupiter.api.Assertions;
4 | import org.junit.jupiter.api.Test;
5 |
6 | class LibraryTest {
7 | @Test
8 | void returns_null_when_dividing_by_zero() {
9 | Library library = new Library();
10 | Assertions.assertNull(library.div(2, 0));
11 | }
12 | }
--------------------------------------------------------------------------------
/its/src/test/resources/aggregate-and-module-based-mixed-coverage/self-covered/src/test/java/org/example/SquarerTest.java:
--------------------------------------------------------------------------------
1 | package org.example;
2 |
3 | import org.junit.jupiter.api.Assertions;
4 | import org.junit.jupiter.api.Test;
5 |
6 | class SquarerTest {
7 | @Test
8 | void returns_squared_value() {
9 | Squarer squarer = new Squarer();
10 | Assertions.assertEquals(4, squarer.square(2));
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/its/src/test/resources/simple-project-jacoco/src/main/java/org/sonarsource/test/CalcNoCoverage.java:
--------------------------------------------------------------------------------
1 | package org.sonarsource.test;
2 |
3 | public class CalcNoCoverage {
4 | private int acc;
5 |
6 | public CalcNoCoverage(int initial) {
7 | this.acc = initial;
8 | }
9 |
10 | public int add(int add) {
11 | acc = acc+add;
12 | if (acc < 0) {
13 | acc = 0;
14 | }
15 | return acc;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/its/src/test/resources/simple-project-jacoco/jacoco-with-invalid-format.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.github/workflows/releasability.yaml:
--------------------------------------------------------------------------------
1 | name: Releasability status
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | update_releasability_status:
8 | runs-on: github-ubuntu-latest-s
9 | name: Releasability status
10 | permissions:
11 | id-token: write
12 | statuses: write
13 | contents: read
14 | steps:
15 | - uses: SonarSource/gh-action_releasability/releasability-status@v3
16 | env:
17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # ---- gradle
2 | build
3 | bin
4 | .gradle
5 |
6 | # ---- IntelliJ IDEA
7 | *.iws
8 | *.iml
9 | *.ipr
10 | .idea/
11 | out
12 |
13 | # ---- Eclipse
14 | .classpath
15 | .project
16 | .settings
17 | .externalToolBuilders
18 |
19 | # ---- Mac OS X
20 | .DS_Store
21 | Icon?
22 | # Thumbnails
23 | ._*
24 | # Files that might appear on external disk
25 | .Spotlight-V100
26 | .Trashes
27 |
28 | # ---- Windows
29 | # Windows image file caches
30 | Thumbs.db
31 | # Folder config file
32 | Desktop.ini
33 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: sonar-release
3 | # This workflow is triggered when publishing a new github release
4 | # yamllint disable-line rule:truthy
5 | on:
6 | release:
7 | types:
8 | - published
9 |
10 | jobs:
11 | release:
12 | permissions:
13 | id-token: write
14 | contents: write
15 | uses: SonarSource/gh-action_release/.github/workflows/main.yaml@v6
16 | with:
17 | publishToBinaries: true
18 | mavenCentralSync: true
19 | slackChannel: squad-jvm-notifs
20 |
--------------------------------------------------------------------------------
/its/src/test/resources/simple-project-jacoco/src/main/java/org/sonarsource/test/Calc.java:
--------------------------------------------------------------------------------
1 | package org.sonarsource.test;
2 |
3 | public class Calc {
4 | private int acc;
5 |
6 | public Calc(int initial) {
7 | this.acc = initial;
8 | }
9 |
10 | public int add(int add) {
11 | acc = acc+add;
12 | if (acc < 0) {
13 | acc = 0;
14 | }
15 | return acc;
16 | }
17 |
18 | public int subtract(int sub) {
19 | acc = acc - sub;
20 | if (acc > 0) {
21 | acc = 0;
22 | }
23 | return acc;
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/its/src/test/resources/aggregate-and-module-based-mixed-coverage/README.md:
--------------------------------------------------------------------------------
1 | # Aggregate Maven project
2 |
3 | A project with 4 modules:
4 |
5 | 1. [library](./library) - containing code but no tests
6 | 2. [library.test](./library.test) - containing test code that uses code from `library`
7 | 3. [report](./report) - generating the aggregate coverage report
8 | 4. [self-covered](./self-covered) - containing code, tests and generating its own module-based coverage report
9 |
10 |
11 | The report can be generated by running the following command:
12 | ```shell
13 | mvn verify --file ./pom.xml
14 | ```
--------------------------------------------------------------------------------
/its/src/test/resources/simple-project-jacoco/src/test/java/org/sonarsource/test/CalcTest.java:
--------------------------------------------------------------------------------
1 | package org.sonarsource.test;
2 |
3 | import org.junit.Assert;
4 | import org.junit.Test;
5 |
6 | public class CalcTest {
7 | private Calc calc = new Calc(5);
8 |
9 | @Test
10 | public void should_add() {
11 | Assert.assertEquals(9, calc.add(4));
12 | }
13 |
14 | @Test
15 | public void should_subtract() {
16 | Assert.assertEquals(-2, calc.subtract(7));
17 | }
18 |
19 | @Test
20 | public void should_add_and_return_0_if_sum_negative() {
21 | Assert.assertEquals(0, calc.add(-9));
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/.github/workflows/mark-prs-stale.yml:
--------------------------------------------------------------------------------
1 | name: 'Close stale PRs'
2 | on:
3 | workflow_dispatch:
4 | schedule:
5 | - cron: '30 2 * * *'
6 |
7 | jobs:
8 | stale:
9 | runs-on: github-ubuntu-latest-s
10 | permissions:
11 | issues: write
12 | pull-requests: write
13 | steps:
14 | - uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10
15 | with:
16 | stale-pr-message: 'This PR is stale because it has been open 7 days with no activity. If there is no activity in the next 7 days it will be closed automatically'
17 | stale-pr-label: 'stale'
18 | days-before-stale: 7
19 | days-before-close: 7
20 | exempt-pr-labels: 'do-not-close,External Contribution'
21 |
--------------------------------------------------------------------------------
/its/src/test/resources/simple-project-jacoco/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | org.sonarsource.test
8 | test-jacoco-plugin
9 | 1.0-SNAPSHOT
10 |
11 |
12 |
13 | junit
14 | junit
15 | 4.13.1
16 | test
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/LICENSE_HEADER:
--------------------------------------------------------------------------------
1 | SonarQube JaCoCo Plugin
2 | Copyright (C) 2018-${year} SonarSource SA
3 | mailto:info AT sonarsource DOT com
4 |
5 | This program is free software; you can redistribute it and/or
6 | modify it under the terms of the GNU Lesser General Public
7 | License as published by the Free Software Foundation; either
8 | version 3 of the License, or (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program; if not, write to the Free Software Foundation,
17 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 |
--------------------------------------------------------------------------------
/.github/workflows/ToggleLockBranch.yml:
--------------------------------------------------------------------------------
1 | name: Toggle lock branch
2 |
3 | on:
4 | workflow_dispatch: # Triggered manually from the GitHub UI / Actions
5 |
6 | jobs:
7 | ToggleLockBranch_job:
8 | name: Toggle lock branch
9 | runs-on: github-ubuntu-latest-s
10 | permissions:
11 | id-token: write
12 | steps:
13 | - id: secrets
14 | uses: SonarSource/vault-action-wrapper@v3
15 | with:
16 | secrets: |
17 | development/github/token/{REPO_OWNER_NAME_DASH}-lock token | lock_token;
18 | development/kv/data/slack token | slack_api_token;
19 | - uses: sonarsource/gh-action-lt-backlog/ToggleLockBranch@v2
20 | with:
21 | github-token: ${{ fromJSON(steps.secrets.outputs.vault).lock_token }}
22 | slack-token: ${{ fromJSON(steps.secrets.outputs.vault).slack_api_token }}
23 | slack-channel: squad-jvm-notifs
24 |
--------------------------------------------------------------------------------
/its/src/test/resources/aggregate-and-module-based-mixed-coverage/library/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 |
7 | org.example
8 | aggregate-and-module-based-mixed-coverage
9 | 1.0-SNAPSHOT
10 |
11 |
12 | library
13 | jar
14 |
15 |
16 |
17 | org.junit.jupiter
18 | junit-jupiter-api
19 | 6.0.1
20 | test
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.github/workflows/PullRequestClosed.yml:
--------------------------------------------------------------------------------
1 | name: Pull Request Closed
2 |
3 | on:
4 | pull_request:
5 | types: [closed]
6 |
7 | jobs:
8 | PullRequestMerged_job:
9 | name: Pull Request Merged
10 | runs-on: github-ubuntu-latest-s
11 | permissions:
12 | id-token: write
13 | pull-requests: read
14 | # For external PR, ticket should be moved manually
15 | if: |
16 | github.event.pull_request.head.repo.full_name == github.repository
17 | steps:
18 | - id: secrets
19 | uses: SonarSource/vault-action-wrapper@v3
20 | with:
21 | secrets: |
22 | development/kv/data/jira user | JIRA_USER;
23 | development/kv/data/jira token | JIRA_TOKEN;
24 | - uses: sonarsource/gh-action-lt-backlog/PullRequestClosed@v2
25 | with:
26 | github-token: ${{secrets.GITHUB_TOKEN}}
27 | jira-user: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_USER }}
28 | jira-token: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_TOKEN }}
29 |
--------------------------------------------------------------------------------
/src/main/java/org/sonar/plugins/jacoco/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SonarQube JaCoCo Plugin
3 | * Copyright (C) 2018-2025 SonarSource SA
4 | * mailto:info AT sonarsource DOT com
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 | @javax.annotation.ParametersAreNonnullByDefault
21 | package org.sonar.plugins.jacoco;
22 |
--------------------------------------------------------------------------------
/.github/workflows/RequestReview.yml:
--------------------------------------------------------------------------------
1 | name: Request review
2 |
3 | on:
4 | pull_request:
5 | types: ["review_requested"]
6 |
7 | jobs:
8 | RequestReview_job:
9 | name: Request review
10 | runs-on: github-ubuntu-latest-s
11 | permissions:
12 | id-token: write
13 | # For external PR, ticket should be moved manually
14 | if: |
15 | github.event.pull_request.head.repo.full_name == github.repository
16 | steps:
17 | - id: secrets
18 | uses: SonarSource/vault-action-wrapper@v3
19 | with:
20 | secrets: |
21 | development/github/token/{REPO_OWNER_NAME_DASH}-jira token | GITHUB_TOKEN;
22 | development/kv/data/jira user | JIRA_USER;
23 | development/kv/data/jira token | JIRA_TOKEN;
24 | - uses: sonarsource/gh-action-lt-backlog/RequestReview@v2
25 | with:
26 | github-token: ${{ fromJSON(steps.secrets.outputs.vault).GITHUB_TOKEN }}
27 | jira-user: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_USER }}
28 | jira-token: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_TOKEN }}
29 |
--------------------------------------------------------------------------------
/.github/workflows/SubmitReview.yml:
--------------------------------------------------------------------------------
1 | name: Submit Review
2 |
3 | on:
4 | pull_request_review:
5 | types: [submitted]
6 |
7 | jobs:
8 | SubmitReview_job:
9 | name: Submit Review
10 | runs-on: github-ubuntu-latest-s
11 | permissions:
12 | id-token: write
13 | pull-requests: read
14 | # For external PR, ticket should be moved manually
15 | if: |
16 | github.event.pull_request.head.repo.full_name == github.repository
17 | && (github.event.review.state == 'changes_requested'
18 | || github.event.review.state == 'approved')
19 | steps:
20 | - id: secrets
21 | uses: SonarSource/vault-action-wrapper@v3
22 | with:
23 | secrets: |
24 | development/kv/data/jira user | JIRA_USER;
25 | development/kv/data/jira token | JIRA_TOKEN;
26 | - uses: sonarsource/gh-action-lt-backlog/SubmitReview@v2
27 | with:
28 | github-token: ${{secrets.GITHUB_TOKEN}}
29 | jira-user: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_USER }}
30 | jira-token: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_TOKEN }}
31 |
--------------------------------------------------------------------------------
/.github/workflows/PullRequestCreated.yml:
--------------------------------------------------------------------------------
1 | name: Pull Request Created
2 |
3 | on:
4 | pull_request:
5 | types: ["opened"]
6 |
7 | jobs:
8 | PullRequestCreated_job:
9 | name: Pull Request Created
10 | runs-on: github-ubuntu-latest-s
11 | permissions:
12 | id-token: write
13 | # For external PR, ticket should be created manually
14 | if: |
15 | github.event.pull_request.head.repo.full_name == github.repository
16 | steps:
17 | - id: secrets
18 | uses: SonarSource/vault-action-wrapper@v3
19 | with:
20 | secrets: |
21 | development/github/token/{REPO_OWNER_NAME_DASH}-jira token | GITHUB_TOKEN;
22 | development/kv/data/jira user | JIRA_USER;
23 | development/kv/data/jira token | JIRA_TOKEN;
24 | - uses: sonarsource/gh-action-lt-backlog/PullRequestCreated@v2
25 | with:
26 | github-token: ${{ fromJSON(steps.secrets.outputs.vault).GITHUB_TOKEN }}
27 | jira-user: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_USER }}
28 | jira-token: ${{ fromJSON(steps.secrets.outputs.vault).JIRA_TOKEN }}
29 | jira-project: JACOCO
30 |
--------------------------------------------------------------------------------
/its/src/test/resources/aggregate-and-module-based-mixed-coverage/library-test/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 |
7 | org.example
8 | aggregate-and-module-based-mixed-coverage
9 | 1.0-SNAPSHOT
10 |
11 |
12 | library-test
13 | jar
14 |
15 |
16 |
17 | org.example
18 | library
19 | ${project.version}
20 | test
21 |
22 |
23 | org.junit.jupiter
24 | junit-jupiter-api
25 | 6.0.1
26 | test
27 |
28 |
29 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Reporting Security Issues
2 |
3 | A mature software vulnerability treatment process is a cornerstone of a robust information security management system. Contributions from the community play an important role in the evolution and security of our products, and in safeguarding the security and privacy of our users.
4 |
5 | If you believe you have discovered a security vulnerability in Sonar's products, we encourage you to report it immediately.
6 |
7 | To responsibly report a security issue, please email us at [security@sonarsource.com](mailto:security@sonarsource.com). Sonar’s security team will acknowledge your report, guide you through the next steps, or request additional information if necessary. Customers with a support contract can also report the vulnerability directly through the support channel.
8 |
9 | For security vulnerabilities found in third-party libraries, please also contact the library's owner or maintainer directly.
10 |
11 | ## Responsible Disclosure Policy
12 |
13 | For more information about disclosing a security vulnerability to Sonar, please refer to our community post: [Responsible Vulnerability Disclosure](https://community.sonarsource.com/t/responsible-vulnerability-disclosure/9317).
--------------------------------------------------------------------------------
/.github/workflows/unified-dogfooding.yml:
--------------------------------------------------------------------------------
1 | name: Unified Dogfooding scans
2 | on:
3 | schedule:
4 | - cron: '45 3 * * *' # Run the workflow every day at 03:45 UTC
5 | workflow_dispatch:
6 |
7 | jobs:
8 | unified-platform-dogfooding:
9 | runs-on: github-ubuntu-latest-s
10 | name: Unified Platform Dogfooding
11 | permissions:
12 | id-token: write
13 | contents: read
14 | steps:
15 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
16 | - uses: SonarSource/ci-github-actions/build-gradle@v1
17 | with:
18 | run-shadow-scans: true
19 | artifactory-reader-role: private-reader
20 | artifactory-deployer-role: qa-deployer
21 | deploy-pull-request: false
22 | - name: Run IRIS Analysis
23 | uses: SonarSource/unified-dogfooding-actions/run-iris@v1
24 | with:
25 | primary_project_key: "org.sonarsource.jacoco:sonar-jacoco"
26 | primary_platform: "Next"
27 | shadow1_project_key: "org.sonarsource.jacoco:sonar-jacoco"
28 | shadow1_platform: "SQC-EU"
29 | shadow2_project_key: "org.sonarsource.jacoco:sonar-jacoco"
30 | shadow2_platform: "SQC-US"
31 |
--------------------------------------------------------------------------------
/its/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id "java"
3 | }
4 |
5 | description = 'SonarQube JaCoCo :: Integration Tests'
6 |
7 | dependencies {
8 | testImplementation(platform('org.junit:junit-bom:5.6.2'))
9 | testImplementation('org.junit.jupiter:junit-jupiter')
10 | testImplementation('org.junit.jupiter:junit-jupiter-migrationsupport')
11 | testRuntimeOnly('org.junit.platform:junit-platform-launcher')
12 | testImplementation('org.mockito:mockito-core:1.10.19')
13 | testImplementation('org.assertj:assertj-core:3.10.0')
14 | testImplementation('org.sonarsource.orchestrator:sonar-orchestrator-junit4:6.0.1.3892')
15 | testImplementation('org.sonarsource.sonarqube:sonar-ws:6.7')
16 | testImplementation('com.google.code.findbugs:jsr305:3.0.2')
17 | }
18 |
19 | test {
20 | useJUnitPlatform()
21 | }
22 |
23 | sonarqube.skipProject = true
24 |
25 | task integrationTest(type: Test) {
26 | systemProperty 'java.awt.headless', 'true'
27 |
28 | def orchestratorProps = System.getProperties().findAll { it.key.startsWith("orchestrator") || it.key.startsWith("sonar") }.collect { it.key }
29 | systemProperties System.getProperties().subMap(orchestratorProps)
30 | }
31 |
32 | test.onlyIf { project.hasProperty('integrationTests') && project.getProperty('integrationTests') }
33 |
34 |
35 |
--------------------------------------------------------------------------------
/its/src/test/resources/aggregate-and-module-based-mixed-coverage/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.example
7 | aggregate-and-module-based-mixed-coverage
8 | 1.0-SNAPSHOT
9 |
10 | pom
11 |
12 |
13 | library
14 | library-test
15 | report
16 | self-covered
17 |
18 |
19 |
20 |
21 |
22 | org.jacoco
23 | jacoco-maven-plugin
24 | 0.8.14
25 |
26 |
27 | prepare-agent
28 |
29 | prepare-agent
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SonarQube JaCoCo Plugin
2 |
3 | [](https://github.com/SonarSource/sonar-jacoco/actions/workflows/build.yml)
4 | [](https://next.sonarqube.com/sonarqube/dashboard?id=org.sonarsource.jacoco%3Asonar-jacoco)
5 | [](https://next.sonarqube.com/sonarqube/component_measures?id=org.sonarsource.jacoco%3Asonar-jacoco&metric=coverage)
6 |
7 |
8 | ### How to use?
9 |
10 | Have a look at [importing JaCoCo coverage report in XML format](https://community.sonarsource.com/t/coverage-test-data-importing-jacoco-coverage-report-in-xml-format/12151) guide.
11 |
12 |
13 | ### Have question or feedback?
14 |
15 |
16 | To provide feedback (request a feature, report a bug etc.) use the [SonarQube Community Forum](https://community.sonarsource.com/). Please do not forget to specify the language and tag 'jacoco', plugin version and SonarQube version.
17 |
18 | If you have a question on how to use plugin (and the [guide](https://community.sonarsource.com/t/coverage-test-data-importing-jacoco-coverage-report-in-xml-format/12151) doesn't help you), we also encourage you to use the community forum.
19 |
20 | ### License
21 |
22 | Copyright 2018-2025 SonarSource.
23 |
24 | Licensed under the [GNU Lesser General Public License, Version 3.0](http://www.gnu.org/licenses/lgpl.txt)
25 |
--------------------------------------------------------------------------------
/its/src/test/resources/aggregate-and-module-based-mixed-coverage/report/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 |
7 | org.example
8 | aggregate-and-module-based-mixed-coverage
9 | 1.0-SNAPSHOT
10 |
11 |
12 | report
13 | jar
14 |
15 |
16 |
17 | org.example
18 | library
19 | ${project.version}
20 | compile
21 |
22 |
23 | org.example
24 | library-test
25 | ${project.version}
26 | test
27 |
28 |
29 |
30 |
31 |
32 |
33 | org.jacoco
34 | jacoco-maven-plugin
35 |
36 |
37 | report-aggregate
38 | verify
39 |
40 | report-aggregate
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "github>SonarSource/renovate-config:languages-team"
5 | ],
6 | "schedule": [
7 | "before 4am on Monday"
8 | ],
9 | "rebaseWhen": "conflicted",
10 | "enabledManagers": [
11 | "gradle",
12 | "gradle-wrapper",
13 | "github-actions"
14 | ],
15 | "packageRules": [
16 | {
17 | "matchManagers": [
18 | "github-actions"
19 | ],
20 | "matchPackageNames": [
21 | "SonarSource/*"
22 | ],
23 | "pinDigests": false,
24 | "groupName": "all Sonar GitHub Actions",
25 | "groupSlug": "all-sonar-github-actions"
26 | },
27 | {
28 | "matchManagers": [
29 | "github-actions"
30 | ],
31 | "matchPackageNames": [
32 | "!SonarSource/*"
33 | ],
34 | "pinDigests": true,
35 | "groupName": "all third-party GitHub Actions",
36 | "groupSlug": "all-3rd-party-github-actions"
37 | },
38 | {
39 | "matchManagers": [
40 | "gradle"
41 | ],
42 | "matchPackageNames": [
43 | "!org.sonarsource.api.plugin:sonar-plugin-api*"
44 | ],
45 | "matchUpdateTypes": [
46 | "minor",
47 | "patch"
48 | ],
49 | "groupName": "all non-major dependencies",
50 | "groupSlug": "all-minor-patch"
51 | },
52 | {
53 | "matchManagers": [
54 | "gradle"
55 | ],
56 | "matchPackageNames": [
57 | "org.sonarsource.api.plugin:sonar-plugin-api*"
58 | ],
59 | "groupName": "sonar-plugin-api",
60 | "groupSlug": "sonar-plugin-api",
61 | "prHeader": "**Before updating the plugin-api version, make sure to check the [compatibility matrix](https://github.com/SonarSource/sonar-plugin-api?tab=readme-ov-file#compatibility) and stick to the lowest denominator.**"
62 | }
63 | ],
64 | "reviewers": [
65 | "team:quality-jvm-squad"
66 | ]
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/org/sonar/plugins/jacoco/SensorUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SonarQube JaCoCo Plugin
3 | * Copyright (C) 2018-2025 SonarSource SA
4 | * mailto:info AT sonarsource DOT com
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 | package org.sonar.plugins.jacoco;
21 |
22 | import java.util.List;
23 | import org.sonar.api.batch.fs.InputFile;
24 | import org.sonar.api.utils.log.Logger;
25 |
26 | class SensorUtils {
27 | private SensorUtils() {
28 | /* This class should not be instantiated */
29 | }
30 |
31 | static void importReport(XmlReportParser reportParser, FileLocator locator, ReportImporter importer, Logger logger) {
32 | List sourceFiles = reportParser.parse();
33 |
34 | for (XmlReportParser.SourceFile sourceFile : sourceFiles) {
35 | InputFile inputFile = locator.getInputFile(sourceFile.packageName(), sourceFile.name());
36 | if (inputFile == null) {
37 | continue;
38 | }
39 |
40 | try {
41 | importer.importCoverage(sourceFile, inputFile);
42 | } catch (IllegalStateException e) {
43 | logger.error("Cannot import coverage information for file '{}', coverage data is invalid. Error: {}", inputFile, e);
44 | }
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/its/src/test/resources/aggregate-and-module-based-mixed-coverage/self-covered/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 |
7 | org.example
8 | aggregate-and-module-based-mixed-coverage
9 | 1.0-SNAPSHOT
10 |
11 |
12 | self-covered
13 | jar
14 |
15 |
16 |
17 | org.junit.jupiter
18 | junit-jupiter-api
19 | 6.0.1
20 | test
21 |
22 |
23 |
24 |
25 |
26 |
27 | org.jacoco
28 | jacoco-maven-plugin
29 | 0.8.14
30 |
31 |
32 | prepare-agent
33 |
34 | prepare-agent
35 |
36 |
37 |
38 | report
39 |
40 | report
41 |
42 |
43 |
44 | XML
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/src/main/java/org/sonar/plugins/jacoco/ReportImporter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SonarQube JaCoCo Plugin
3 | * Copyright (C) 2018-2025 SonarSource SA
4 | * mailto:info AT sonarsource DOT com
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 | package org.sonar.plugins.jacoco;
21 |
22 | import org.sonar.api.batch.fs.InputFile;
23 | import org.sonar.api.batch.sensor.SensorContext;
24 | import org.sonar.api.batch.sensor.coverage.NewCoverage;
25 |
26 | public class ReportImporter {
27 | private final SensorContext ctx;
28 |
29 | public ReportImporter(SensorContext ctx) {
30 | this.ctx = ctx;
31 | }
32 |
33 | public void importCoverage(XmlReportParser.SourceFile sourceFile, InputFile inputFile) {
34 | NewCoverage newCoverage = ctx.newCoverage()
35 | .onFile(inputFile);
36 |
37 | for (XmlReportParser.Line line : sourceFile.lines()) {
38 | boolean conditions = false;
39 | if (line.coveredBranches() > 0 || line.missedBranches() > 0) {
40 | int branches = line.coveredBranches() + line.missedBranches();
41 | newCoverage.conditions(line.number(), branches, line.coveredBranches());
42 | conditions = true;
43 | }
44 | if (conditions || line.coveredInstrs() > 0 || line.missedInstrs() > 0) {
45 | newCoverage.lineHits(line.number(), line.coveredInstrs() > 0 ? 1 : 0);
46 | }
47 | }
48 |
49 | newCoverage.save();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/org/sonar/plugins/jacoco/ReversePathTree.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SonarQube JaCoCo Plugin
3 | * Copyright (C) 2018-2025 SonarSource SA
4 | * mailto:info AT sonarsource DOT com
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 | package org.sonar.plugins.jacoco;
21 |
22 | import java.util.LinkedHashMap;
23 | import java.util.Map;
24 | import org.sonar.api.batch.fs.InputFile;
25 |
26 | public class ReversePathTree {
27 | private Node root = new Node();
28 |
29 | public void index(InputFile inputFile, String[] path) {
30 | Node currentNode = root;
31 | for (int i = path.length - 1; i >= 0; i--) {
32 | currentNode = currentNode.children.computeIfAbsent(path[i], e -> new Node());
33 | }
34 | currentNode.file = inputFile;
35 | }
36 |
37 | public InputFile getFileWithSuffix(String[] path) {
38 | Node currentNode = root;
39 |
40 | for (int i = path.length - 1; i >= 0; i--) {
41 | currentNode = currentNode.children.get(path[i]);
42 | if (currentNode == null) {
43 | return null;
44 | }
45 | }
46 | return getFirstLeaf(currentNode);
47 | }
48 |
49 | private static InputFile getFirstLeaf(Node node) {
50 | while (!node.children.isEmpty()) {
51 | node = node.children.values().iterator().next();
52 | }
53 | return node.file;
54 | }
55 |
56 | static class Node {
57 | Map children = new LinkedHashMap<>();
58 | InputFile file = null;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/org/sonar/plugins/jacoco/JacocoPlugin.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SonarQube JaCoCo Plugin
3 | * Copyright (C) 2018-2025 SonarSource SA
4 | * mailto:info AT sonarsource DOT com
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 | package org.sonar.plugins.jacoco;
21 |
22 | import org.sonar.api.Plugin;
23 | import org.sonar.api.PropertyType;
24 | import org.sonar.api.config.PropertyDefinition;
25 | import org.sonar.api.resources.Qualifiers;
26 |
27 | public class JacocoPlugin implements Plugin {
28 | @Override
29 | public void define(Context context) {
30 | context.addExtension(JacocoSensor.class);
31 | context.addExtension(PropertyDefinition.builder(ReportPathsProvider.REPORT_PATHS_PROPERTY_KEY)
32 | .onQualifiers(Qualifiers.PROJECT)
33 | .multiValues(true)
34 | .category("JaCoCo")
35 | .description("Paths to JaCoCo XML coverage report files. Each path can be either absolute or relative" +
36 | " to the project base directory. Wildcard patterns are accepted (*, ** and ?).")
37 | .build());
38 |
39 | context.addExtension(JacocoAggregateSensor.class);
40 | context.addExtension(PropertyDefinition.builder(ReportPathsProvider.AGGREGATE_REPORT_PATH_PROPERTY_KEY)
41 | .onQualifiers(Qualifiers.PROJECT)
42 | .type(PropertyType.STRING)
43 | .multiValues(false)
44 | .category("JaCoCo")
45 | .description("Single path to aggregate XML coverage report file.")
46 | .build());
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/org/sonar/plugins/jacoco/FileLocator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SonarQube JaCoCo Plugin
3 | * Copyright (C) 2018-2025 SonarSource SA
4 | * mailto:info AT sonarsource DOT com
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 | package org.sonar.plugins.jacoco;
21 |
22 | import java.util.List;
23 | import java.util.stream.Collectors;
24 | import java.util.stream.StreamSupport;
25 | import javax.annotation.CheckForNull;
26 | import org.sonar.api.batch.fs.InputFile;
27 |
28 | public class FileLocator {
29 | private final ReversePathTree tree = new ReversePathTree();
30 | private final KotlinFileLocator kotlinFileLocator;
31 |
32 | public FileLocator(Iterable inputFiles, KotlinFileLocator kotlinFileLocator) {
33 | this(StreamSupport.stream(inputFiles.spliterator(), false).collect(Collectors.toList()), kotlinFileLocator);
34 | }
35 |
36 | public FileLocator(List inputFiles, KotlinFileLocator kotlinFileLocator) {
37 | this.kotlinFileLocator = kotlinFileLocator;
38 | for (InputFile inputFile : inputFiles) {
39 | String[] path = inputFile.relativePath().split("/");
40 | tree.index(inputFile, path);
41 | }
42 | }
43 |
44 | @CheckForNull
45 | public InputFile getInputFile(String packagePath, String fileName) {
46 | String filePath = packagePath.isEmpty() ? fileName : (packagePath + "/" + fileName);
47 | String[] path = filePath.split("/");
48 | InputFile fileWithSuffix = tree.getFileWithSuffix(path);
49 | if (fileWithSuffix == null && fileName.endsWith(".kt")) {
50 | fileWithSuffix = kotlinFileLocator.getInputFile(packagePath, fileName);
51 | }
52 |
53 | return fileWithSuffix;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/test/java/org/sonar/plugins/jacoco/SensorUtilsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SonarQube JaCoCo Plugin
3 | * Copyright (C) 2018-2025 SonarSource SA
4 | * mailto:info AT sonarsource DOT com
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 | package org.sonar.plugins.jacoco;
21 |
22 | import java.util.Collections;
23 | import org.junit.jupiter.api.Test;
24 | import org.sonar.api.batch.fs.InputFile;
25 |
26 | import static org.mockito.Mockito.mock;
27 | import static org.mockito.Mockito.verify;
28 | import static org.mockito.Mockito.verifyNoInteractions;
29 | import static org.mockito.Mockito.when;
30 |
31 | class SensorUtilsTest {
32 |
33 | @Test
34 | void import_coverage() {
35 | FileLocator locator = mock(FileLocator.class);
36 | ReportImporter importer = mock(ReportImporter.class);
37 | XmlReportParser parser = mock(XmlReportParser.class);
38 | InputFile inputFile = mock(InputFile.class);
39 |
40 | XmlReportParser.SourceFile sourceFile = new XmlReportParser.SourceFile("package", "File.java");
41 | sourceFile.lines().add(new XmlReportParser.Line(1, 0, 1, 0, 0));
42 |
43 | when(parser.parse()).thenReturn(Collections.singletonList(sourceFile));
44 | when(locator.getInputFile("package", "File.java")).thenReturn(inputFile);
45 |
46 | SensorUtils.importReport(parser, locator, importer, null);
47 |
48 | verify(importer).importCoverage(sourceFile, inputFile);
49 | }
50 |
51 | @Test
52 | void do_nothing_if_file_not_found() {
53 | FileLocator locator = mock(FileLocator.class);
54 | ReportImporter importer = mock(ReportImporter.class);
55 | XmlReportParser parser = mock(XmlReportParser.class);
56 | XmlReportParser.SourceFile sourceFile = mock(XmlReportParser.SourceFile.class);
57 |
58 | when(parser.parse()).thenReturn(Collections.singletonList(sourceFile));
59 | SensorUtils.importReport(parser, locator, importer, null);
60 |
61 | verifyNoInteractions(importer);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/org/sonar/plugins/jacoco/JacocoAggregateSensor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SonarQube JaCoCo Plugin
3 | * Copyright (C) 2018-2025 SonarSource SA
4 | * mailto:info AT sonarsource DOT com
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 | package org.sonar.plugins.jacoco;
21 |
22 | import java.io.FileNotFoundException;
23 | import java.nio.file.Path;
24 | import java.util.stream.Stream;
25 | import java.util.stream.StreamSupport;
26 | import org.sonar.api.batch.fs.InputFile;
27 | import org.sonar.api.batch.sensor.SensorContext;
28 | import org.sonar.api.batch.sensor.SensorDescriptor;
29 | import org.sonar.api.scanner.sensor.ProjectSensor;
30 | import org.sonar.api.utils.log.Logger;
31 | import org.sonar.api.utils.log.Loggers;
32 |
33 | public class JacocoAggregateSensor implements ProjectSensor {
34 | private static final Logger LOG = Loggers.get(JacocoAggregateSensor.class);
35 |
36 | @Override
37 | public void describe(SensorDescriptor descriptor) {
38 | descriptor.name("JaCoCo Aggregate XML Report Importer");
39 | }
40 |
41 | @Override
42 | public void execute(SensorContext context) {
43 | Path reportPath = null;
44 | try {
45 | reportPath = new ReportPathsProvider(context).getAggregateReportPath();
46 | } catch (FileNotFoundException e) {
47 | LOG.error(String.format("The aggregate JaCoCo sensor will stop: %s", e.getMessage()));
48 | return;
49 | }
50 | if (reportPath == null) {
51 | LOG.debug("No aggregate XML report found. No coverage coverage information will be added at project level.");
52 | return;
53 | }
54 | Iterable inputFiles = context.fileSystem().inputFiles(context.fileSystem().predicates().all());
55 | Stream kotlinInputFileStream = StreamSupport.stream(inputFiles.spliterator(), false).filter(f -> "kotlin".equals(f.language()));
56 | FileLocator locator = new FileLocator(inputFiles, new KotlinFileLocator(kotlinInputFileStream));
57 | ReportImporter importer = new ReportImporter(context);
58 |
59 | LOG.info("Importing aggregate report {}.", reportPath);
60 | SensorUtils.importReport(new XmlReportParser(reportPath), locator, importer, LOG);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/test/java/org/sonar/plugins/jacoco/JacocoPluginTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SonarQube JaCoCo Plugin
3 | * Copyright (C) 2018-2025 SonarSource SA
4 | * mailto:info AT sonarsource DOT com
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 | package org.sonar.plugins.jacoco;
21 |
22 | import org.junit.jupiter.api.Test;
23 | import org.mockito.ArgumentCaptor;
24 | import org.sonar.api.Plugin;
25 | import org.sonar.api.PropertyType;
26 | import org.sonar.api.config.PropertyDefinition;
27 | import org.sonar.api.resources.Qualifiers;
28 |
29 | import static org.assertj.core.api.Assertions.assertThat;
30 | import static org.mockito.Mockito.mock;
31 | import static org.mockito.Mockito.times;
32 | import static org.mockito.Mockito.verify;
33 | import static org.mockito.Mockito.verifyNoMoreInteractions;
34 |
35 | class JacocoPluginTest {
36 | private JacocoPlugin plugin = new JacocoPlugin();
37 | private Plugin.Context ctx = mock(Plugin.Context.class);
38 | @Test
39 | void should_add_sensors_and_property_definitions() {
40 | plugin.define(ctx);
41 |
42 | ArgumentCaptor