- which versions are you using (SonarQube, Scanner, Plugin, and any relevant extension)
- SonarQube Enterprise Edition Version 9.9 (build 65466)
- Gradle Plugin version 3.4.0.2513
- jacoco toolVersion = “0.8.10”
- Gradle 7.4.2
- how is SonarQube deployed: Docker
- what are you trying to achieve
- SonarQube coverage should match Jacoco coverage
I have Gradle Java multi-module project.
Problem: SonarQube says that MyAwesomeClassModule1.callFromDifferentModule
is not covered by tests but it is covered by test in other module.
Module1:
package org.example;
public class MyAwesomeClassModule1 {
public int callFromTheSameModule(int a, int b) {
return a + b;
}
public int callFromDifferentModule(int a, int b) {
return a - b;
}
}
test in Module1:
package org.example;
import org.junit.Assert;
import org.junit.Test;
public class MyAwesomeClassModule1Test {
@Test
public void callFromTheSameModule() {
Assert.assertEquals(3, new MyAwesomeClassModule1().callFromTheSameModule(1, 2));
}
}
test in Module2 that covers class in Module1:
package org.example;
import org.junit.Assert;
import org.junit.Test;
public class MyAwesomeClassModule2Test {
@Test
public void callFromDifferentModule() {
Assert.assertEquals(-1, new MyAwesomeClassModule1().callFromDifferentModule(1, 2));
}
}
Root build.gradle
:
plugins {
id 'jacoco'
id 'java'
id 'org.sonarqube' version '3.4.0.2513'
}
apply plugin: 'org.sonarqube'
sonarqube {
properties {
property "sonar.projectKey", "multi"
property "sonar.host.url", "http://localhost:9000"
property "sonar.login", "sqp_432705d91e78439505e76b60745102d5a3aadfc7"
property "sonar.coverage.jacoco.xmlReportPaths", "$buildDir/reports/jacoco/jacocoRootReport/jacocoRootReport.xml"
property "sonar.verbose", "true"
}
}
group = 'org.example'
version = '1.0-SNAPSHOT'
allprojects {
repositories {
mavenCentral()
}
apply plugin: 'jacoco'
jacoco {
toolVersion = "0.8.10"
}
}
subprojects {
apply plugin: 'java'
apply plugin: 'java-library'
sourceCompatibility = 11
jacocoTestReport {
reports {
xml.required = true
html.required = true
}
dependsOn test
mustRunAfter test
}
test {
dependsOn cleanTest
testLogging.showStandardStreams = true
workingDir(projectDir.getParent())
testLogging {
exceptionFormat "full"
}
beforeTest { descriptor ->
logger.lifecycle("Running test: " + descriptor)
}
useJUnitPlatform {
includeEngines("junit-jupiter", "junit-vintage")
}
finalizedBy jacocoTestReport
}
apply plugin: 'idea'
idea {
module {
sourceDirs += file("src/main/generated/main/java")
generatedSourceDirs += file("src/main/generated/main/java")
downloadSources = true
downloadJavadoc = false
inheritOutputDirs = false
outputDir = file('.out/classes/main')
testOutputDir = file('.out/classes/test')
testSourceDirs += file('src/test/java')
testSourceDirs += file('src/test/cassandra')
}
}
dependencies {
testImplementation 'junit:junit:4.13.2'
testImplementation "org.junit.jupiter:junit-jupiter-api:5.9.3"
testImplementation "org.junit.jupiter:junit-jupiter-engine:5.9.3"
testImplementation "org.junit.jupiter:junit-jupiter-params:5.9.3"
testRuntimeOnly('org.junit.vintage:junit-vintage-engine:5.9.3')
}
}
task jacocoRootReport(type: JacocoReport) {
description = 'Generates an aggregate report from all subprojects'
dependsOn(subprojects.jacocoTestReport)
additionalSourceDirs.from = files(subprojects.sourceSets.main.allSource.srcDirs).filter { f -> f.exists() }
sourceDirectories.from = files(subprojects.sourceSets.main.allSource.srcDirs).filter { f -> f.exists() }
classDirectories.from = files(subprojects.sourceSets.main.output).filter { f -> f.exists() }
getExecutionData().setFrom(fileTree(projectDir).include("**/jacoco/*.exec"))
reports {
xml.required = true
html.required = true
}
}
check.finalizedBy tasks.jacocoRootReport
tasks.jacocoRootReport.finalizedBy("sonarqube")
tasks.sonarqube.mustRunAfter("jacocoRootReport")
Jacoco shows that both methods are covered build/reports/jacoco/jacocoRootReport/jacocoRootReport.xml
:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><!DOCTYPE report PUBLIC "-//JACOCO//DTD Report 1.1//EN"
"report.dtd">
<report name="multi">
<sessioninfo id="MacBook-Pro-10.local-16e8272c" start="1687892217117" dump="1687892217680"/>
<sessioninfo id="MacBook-Pro-10.local-11d44b8e" start="1687892218237" dump="1687892218799"/>
<package name="org/example">
<class name="org/example/MyAwesomeClassModule1" sourcefilename="MyAwesomeClassModule1.java">
<method name="<init>" desc="()V" line="3">
<counter type="INSTRUCTION" missed="0" covered="3"/>
<counter type="LINE" missed="0" covered="1"/>
<counter type="COMPLEXITY" missed="0" covered="1"/>
<counter type="METHOD" missed="0" covered="1"/>
</method>
<method name="callFromTheSameModule" desc="(II)I" line="5">
<counter type="INSTRUCTION" missed="0" covered="4"/>
<counter type="LINE" missed="0" covered="1"/>
<counter type="COMPLEXITY" missed="0" covered="1"/>
<counter type="METHOD" missed="0" covered="1"/>
</method>
<method name="callFromDifferentModule" desc="(II)I" line="9">
<counter type="INSTRUCTION" missed="0" covered="4"/>
<counter type="LINE" missed="0" covered="1"/>
<counter type="COMPLEXITY" missed="0" covered="1"/>
<counter type="METHOD" missed="0" covered="1"/>
</method>
<counter type="INSTRUCTION" missed="0" covered="11"/>
<counter type="LINE" missed="0" covered="3"/>
<counter type="COMPLEXITY" missed="0" covered="3"/>
<counter type="METHOD" missed="0" covered="3"/>
<counter type="CLASS" missed="0" covered="1"/>
</class>
<sourcefile name="MyAwesomeClassModule1.java">
<line nr="3" mi="0" ci="3" mb="0" cb="0"/>
<line nr="5" mi="0" ci="4" mb="0" cb="0"/>
<line nr="9" mi="0" ci="4" mb="0" cb="0"/>
<counter type="INSTRUCTION" missed="0" covered="11"/>
<counter type="LINE" missed="0" covered="3"/>
<counter type="COMPLEXITY" missed="0" covered="3"/>
<counter type="METHOD" missed="0" covered="3"/>
<counter type="CLASS" missed="0" covered="1"/>
</sourcefile>
<counter type="INSTRUCTION" missed="0" covered="11"/>
<counter type="LINE" missed="0" covered="3"/>
<counter type="COMPLEXITY" missed="0" covered="3"/>
<counter type="METHOD" missed="0" covered="3"/>
<counter type="CLASS" missed="0" covered="1"/>
</package>
<counter type="INSTRUCTION" missed="0" covered="11"/>
<counter type="LINE" missed="0" covered="3"/>
<counter type="COMPLEXITY" missed="0" covered="3"/>
<counter type="METHOD" missed="0" covered="3"/>
<counter type="CLASS" missed="0" covered="1"/>
</report>
However SonarQube shows that callFromDifferentModule
method is not covered.
Could you please suggest what I can be missing in configuration that leads to this behavior?
build/sonar/scanner-report/analysis.log
:
Plugins:
Bundled analyzers:
- Python Code Quality and Security 3.24.0.10784 (python)
- Go Code Quality and Security 1.11.0.3905 (go)
- JaCoCo 1.3.0.1538 (jacoco)
- Kotlin Code Quality and Security 2.12.0.1956 (kotlin)
- IaC Code Quality and Security 1.11.0.2847 (iac)
- JavaScript/TypeScript/CSS Code Quality and Security 9.13.0.20537 (javascript)
- Ruby Code Quality and Security 1.11.0.3905 (ruby)
- Scala Code Quality and Security 1.11.0.3905 (sonarscala)
- C# Code Quality and Security 8.51.0.59060 (csharp)
- Java Code Quality and Security 7.16.0.30901 (java)
- HTML Code Quality and Security 3.7.1.3306 (web)
- Flex Code Quality and Security 2.8.0.3166 (flex)
- XML Code Quality and Security 2.7.0.3820 (xml)
- PHP Code Quality and Security 3.27.1.9352 (php)
- Text Code Quality and Security 2.0.2.1090 (text)
- VB.NET Code Quality and Security 8.51.0.59060 (vbnet)
- Configuration detection fot Code Quality and Security 1.2.0.267 (config)
Global server settings:
- sonar.core.id=147B411E-AYj7Iy2JeWCOTM7vJ1Wa
- sonar.core.startTime=2023-06-27T18:48:02+0000
- sonar.forceAuthentication=true
Project server settings:
Project scanner properties:
- sonar.coverage.jacoco.xmlReportPaths=/Users/username/Repos/multi/build/reports/jacoco/jacocoRootReport/jacocoRootReport.xml
- sonar.host.url=http://localhost:9000
- sonar.java.jdkHome=/Library/Java/JavaVirtualMachines/applejdk-11.0.15.10.2.jdk/Contents/Home
- sonar.java.source=11
- sonar.java.target=11
- sonar.login=******
- sonar.modules=:module1,:module2
- sonar.projectBaseDir=/Users/username/Repos/multi
- sonar.projectKey=multi
- sonar.projectName=multi
- sonar.projectVersion=1.0-SNAPSHOT
- sonar.scanner.app=ScannerGradle
- sonar.scanner.appVersion=3.4.0.2513/Gradle 7.4.2
- sonar.sourceEncoding=UTF-8
- sonar.sources=
- sonar.verbose=true
- sonar.working.directory=/Users/username/Repos/multi/build/sonar
Scanner properties of module: multi:module2
- sonar.coverage.jacoco.xmlReportPaths=/Users/username/Repos/multi/module2/build/reports/jacoco/test/jacocoTestReport.xml
- sonar.jacoco.reportPath=/Users/username/Repos/multi/module2/build/jacoco/test.exec
- sonar.jacoco.reportPaths=/Users/username/Repos/multi/module2/build/jacoco/test.exec
- sonar.java.libraries=/Users/username/Repos/multi/module1/build/classes/java/main
- sonar.java.test.binaries=/Users/username/Repos/multi/module2/build/classes/java/test
- sonar.java.test.libraries=/Users/username/Repos/multi/module1/build/classes/java/main,/Users/username/.gradle/caches/modules-2/files-2.1/junit/junit/4.13.2/8ac9e16d933b6fb43bc7f576336b8f4d7eb5ba12/junit-4.13.2.jar,/Users/username/.gradle/caches/modules-2/files-2.1/org.junit.jupiter/junit-jupiter-params/5.9.3/9e2a4bf6016a1975f408a73523392875cff7c26f/junit-jupiter-params-5.9.3.jar,/Users/username/.gradle/caches/modules-2/files-2.1/org.junit.platform/junit-platform-engine/1.9.3/8616734a190f8d307376aeb7353dba0a2c037a09/junit-platform-engine-1.9.3.jar,/Users/username/.gradle/caches/modules-2/files-2.1/org.junit.platform/junit-platform-commons/1.9.3/36b2e26a90c41603be7f0094bee80e3f8a2cd4d4/junit-platform-commons-1.9.3.jar,/Users/username/.gradle/caches/modules-2/files-2.1/org.junit.jupiter/junit-jupiter-engine/5.9.3/355322b03bf39306a183162cd06626c206f0286b/junit-jupiter-engine-5.9.3.jar,/Users/username/.gradle/caches/modules-2/files-2.1/org.junit.jupiter/junit-jupiter-api/5.9.3/8...
- sonar.junit.reportPaths=/Users/username/Repos/multi/module2/build/test-results/test
- sonar.junit.reportsPath=/Users/username/Repos/multi/module2/build/test-results/test
- sonar.libraries=/Users/username/Repos/multi/module1/build/classes/java/main
- sonar.moduleKey=multi:module2
- sonar.projectBaseDir=/Users/username/Repos/multi/module2
- sonar.projectKey=multi:module2
- sonar.projectName=module2
- sonar.projectVersion=unspecified
- sonar.sources=/Users/username/Repos/multi/module2/src/main/java
- sonar.surefire.reportsPath=/Users/username/Repos/multi/module2/build/test-results/test
- sonar.tests=/Users/username/Repos/multi/module2/src/test/java
Scanner properties of module: multi:module1
- sonar.binaries=/Users/username/Repos/multi/module1/build/classes/java/main
- sonar.coverage.jacoco.xmlReportPaths=/Users/username/Repos/multi/module1/build/reports/jacoco/test/jacocoTestReport.xml
- sonar.jacoco.reportPath=/Users/username/Repos/multi/module1/build/jacoco/test.exec
- sonar.jacoco.reportPaths=/Users/username/Repos/multi/module1/build/jacoco/test.exec
- sonar.java.binaries=/Users/username/Repos/multi/module1/build/classes/java/main
- sonar.java.test.binaries=/Users/username/Repos/multi/module1/build/classes/java/test
- sonar.java.test.libraries=/Users/username/Repos/multi/module1/build/classes/java/main,/Users/username/.gradle/caches/modules-2/files-2.1/junit/junit/4.13.2/8ac9e16d933b6fb43bc7f576336b8f4d7eb5ba12/junit-4.13.2.jar,/Users/username/.gradle/caches/modules-2/files-2.1/org.junit.jupiter/junit-jupiter-params/5.9.3/9e2a4bf6016a1975f408a73523392875cff7c26f/junit-jupiter-params-5.9.3.jar,/Users/username/.gradle/caches/modules-2/files-2.1/org.junit.platform/junit-platform-engine/1.9.3/8616734a190f8d307376aeb7353dba0a2c037a09/junit-platform-engine-1.9.3.jar,/Users/username/.gradle/caches/modules-2/files-2.1/org.junit.platform/junit-platform-commons/1.9.3/36b2e26a90c41603be7f0094bee80e3f8a2cd4d4/junit-platform-commons-1.9.3.jar,/Users/username/.gradle/caches/modules-2/files-2.1/org.junit.jupiter/junit-jupiter-engine/5.9.3/355322b03bf39306a183162cd06626c206f0286b/junit-jupiter-engine-5.9.3.jar,/Users/username/.gradle/caches/modules-2/files-2.1/org.junit.jupiter/junit-jupiter-api/5.9.3/8...
- sonar.junit.reportPaths=/Users/username/Repos/multi/module1/build/test-results/test
- sonar.junit.reportsPath=/Users/username/Repos/multi/module1/build/test-results/test
- sonar.moduleKey=multi:module1
- sonar.projectBaseDir=/Users/username/Repos/multi/module1
- sonar.projectKey=multi:module1
- sonar.projectName=module1
- sonar.projectVersion=unspecified
- sonar.sources=/Users/username/Repos/multi/module1/src/main/java
- sonar.surefire.reportsPath=/Users/username/Repos/multi/module1/build/test-results/test
- sonar.tests=/Users/username/Repos/multi/module1/src/test/java