jacoco icon indicating copy to clipboard operation
jacoco copied to clipboard

JaCoCo 0.8.14 version reduces the test coverage results for switch statement (parameter type String) when used with forEach

Open Sofiya-Sundaramoorthy opened this issue 1 month ago • 1 comments

JaCoCo version: 0.8.14 JDK version: 25 OS: Mac Tools: Maven Github: https://github.com/Sofiya-Sundaramoorthy/jacoco-0.8.14-switch-statement Brief description: When upgrading the JaCoCo version from 0.8.12 to 0.8.14 to support JDK25 upgrades, I noticed that the test coverage dropped for a switch statement in combination with forEach.

checkData method takes List<String> as parameter which then loops using forEach to pass data to a switch statement

// method

protected String checkData(List<String> datas) {
        datas.forEach(data -> {
            switch(data) {
                case "a" -> System.out.println("first data");
                case "b" -> System.out.println("second data");
                case "c" -> System.out.println("third data");
                default -> System.out.println("default data");
            };
        });
        return null;
    };

//testcase

@Test
	void testData() {
		var application = new SwitchStatementApplication();
		var result1 = application.checkData(List.of("a", "b", "c", "d"));
		assertNull(result1);
	}

The minimal reproducible code is in the github repo https://github.com/Sofiya-Sundaramoorthy/jacoco-0.8.14-switch-statement

Note: The switch statement takes parameter of type String which gives 78% test coverage. However, when passing parameter type of Int the coverage is 100%

Expected behaviour: The combination of forEach + switch (parameter type as String) should produce 100% test coverage

Actual behaviour: The combination of forEach + switch (parameter type as String) produces 78% test coverage

Please refer the screenshots

JaCoCo 0.8.12 with JDK21

Image Image

JaCoCo 0.8.14 with JDK25

Image Image

Sofiya-Sundaramoorthy avatar Nov 29 '25 05:11 Sofiya-Sundaramoorthy

First of all thank you for the reproducer! ❤️


Should be noted that this is not a regression in JaCoCo, but change in javac compiler in JDK 24:

for src/Example.java

import java.util.List;

public class Example {
    String checkData(List<String> datas) {
        datas.forEach(data -> {
            switch(data) {
                case "a" -> System.out.println("first data");
                case "b" -> System.out.println("second data");
                case "c" -> System.out.println("third data");
                default -> System.out.println("default data");
            };
        });
        return null;
    };

    public static void main(String[] args) {
        new Example().checkData(List.of("a", "b", "c", "d"));
    }
}

using JaCoCo version 0.8.12 execution of

javac --release 21 -g -d classes src/Example.java
java  -javaagent:jacoco-0.8.12/lib/jacocoagent.jar -cp classes Example
java -jar jacoco-0.8.12/lib/jacococli.jar \
    report jacoco.exec \
    --classfiles classes \
    --sourcefiles src \
    --html report

with javac 23.0.2 produces expected result

All 4 branches covered.

whereas with javac 24.0.2 produces unexpected result

3 of 14 branches missed.

For these two versions execution of

javap -v -p classes/Example.class

produces two different outputs

       LocalVariableTable:
         Start  Length  Slot  Name   Signature
+            2     143     1   s0$   Ljava/lang/String;
+            4     141     2 tmp1$   I
             0     146     0  data   Ljava/lang/String;

showing change of LocalVariableTable in method lambda$checkData$0 that contains body of the lambda.

Which affects

https://github.com/jacoco/jacoco/blob/2eb248366f0eb63fd964fc7a81804b27229a6edd/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/StringSwitchJavacFilter.java#L60-L62

Godin avatar Dec 01 '25 10:12 Godin