aboutsummaryrefslogtreecommitdiff
path: root/org.jacoco.core.test
diff options
context:
space:
mode:
authorEvgeny Mandrikov <Godin@users.noreply.github.com>2018-12-21 12:37:39 +0100
committerMarc R. Hoffmann <hoffmann@mountainminds.com>2018-12-21 12:37:39 +0100
commitd919b8e8c1341c2713094efd539ff4b5a54b3598 (patch)
treec95072b12620cc1502d91b2d8b58222cf6319671 /org.jacoco.core.test
parent9ad5e6ca90822fc0f99fc2c1600d41d38ec69e47 (diff)
downloadjacoco-d919b8e8c1341c2713094efd539ff4b5a54b3598.tar.gz
Add filter for Kotlin coroutines (#802)
Diffstat (limited to 'org.jacoco.core.test')
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/AbstractMatcherTest.java13
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilterTest.java135
2 files changed, 144 insertions, 4 deletions
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/AbstractMatcherTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/AbstractMatcherTest.java
index 69c520ff..e89bd419 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/AbstractMatcherTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/AbstractMatcherTest.java
@@ -223,23 +223,28 @@ public class AbstractMatcherTest {
}
@Test
- public void nextIsNew() {
+ public void nextIsType() {
m.visitInsn(Opcodes.NOP);
m.visitTypeInsn(Opcodes.NEW, "descriptor");
+ // should set cursor to null when opcode mismatch
+ matcher.cursor = m.instructions.getFirst();
+ matcher.nextIsType(Opcodes.CHECKCAST, "descriptor");
+ assertNull(matcher.cursor);
+
// should set cursor to null when descriptor mismatch
matcher.cursor = m.instructions.getFirst();
- matcher.nextIsNew("another_descriptor");
+ matcher.nextIsType(Opcodes.NEW, "another_descriptor");
assertNull(matcher.cursor);
// should set cursor to next instruction when match
matcher.cursor = m.instructions.getFirst();
- matcher.nextIsNew("descriptor");
+ matcher.nextIsType(Opcodes.NEW, "descriptor");
assertSame(m.instructions.getLast(), matcher.cursor);
// should not do anything when cursor is null
matcher.cursor = null;
- matcher.nextIsNew("descriptor");
+ matcher.nextIsType(Opcodes.NEW, "descriptor");
}
@Test
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilterTest.java
new file mode 100644
index 00000000..13cdc397
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinCoroutineFilterTest.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2018 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Evgeny Mandrikov - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.internal.analysis.filter;
+
+import org.jacoco.core.internal.instr.InstrSupport;
+import org.junit.Test;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.MethodNode;
+
+/**
+ * Unit test for {@link KotlinCoroutineFilter}.
+ */
+public class KotlinCoroutineFilterTest extends FilterTestBase {
+
+ private final IFilter filter = new KotlinCoroutineFilter();
+
+ private final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
+ "invokeSuspend", "(Ljava/lang/Object;)Ljava/lang/Object;", null,
+ null);
+
+ /**
+ * <pre>
+ * runBlocking {
+ * nop()
+ * suspendingFunction()
+ * nop()
+ * }
+ * </pre>
+ */
+ @Test
+ public void should_filter() {
+ context.classAnnotations
+ .add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC);
+
+ m.visitLabel(new Label());
+ m.visitMethodInsn(Opcodes.INVOKESTATIC,
+ "kotlin/coroutines/intrinsics/IntrinsicsKt",
+ "getCOROUTINE_SUSPENDED", "()Ljava/lang/Object;", false);
+ m.visitVarInsn(Opcodes.ASTORE, 3);
+
+ m.visitVarInsn(Opcodes.ALOAD, 0);
+ // line of "runBlocking"
+ m.visitFieldInsn(Opcodes.GETFIELD, "", "label", "I");
+ final Label dflt = new Label();
+ final Label state0 = new Label();
+ final Label state1 = new Label();
+ m.visitTableSwitchInsn(0, 1, dflt, state0, state1);
+ final Range range1 = new Range();
+ range1.fromInclusive = m.instructions.getLast();
+
+ m.visitLabel(state0);
+
+ {
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitInsn(Opcodes.DUP);
+ m.visitTypeInsn(Opcodes.INSTANCEOF, "kotlin/Result$Failure");
+ Label label = new Label();
+ m.visitJumpInsn(Opcodes.IFEQ, label);
+ m.visitTypeInsn(Opcodes.CHECKCAST, "kotlin/Result$Failure");
+ m.visitFieldInsn(Opcodes.GETFIELD, "kotlin/Result$Failure",
+ "exception", "Ljava/lang/Throwable");
+ m.visitInsn(Opcodes.ATHROW);
+ m.visitInsn(Opcodes.POP);
+ range1.toInclusive = m.instructions.getLast();
+ m.visitLabel(label);
+ }
+
+ // line before "suspendingFunction"
+ m.visitInsn(Opcodes.NOP);
+
+ // line of "suspendingFunction"
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "", "suspendingFunction",
+ "(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", false);
+
+ m.visitInsn(Opcodes.DUP);
+ final Range range2 = new Range();
+ range2.fromInclusive = m.instructions.getLast();
+ m.visitVarInsn(Opcodes.ALOAD, 3);
+ final Label continuationLabelAfterLoadedResult = new Label();
+ m.visitJumpInsn(Opcodes.IF_ACMPNE, continuationLabelAfterLoadedResult);
+ // line of "runBlocking"
+ m.visitVarInsn(Opcodes.ALOAD, 3);
+ m.visitInsn(Opcodes.ARETURN);
+
+ m.visitLabel(state1);
+
+ {
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitInsn(Opcodes.DUP);
+ m.visitTypeInsn(Opcodes.INSTANCEOF, "kotlin/Result$Failure");
+ final Label label = new Label();
+ m.visitJumpInsn(Opcodes.IFEQ, label);
+ m.visitTypeInsn(Opcodes.CHECKCAST, "kotlin/Result$Failure");
+ m.visitFieldInsn(Opcodes.GETFIELD, "kotlin/Result$Failure",
+ "exception", "Ljava/lang/Throwable");
+ m.visitInsn(Opcodes.ATHROW);
+ m.visitInsn(Opcodes.POP);
+ m.visitLabel(label);
+ }
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ range2.toInclusive = m.instructions.getLast();
+ m.visitLabel(continuationLabelAfterLoadedResult);
+
+ // line after "suspendingFunction"
+ m.visitInsn(Opcodes.NOP);
+ m.visitInsn(Opcodes.ARETURN);
+
+ m.visitLabel(dflt);
+ final Range range0 = new Range();
+ range0.fromInclusive = m.instructions.getLast();
+ m.visitTypeInsn(Opcodes.NEW, "java/lang/IllegalStateException");
+ m.visitInsn(Opcodes.DUP);
+ m.visitLdcInsn("call to 'resume' before 'invoke' with coroutine");
+ m.visitMethodInsn(Opcodes.INVOKESPECIAL,
+ "java/lang/IllegalStateException", "<init>",
+ "(Ljava/lang/String;)V", false);
+ m.visitInsn(Opcodes.ATHROW);
+ range0.toInclusive = m.instructions.getLast();
+
+ filter.filter(m, context, output);
+
+ assertIgnored(range0, range1, range2);
+ }
+
+}