Skip to content

Add filter for instructions inlined by Kotlin compiler #764

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

Merged
merged 33 commits into from
Dec 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
1d932e7
(WIP) add validation test
Godin Oct 9, 2018
09bb132
(WIP) add unit test
Godin Oct 9, 2018
169eb95
(WIP) filters should have access to SourceDebugExtension attribute
Godin Oct 9, 2018
04523d4
(WIP) add filter
Godin Oct 9, 2018
3a3db75
(WIP) simplify validation test
Godin Oct 9, 2018
e13adca
(WIP) simplify code, add javadocs
Godin Oct 9, 2018
9c21e54
(WIP) add unit tests
Godin Oct 9, 2018
98dfd30
(WIP) remove unused variable
Godin Oct 18, 2018
2290659
Merge branch 'master' into filter_kotlin_inline
Godin Oct 23, 2018
dae6687
(WIP) reorder imports
Godin Oct 23, 2018
3a445bd
(WIP) assert: last line in coverage is less or equal to lines in source
Godin Oct 23, 2018
ab5c324
(WIP) ignored instructions should affect computation of lines
Godin Oct 23, 2018
4292efb
(WIP) do not reparse SourceDebugExtension attribute
Godin Oct 24, 2018
653a6ef
Merge branch 'master' into filter_kotlin_inline
Godin Oct 24, 2018
8f8cea6
Merge branch 'master' into filter_kotlin_inline
Godin Nov 19, 2018
37da973
Merge branch 'master' into filter_kotlin_inline
Godin Nov 27, 2018
ebe8437
(WIP) get rid of warning in Eclipse
Godin Nov 27, 2018
d333f2c
(WIP) move assertion into separate test
Godin Nov 27, 2018
24ed5cd
(WIP) simplify regexp
Godin Nov 27, 2018
f083c88
(WIP) rename readLine into expectLine
Godin Nov 27, 2018
680dd1b
(WIP) add comment about StringReader and IOException
Godin Nov 27, 2018
cda6733
(WIP) Replace AssertionError by IllegalStateException
Godin Nov 27, 2018
179bddc
(WIP) improve wording
Godin Nov 27, 2018
db22afd
(WIP) reuse method isKotlinClass
Godin Nov 27, 2018
32c3761
(WIP) avoid infinite loop
Godin Nov 27, 2018
24d2f9c
(WIP) update changelog
Godin Nov 30, 2018
f6967ee
Add missing final.
marchof Dec 6, 2018
aac9ce4
(WIP) update test to prevent accidental merge
Godin Dec 6, 2018
7e7724f
(WIP) add unit test
Godin Dec 6, 2018
788abf0
(WIP) use only main "Kotlin" strata instead of optional "KotlinDebug"
Godin Dec 6, 2018
2e7f6aa
(WIP) add case into validation test
Godin Dec 7, 2018
524958b
(WIP) clarify entry in changelog
Godin Dec 7, 2018
289eaa4
(WIP) add comments into unit test
Godin Dec 7, 2018
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,26 @@
/*******************************************************************************
* 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.test.validation.kotlin;

import org.jacoco.core.test.validation.ValidationTestBase;
import org.jacoco.core.test.validation.kotlin.targets.KotlinInlineTarget;

/**
* Test of inline functions.
*/
public class KotlinInlineTest extends ValidationTestBase {

public KotlinInlineTest() {
super(KotlinInlineTarget.class);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*******************************************************************************
* 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.test.validation.kotlin.targets

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

/**
* Test target for `inline` functions.
*/
object KotlinInlineTarget {

inline fun inlined() {
nop() // assertNotCovered()
}

@JvmStatic
fun main(args: Array) {

inlined() // assertFullyCovered()

/* Following inlined method for some reasons doesn't appear in SMAP: */
assert(t()) // assertPartlyCovered(2, 2)

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ public void linear_instruction_sequence_should_ignore_instructions_when_filter_i
probes[0] = true;
runMethodAnalzer(NOP_FILTER);

assertEquals(1002, result.getFirstLine());
assertEquals(1002, result.getLastLine());

assertLine(1001, 0, 0, 0, 0);
assertLine(1002, 0, 1, 0, 0);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class FilterContextMock implements IFilterContext {
public String superClassName = "java/lang/Object";
public Set classAnnotations = new HashSet();
public String sourceFileName = "Foo.java";
public String sourceDebugExtension;

public String getClassName() {
return className;
Expand All @@ -40,4 +41,8 @@ public String getSourceFileName() {
return sourceFileName;
}

public String getSourceDebugExtension() {
return sourceDebugExtension;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
/*******************************************************************************
* 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import java.util.ArrayList;
import java.util.List;

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 tests for {@link KotlinInlineFilter}.
*/
public class KotlinInlineFilterTest extends FilterTestBase {

private final KotlinInlineFilter filter = new KotlinInlineFilter();

private final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
"callsite", "()V", null, null);

@Test
public void should_filter() {
context.sourceFileName = "callsite.kt";
context.sourceDebugExtension = "" //
+ "SMAP\n" //
+ "callsite.kt\n" // OutputFileName=callsite.kt
+ "Kotlin\n" // DefaultStratumId=Kotlin
+ "*S Kotlin\n" // StratumID=Kotlin
+ "*F\n" // FileSection
+ "+ 1 callsite.kt\n" // FileID=1,FileName=callsite.kt
+ "CallsiteKt\n" //
+ "+ 2 a.kt\n" // FileID=2,FileName=a.kt
+ "AKt\n" //
+ "+ 3 b.kt\n" // FileID=3,FileName=b.kt
+ "BKt\n" //
+ "*L\n" // LineSection
+ "1#1,8:1\n" // InputStartLine=1,LineFileID=1,RepeatCount=8,OutputStartLine=1
+ "2#2,2:9\n" // InputStartLine=2,LineFileID=2,RepeatCount=2,OutputStartLine=9
+ "2#3,2:11\n" // InputStartLine=2,LineFileID=3,RepeatCount=2,OutputStartLine=11
+ "*E\n"; // EndSection
context.classAnnotations
.add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC);

m.visitLineNumber(2, new Label());
m.visitInsn(Opcodes.NOP);

m.visitLineNumber(9, new Label());
shouldIgnorePrevious(m);
m.visitMethodInsn(Opcodes.INVOKESTATIC, "Stubs", "nop", "()V", false);
shouldIgnorePrevious(m);
m.visitLineNumber(10, new Label());
shouldIgnorePrevious(m);
m.visitInsn(Opcodes.NOP);
shouldIgnorePrevious(m);

m.visitLineNumber(3, new Label());
m.visitInsn(Opcodes.NOP);

m.visitLineNumber(11, new Label());
shouldIgnorePrevious(m);
m.visitMethodInsn(Opcodes.INVOKESTATIC, "Stubs", "nop", "()V", false);
shouldIgnorePrevious(m);
m.visitLineNumber(12, new Label());
shouldIgnorePrevious(m);
m.visitInsn(Opcodes.NOP);
shouldIgnorePrevious(m);

m.visitLineNumber(4, new Label());
m.visitInsn(Opcodes.RETURN);

filter.filter(m, context, output);

assertIgnored(expectedRanges.toArray(new Range[0]));

// should not reparse:
context.sourceDebugExtension = "";
filter.filter(m, context, output);
}

@Test
public void should_filter_when_in_same_file() {
context.sourceFileName = "callsite.kt";
context.sourceDebugExtension = "" //
+ "SMAP\n" //
+ "callsite.kt\n" // OutputFileName=callsite.kt
+ "Kotlin\n" // DefaultStratumId=Kotlin
+ "*S Kotlin\n" // StratumID=Kotlin
+ "*F\n" // FileSection
+ "+ 1 callsite.kt\n" // FileID=1,FileName=callsite.kt
+ "CallsiteKt\n" //
+ "*L\n" // LineSection
+ "1#1,33:1\n" // InputStartLine=1,LineFileID=1,RepeatCount=33,OutputStartLine=1
+ "22#1,2:34\n" // InputStartLine=22,LineFileID=1,RepeatCount=2,OutputStartLine=34
+ "*E\n"; // EndSection
context.classAnnotations
.add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC);

m.visitLineNumber(28, new Label());
m.visitInsn(Opcodes.NOP);

m.visitLineNumber(34, new Label());
shouldIgnorePrevious(m);
m.visitMethodInsn(Opcodes.INVOKESTATIC, "Stubs", "nop", "()V", false);
shouldIgnorePrevious(m);
m.visitLineNumber(35, new Label());
shouldIgnorePrevious(m);
m.visitInsn(Opcodes.NOP);
shouldIgnorePrevious(m);

m.visitLineNumber(30, new Label());
m.visitInsn(Opcodes.RETURN);

filter.filter(m, context, output);

assertIgnored(expectedRanges.toArray(new Range[0]));
}

@Test
public void should_not_parse_SourceDebugExtension_attribute_when_no_kotlin_metadata_annotation() {
context.sourceDebugExtension = "SMAP";

m.visitLineNumber(1, new Label());
m.visitInsn(Opcodes.RETURN);

filter.filter(m, context, output);

assertIgnored();
}

@Test
public void should_not_filter_when_no_SourceDebugExtension_attribute() {
context.classAnnotations
.add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC);

m.visitLineNumber(1, new Label());
m.visitInsn(Opcodes.RETURN);

filter.filter(m, context, output);

assertIgnored();
}

@Test
public void should_throw_exception_when_SMAP_incomplete() {
context.sourceDebugExtension = "" //
+ "SMAP\n";
context.classAnnotations
.add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC);

try {
filter.filter(m, context, output);
fail("exception expected");
} catch (final IllegalStateException e) {
assertEquals("Unexpected SMAP line: null", e.getMessage());
}
}

@Test
public void should_throw_exception_when_unexpected_FileInfo() {
context.sourceFileName = "callsite.kt";
context.sourceDebugExtension = "" //
+ "SMAP\n" //
+ "callsite.kt\n" //
+ "Kotlin\n" //
+ "*S Kotlin\n" //
+ "*F\n" //
+ "xxx";
context.classAnnotations
.add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC);

try {
filter.filter(m, context, output);
fail("exception expected");
} catch (final IllegalStateException e) {
assertEquals("Unexpected SMAP line: xxx", e.getMessage());
}
}

@Test
public void should_throw_exception_when_unexpected_LineInfo() {
context.sourceFileName = "callsite.kt";
context.sourceDebugExtension = "" //
+ "SMAP\n" //
+ "callsite.kt\n" //
+ "Kotlin\n" //
+ "*S Kotlin\n" //
+ "*F\n" //
+ "*L\n" //
+ "xxx";
context.classAnnotations
.add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC);

try {
filter.filter(m, context, output);
fail("exception expected");
} catch (final IllegalStateException e) {
assertEquals("Unexpected SMAP line: xxx", e.getMessage());
}
}

private final List expectedRanges = new ArrayList();

private void shouldIgnorePrevious(final MethodNode m) {
expectedRanges.add(
new Range(m.instructions.getLast(), m.instructions.getLast()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
package org.jacoco.core.test.validation;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.IOException;
import java.lang.reflect.Method;
Expand Down Expand Up @@ -113,6 +114,15 @@ public void execute_assertions_in_comments() throws IOException {
}
}

@Test
public void last_line_in_coverage_data_should_be_less_or_equal_to_number_of_lines_in_source_file() {
assertTrue(String.format(
"Last line in coverage data (%d) should be less or equal to number of lines in source file (%d)",
Integer.valueOf(source.getCoverage().getLastLine()),
Integer.valueOf(source.getLines().size())),
source.getCoverage().getLastLine() <= source.getLines().size());
}

/*
* Predefined assertion methods:
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,16 @@
public class ClassAnalyzer extends ClassProbesVisitor
implements IFilterContext {

private final IFilter filter = Filters.ALL;

private final ClassCoverageImpl coverage;
private final boolean[] probes;
private final StringPool stringPool;

private final Set classAnnotations = new HashSet();

private String sourceDebugExtension;

private final IFilter filter;

/**
* Creates a new analyzer that builds coverage data for a class.
*
Expand All @@ -54,6 +56,7 @@ public ClassAnalyzer(final ClassCoverageImpl coverage,
this.coverage = coverage;
this.probes = probes;
this.stringPool = stringPool;
this.filter = Filters.all();
}

@Override
Expand All @@ -75,6 +78,7 @@ public AnnotationVisitor visitAnnotation(final String desc,
@Override
public void visitSource(final String source, final String debug) {
coverage.setSourceFileName(stringPool.get(source));
sourceDebugExtension = debug;
}

@Override
Expand Down Expand Up @@ -146,4 +150,8 @@ public String getSourceFileName() {
return coverage.getSourceFileName();
}

public String getSourceDebugExtension() {
return sourceDebugExtension;
}

}
Loading