Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Indicate in code which methods are not covered by Jacoco instead of being forced to drop the coverage ratio #1598

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
@@ -0,0 +1,86 @@
/*******************************************************************************
* 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:
* Orestis Gkorgkas - 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.Opcodes;
import org.objectweb.asm.tree.MethodNode;

/**
* Unit tests for {@link ManuallyIgnoredFilter}.
*/
public class ManuallyIgnoredFilterTest extends FilterTestBase {

public static final String SOME_METHOD_NAME = "someMethodName";
private static final String ANNOTATION_VALUE = "L"
+ ManuallyIgnoredFilter.ANNOTATION_VALUE + ";";
private final IFilter filter = new ManuallyIgnoredFilter();

@Test
public void should_filter_methods_annotated_with_runtime_invisible_annotation_JacocoIgnored() {
final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
SOME_METHOD_NAME, "()I", null, null);
m.visitAnnotation(ANNOTATION_VALUE, true);

m.visitInsn(Opcodes.ICONST_0);
m.visitInsn(Opcodes.IRETURN);

filter.filter(m, context, output);

assertMethodIgnored(m);
}

@Test
public void should_filter_classes_annotated_with_runtime_visible_annotation_JacocoIgnored() {
final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
"readExternal", "()V", null, null);

m.visitInsn(Opcodes.NOP);
context.classAnnotations.add(ANNOTATION_VALUE);

filter.filter(m, context, output);

assertMethodIgnored(m);
}

@Test
public void should_not_filter_when_no_annotations() {
final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
"hashCode", "()I", null, null);

m.visitInsn(Opcodes.ICONST_0);
m.visitInsn(Opcodes.IRETURN);

filter.filter(m, context, output);

assertIgnored();
}

@Test
public void should_not_filter_when_other_annotations() {
final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
"hashCode", "()I", null, null);
m.visitAnnotation("LOtherAnnotation;", true);

m.visitInsn(Opcodes.ICONST_0);
m.visitInsn(Opcodes.IRETURN);

context.classAnnotations.add("LOtherAnnotation;");

filter.filter(m, context, output);

assertIgnored();
}

}
Expand Up @@ -48,7 +48,8 @@ public static IFilter all() {
new KotlinUnsafeCastOperatorFilter(),
new KotlinNotNullOperatorFilter(),
new KotlinDefaultArgumentsFilter(), new KotlinInlineFilter(),
new KotlinCoroutineFilter(), new KotlinDefaultMethodsFilter());
new KotlinCoroutineFilter(), new KotlinDefaultMethodsFilter(),
new ManuallyIgnoredFilter());
}

private Filters(final IFilter... filters) {
Expand Down
@@ -0,0 +1,67 @@
/*******************************************************************************
* 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:
* Orestis Gkorgkas - initial API and implementation
*
*******************************************************************************/
package org.jacoco.core.internal.analysis.filter;

import java.util.List;

import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.MethodNode;

/**
* Filters out any annotation that is named has the value "JacocoIgnored".
* Example: The following method that for some reason is not reachable by tests
* will be ignored by Jacoco:
*
* <pre>{@code @JacocoIgnored
* public void foo(Object bar) {
* // DO SOMETHING HERE
* }
*
* }</pre>
*/
public class ManuallyIgnoredFilter implements IFilter {

public static final String ANNOTATION_VALUE = "JacocoIgnored";

public void filter(MethodNode methodNode, IFilterContext context,
IFilterOutput output) {
for (String annotation : context.getClassAnnotations()) {
if (matches(annotation)) {
output.ignore(methodNode.instructions.getFirst(),
methodNode.instructions.getLast());
return;
}
}

if (presentIn(methodNode.visibleAnnotations)) {
output.ignore(methodNode.instructions.getFirst(),
methodNode.instructions.getLast());
}

}

private static boolean matches(final String annotation) {
return annotation.contains(ANNOTATION_VALUE);
}

private static boolean presentIn(final List<AnnotationNode> annotations) {
if (annotations != null) {
for (AnnotationNode annotation : annotations) {
if (matches(annotation.desc)) {
return true;
}
}
}
return false;
}
}