Missing branch with when and enum when running Jacoco coverage

Viewed 664

I using Jacoco 0.8.6 and I have an enum with 3 values. I have this when shown on the image below and I have a test for each branch but Jacoco is still saying that I am missing a branch, also my var is non-null. What is wrong? enter image description here

enter image description here

2 Answers

JaCoCo performs analysis of bytecode. For Example.kt

object Example {
    var type: Type = Type.SETUP_LOGIN
        set(value) {
            field = value
            when (value) {
                Type.SETUP_LOGIN -> nop()
                Type.CHANGE_PIN -> nop()
                Type.CHANGE_FINGERPRINT -> nop()
            }
        }

    fun nop() {
    }

    enum class Type {
        SETUP_LOGIN, CHANGE_PIN, CHANGE_FINGERPRINT
    }
}

compiled by

kotlinc-1.4.30/bin/kotlinc Example.kt

execution of

java -v -p Example.class

shows following bytecode for setType method

public final void setType(Example$Type);
    descriptor: (LExample$Type;)V
    flags: ACC_PUBLIC, ACC_FINAL
    Code:
      stack=2, locals=2, args_size=2
         0: aload_1
         1: ldc           #17                 // String value
         3: invokestatic  #23                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
         6: aload_1
         7: putstatic     #11                 // Field type:LExample$Type;
        10: aload_1
        11: getstatic     #29                 // Field Example$WhenMappings.$EnumSwitchMapping$0:[I
        14: swap
        15: invokevirtual #35                 // Method Example$Type.ordinal:()I
        18: iaload
        19: tableswitch   { // 1 to 3
                       1: 44
                       2: 51
                       3: 58
                 default: 65
            }
        44: aload_0
        45: invokevirtual #39                 // Method nop:()V
        48: goto          65
        51: aload_0
        52: invokevirtual #39                 // Method nop:()V
        55: goto          65
        58: aload_0
        59: invokevirtual #39                 // Method nop:()V
        62: goto          65
        65: return
      LineNumberTable:
        line 4: 6
        line 5: 10
        line 6: 44
        line 7: 51
        line 8: 58
        line 9: 65
        line 10: 65

where as you can see tableswitch has 4 branches and this is exactly what you see in the JaCoCo report.

Looking at such bytecode only (as like JaCoCo does) there is no way to realize that default branch of tableswitch can't be reached - add one more value to enum, without changing anything else, recompile and you'll see exactly the same bytecode for this setType method.

You can use exhaustive

val <T> T.exhaustive: T
get() = this

then

when (value) {
    Type.SETUP_LOGIN -> nop()
    Type.CHANGE_PIN -> nop()
    Type.CHANGE_FINGERPRINT -> nop()
}.exhaustive
Related