Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*******************************************************************************
* Copyright (c) 2009, 2024 Mountainminds GmbH & Co. KG and Contributors
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Evgeny Mandrikov - initial API and implementation
*
*******************************************************************************/
package org.jacoco.core.test.validation.kotlin;

import static org.junit.Assert.assertEquals;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashSet;

import org.jacoco.core.test.validation.ValidationTestBase;
import org.jacoco.core.test.validation.kotlin.targets.KotlinInlineReifiedTarget;
import org.junit.Test;

/**
* Test of code coverage in {@link KotlinInlineReifiedTarget}.
*/
public class KotlinInlineReifiedTest extends ValidationTestBase {

public KotlinInlineReifiedTest() {
super(KotlinInlineReifiedTarget.class);
}

@Test
public void compiler_should_generate_synthetic_method() {
final HashSet<String> names = new HashSet<String>();
for (final Method method : KotlinInlineReifiedTarget.class
.getDeclaredMethods()) {
if (method.isSynthetic()) {
names.add(method.getName());
}
}

assertEquals(Collections.singleton("example"), names);
}

@Test
public void test_method_count() {
assertMethodCount(2);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*******************************************************************************
* Copyright (c) 2009, 2024 Mountainminds GmbH & Co. KG and Contributors
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Evgeny Mandrikov - initial API and implementation
*
*******************************************************************************/
package org.jacoco.core.test.validation.kotlin;

import static org.junit.Assert.assertEquals;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashSet;

import org.jacoco.core.test.validation.ValidationTestBase;
import org.jacoco.core.test.validation.kotlin.targets.KotlinJvmSyntheticTarget;
import org.junit.Test;

/**
* Test of code coverage in {@link KotlinJvmSyntheticTarget}.
*/
public class KotlinJvmSyntheticTest extends ValidationTestBase {

public KotlinJvmSyntheticTest() {
super(KotlinJvmSyntheticTarget.class);
}

@Test
public void compiler_should_generate_synthetic_method() {
final HashSet<String> names = new HashSet<String>();
for (final Method method : KotlinJvmSyntheticTarget.class
.getDeclaredMethods()) {
if (method.isSynthetic()) {
names.add(method.getName());
}
}

assertEquals(Collections.singleton("example"), names);
}

@Test
public void test_method_count() {
assertMethodCount(2);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*******************************************************************************
* Copyright (c) 2009, 2024 Mountainminds GmbH & Co. KG and Contributors
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Evgeny Mandrikov - initial API and implementation
*
*******************************************************************************/
package org.jacoco.core.test.validation.kotlin.targets

import org.jacoco.core.test.validation.targets.Stubs.nop

/**
* Test target containing `inline` function with
* [`reified` type parameter](https://kotlinlang.org/docs/inline-functions.html#reified-type-parameters).
*/
object KotlinInlineReifiedTarget {

private inline fun <reified T> example() { // assertEmpty()
nop(T::class) // assertFullyCovered()
} // assertFullyCovered()

@JvmStatic
fun main(args: Array<String>) {
example<String>()
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*******************************************************************************
* Copyright (c) 2009, 2024 Mountainminds GmbH & Co. KG and Contributors
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Evgeny Mandrikov - initial API and implementation
*
*******************************************************************************/
package org.jacoco.core.test.validation.kotlin.targets

import org.jacoco.core.test.validation.targets.Stubs.nop

/**
* Test target containing [JvmSynthetic] function.
*/
object KotlinJvmSyntheticTarget {

@JvmSynthetic
private fun example() { // assertEmpty()
nop() // assertFullyCovered()
} // assertFullyCovered()

@JvmStatic
fun main(args: Array<String>) {
example()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,59 @@ public void testLambda() {
assertIgnored();
}

/**
* <pre>
* &#064;JvmSynthetic
* fun example() {
* }
* </pre>
*
* @see #should_filter_synthetic_accessor_methods_in_Kotlin_classes()
*/
@Test
public void should_not_filter_synthetic_non_accessor_methods_in_Kotlin_classes() {
context.classAnnotations
.add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC);
final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION,
Opcodes.ACC_SYNTHETIC, "example", "()V", null, null);
m.visitInsn(Opcodes.RETURN);

filter.filter(m, context, output);

assertIgnored();
}

/**
* <pre>
* class Outer {
* private var x = 42
*
* inner class Inner {
* fun example() {
* x += 1
* }
* }
* }
* </pre>
*
* @see #should_not_filter_synthetic_non_accessor_methods_in_Kotlin_classes()
*/
@Test
public void should_filter_synthetic_accessor_methods_in_Kotlin_classes() {
context.classAnnotations
.add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC);
final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION,
Opcodes.ACC_SYNTHETIC, "access$getX$p", "(Outer;)I", null,
null);
m.visitVarInsn(Opcodes.ALOAD, 0);
m.visitFieldInsn(Opcodes.GETFIELD, "Outer", "x", "I");
m.visitInsn(Opcodes.IRETURN);

filter.filter(m, context, output);

assertMethodIgnored(m);
}

@Test
public void should_filter_synthetic_method_with_prefix_anonfun_in_non_Scala_classes() {
final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import java.util.List;

import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
Expand All @@ -29,17 +28,6 @@
*/
public final class KotlinCoroutineFilter implements IFilter {

static boolean isImplementationOfSuspendFunction(
final MethodNode methodNode) {
if (methodNode.name.startsWith("access$")) {
return false;
}
final Type methodType = Type.getMethodType(methodNode.desc);
final int lastArgument = methodType.getArgumentTypes().length - 1;
return lastArgument >= 0 && "kotlin.coroutines.Continuation".equals(
methodType.getArgumentTypes()[lastArgument].getClassName());
}

public void filter(final MethodNode methodNode,
final IFilterContext context, final IFilterOutput output) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,13 @@
*/
public final class KotlinDefaultArgumentsFilter implements IFilter {

static boolean isDefaultArgumentsMethod(final MethodNode methodNode) {
private static boolean isDefaultArgumentsMethod(
final MethodNode methodNode) {
return methodNode.name.endsWith("$default");
}

static boolean isDefaultArgumentsConstructor(final MethodNode methodNode) {
private static boolean isDefaultArgumentsConstructor(
final MethodNode methodNode) {
if (!"<init>".equals(methodNode.name)) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,7 @@ public void filter(final MethodNode methodNode,
}

if (KotlinGeneratedFilter.isKotlinClass(context)) {
if (KotlinDefaultArgumentsFilter
.isDefaultArgumentsMethod(methodNode)) {
return;
}

if (KotlinDefaultArgumentsFilter
.isDefaultArgumentsConstructor(methodNode)) {
return;
}

if (KotlinCoroutineFilter
.isImplementationOfSuspendFunction(methodNode)) {
if (!methodNode.name.startsWith("access$")) {
return;
}
}
Expand Down
8 changes: 7 additions & 1 deletion org.jacoco.doc/docroot/doc/changes.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@ <h2>Snapshot Build @qualified.bundle.version@ (@build.date@)</h2>

<h3>New Features</h3>
<ul>
<li>Calculation of line coverage for Kotlin inline functions
<li>Calculation of line coverage for Kotlin <code>inline</code> functions
(GitHub <a href="https://github.com/jacoco/jacoco/issues/1670">#1670</a>).</li>
<li>Calculation of line coverage for Kotlin <code>inline</code> functions
with <code>reified</code> type parameter
(GitHub <a href="https://github.com/jacoco/jacoco/issues/1670">#1670</a>,
<a href="https://github.com/jacoco/jacoco/issues/1700">#1700</a>).</li>
<li>Calculation of coverage for Kotlin <code>JvmSynthetic</code> functions
(GitHub <a href="https://github.com/jacoco/jacoco/issues/1700">#1700</a>).</li>
<li>Experimental support for Java 24 class files
(GitHub <a href="https://github.com/jacoco/jacoco/issues/1631">#1631</a>).</li>
<li>Part of bytecode generated by the Kotlin Compose compiler plugin is
Expand Down