类似于“Why is JaCoCo not covering my String switch statements?”:
JaCoCo 执行字节码分析,而不是源代码。 Example.kt与kotlinc 1.3.10的编译
package example
fun main(args: Array<String>) {
kotlinx.coroutines.runBlocking { // line 4
}
}
产生两个文件ExampleKt.class和ExampleKt$main$1.class,最后一个的字节码(javap -v -p ExampleKt$main$1.class)包含方法invokeSuspend(Object)
public final java.lang.Object invokeSuspend(java.lang.Object);
descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
flags: ACC_PUBLIC, ACC_FINAL
Code:
stack=3, locals=4, args_size=2
0: invokestatic #29 // Method kotlin/coroutines/intrinsics/IntrinsicsKt.getCOROUTINE_SUSPENDED:()Ljava/lang/Object;
3: astore_3
4: aload_0
5: getfield #33 // Field label:I
8: tableswitch { // 0 to 0
0: 28
default: 53
}
28: aload_1
29: dup
30: instanceof #35 // class kotlin/Result$Failure
33: ifeq 43
36: checkcast #35 // class kotlin/Result$Failure
39: getfield #39 // Field kotlin/Result$Failure.exception:Ljava/lang/Throwable;
42: athrow
43: pop
44: aload_0
45: getfield #41 // Field p$:Lkotlinx/coroutines/CoroutineScope;
48: astore_2
49: getstatic #47 // Field kotlin/Unit.INSTANCE:Lkotlin/Unit;
52: areturn
53: new #49 // class java/lang/IllegalStateException
56: dup
57: ldc #51 // String call to 'resume' before 'invoke' with coroutine
59: invokespecial #55 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
62: athrow
LineNumberTable:
line 4: 3
line 5: 49
与源文件的第 4 行关联并包含分支(ifeq、tableswitch)。
虽然截至今天最新的 JaCoCo 版本 (0.8.2) 具有针对各种编译器生成的工件的过滤器,例如 switch 语句中的 String,但 Kotlin 编译器为协程生成的字节码没有被过滤。更改日志可以在 https://www.jacoco.org/jacoco/trunk/doc/changes.html 看到。除此之外,https://www.jacoco.org/research/index.html 还有 presentation about bytecode pattern matching 显示/解释了许多编译器生成的工件。
您在 IntelliJ IDEA 中看到的 100% - 只是线路覆盖率,因此您试图比较两个完全不同的事物。作为证明 - 这是 IntelliJ IDEA 的屏幕截图,它显示了 100% 的线路覆盖率,但只执行了 if 的一个分支(其中 args.size >= 0 的计算结果为 true)
这里是JaCoCo报告执行相同源文件的对应截图
上升到包级别,您可以看到 100% 的线路覆盖率,但 50% 的分支覆盖率
然后通过第一个链接ExampleKt.main.new Function2() {...} 进入类级别,您可以再次看到方法invokeSuspend(Object) 贡献了错过的分支
更新 (29/01/2019)
JaCoCo version 0.8.3 对 Kotlin 编译器添加的用于挂起 lambda 和函数的分支进行过滤: