aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc R. Hoffmann <hoffmann@mountainminds.com>2015-02-16 09:09:25 +0100
committerMarc R. Hoffmann <hoffmann@mountainminds.com>2015-05-22 11:16:59 +0200
commit873114568ee148ea2e15cd036d57bf7b43ba154d (patch)
tree96244e1b3a49b2458ea4bb03194d8441b71b09e2
parent93c1f077e9856dae19c0a7b9bce963181bfd3fc3 (diff)
downloadjacoco-873114568ee148ea2e15cd036d57bf7b43ba154d.tar.gz
Improve coverage data for code with implicit exception.
Add additional probe before every line with at least one method invocation.
-rw-r--r--jacoco-maven-plugin.test/it/it-report-nomatch/nomatch.execbin24 -> 24 bytes
-rw-r--r--org.jacoco.ant.test/src/org/jacoco/ant/CreateExecFiles.java46
-rw-r--r--org.jacoco.ant.test/src/org/jacoco/ant/InstrumentTaskTest.xml8
-rw-r--r--org.jacoco.ant.test/src/org/jacoco/ant/data/nomatch.execbin42 -> 42 bytes
-rw-r--r--org.jacoco.ant.test/src/org/jacoco/ant/data/sample1.exec2
-rw-r--r--org.jacoco.ant.test/src/org/jacoco/ant/data/sample2.exec2
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/analysis/AnalyzerTest.java4
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelFlowAnalyzerTest.java25
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelInfoTest.java37
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/test/validation/ExceptionsTest.java6
-rw-r--r--org.jacoco.core/src/org/jacoco/core/data/ExecutionDataWriter.java2
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/flow/LabelFlowAnalyzer.java18
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/flow/LabelInfo.java39
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesAdapter.java5
-rw-r--r--org.jacoco.doc/docroot/doc/changes.html8
-rw-r--r--org.jacoco.doc/docroot/doc/faq.html6
-rw-r--r--org.jacoco.doc/docroot/doc/flow.html21
17 files changed, 205 insertions, 24 deletions
diff --git a/jacoco-maven-plugin.test/it/it-report-nomatch/nomatch.exec b/jacoco-maven-plugin.test/it/it-report-nomatch/nomatch.exec
index 31cad96f..b3396529 100644
--- a/jacoco-maven-plugin.test/it/it-report-nomatch/nomatch.exec
+++ b/jacoco-maven-plugin.test/it/it-report-nomatch/nomatch.exec
Binary files differ
diff --git a/org.jacoco.ant.test/src/org/jacoco/ant/CreateExecFiles.java b/org.jacoco.ant.test/src/org/jacoco/ant/CreateExecFiles.java
new file mode 100644
index 00000000..cbe9f90f
--- /dev/null
+++ b/org.jacoco.ant.test/src/org/jacoco/ant/CreateExecFiles.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2015 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.ant;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.jacoco.core.data.ExecutionData;
+import org.jacoco.core.data.ExecutionDataWriter;
+
+/**
+ * Utility class to create exec files required for some Ant tests.
+ */
+public class CreateExecFiles {
+
+ private static final String BASE_LOCATION = "./src/org/jacoco/ant/data/";
+
+ public static void main(String[] args) throws IOException {
+ OutputStream out;
+
+ out = new FileOutputStream(BASE_LOCATION + "sample1.exec");
+ new ExecutionDataWriter(out);
+ out.close();
+
+ out = new FileOutputStream(BASE_LOCATION + "sample2.exec");
+ new ExecutionDataWriter(out);
+ out.close();
+
+ out = new FileOutputStream(BASE_LOCATION + "nomatch.exec");
+ ExecutionDataWriter writer = new ExecutionDataWriter(out);
+ writer.visitClassExecution(new ExecutionData(0,
+ "org/jacoco/ant/TestTarget", new boolean[0]));
+ out.close();
+ }
+
+}
diff --git a/org.jacoco.ant.test/src/org/jacoco/ant/InstrumentTaskTest.xml b/org.jacoco.ant.test/src/org/jacoco/ant/InstrumentTaskTest.xml
index 92cef76f..79a21bab 100644
--- a/org.jacoco.ant.test/src/org/jacoco/ant/InstrumentTaskTest.xml
+++ b/org.jacoco.ant.test/src/org/jacoco/ant/InstrumentTaskTest.xml
@@ -64,7 +64,7 @@
<jacoco:instrument destdir="${instr.dir}">
<fileset dir="${lib.dir}" includes="*.jar"/>
</jacoco:instrument>
- <au:assertLogContains text="Instrumented 14 classes to ${temp.dir}"/>
+ <au:assertLogContains text="Instrumented 15 classes to ${temp.dir}"/>
<unzip src="${instr.dir}/test.jar" dest="${instr.dir}"/>
<au:assertFileDoesntExist file="${instr.dir}/META-INF/TEST.RSA" />
@@ -85,7 +85,7 @@
<jacoco:instrument destdir="${instr.dir}" removesignatures="false">
<fileset dir="${lib.dir}" includes="*.jar"/>
</jacoco:instrument>
- <au:assertLogContains text="Instrumented 14 classes to ${temp.dir}"/>
+ <au:assertLogContains text="Instrumented 15 classes to ${temp.dir}"/>
<unzip src="${instr.dir}/test.jar" dest="${instr.dir}"/>
<au:assertFileExists file="${instr.dir}/META-INF/TEST.RSA" />
@@ -96,7 +96,7 @@
<jacoco:instrument destdir="${temp.dir}">
<fileset dir="${org.jacoco.ant.instrumentTaskTest.classes.dir}" includes="**/*.class"/>
</jacoco:instrument>
- <au:assertLogContains text="Instrumented 14 classes to ${temp.dir}"/>
+ <au:assertLogContains text="Instrumented 15 classes to ${temp.dir}"/>
<au:assertFileExists file="${temp.dir}/org/jacoco/ant/InstrumentTaskTest.class" />
<echo file="${temp.dir}/jacoco-agent.properties">destfile=test.exec</echo>
@@ -113,7 +113,7 @@
<jacoco:instrument destdir="${temp.dir}">
<fileset dir="${org.jacoco.ant.instrumentTaskTest.classes.dir}" includes="**/*.class"/>
</jacoco:instrument>
- <au:assertLogContains text="Instrumented 14 classes to ${temp.dir}"/>
+ <au:assertLogContains text="Instrumented 15 classes to ${temp.dir}"/>
<au:assertFileExists file="${temp.dir}/org/jacoco/ant/InstrumentTaskTest.class" />
<java classname="org.jacoco.ant.TestTarget" failonerror="true" fork="true">
diff --git a/org.jacoco.ant.test/src/org/jacoco/ant/data/nomatch.exec b/org.jacoco.ant.test/src/org/jacoco/ant/data/nomatch.exec
index ef7d62ae..66d78df2 100644
--- a/org.jacoco.ant.test/src/org/jacoco/ant/data/nomatch.exec
+++ b/org.jacoco.ant.test/src/org/jacoco/ant/data/nomatch.exec
Binary files differ
diff --git a/org.jacoco.ant.test/src/org/jacoco/ant/data/sample1.exec b/org.jacoco.ant.test/src/org/jacoco/ant/data/sample1.exec
index b9c3ab93..2e87d6ca 100644
--- a/org.jacoco.ant.test/src/org/jacoco/ant/data/sample1.exec
+++ b/org.jacoco.ant.test/src/org/jacoco/ant/data/sample1.exec
@@ -1 +1 @@
-ÀÀ \ No newline at end of file
+ÀÀ \ No newline at end of file
diff --git a/org.jacoco.ant.test/src/org/jacoco/ant/data/sample2.exec b/org.jacoco.ant.test/src/org/jacoco/ant/data/sample2.exec
index b9c3ab93..2e87d6ca 100644
--- a/org.jacoco.ant.test/src/org/jacoco/ant/data/sample2.exec
+++ b/org.jacoco.ant.test/src/org/jacoco/ant/data/sample2.exec
@@ -1 +1 @@
-ÀÀ \ No newline at end of file
+ÀÀ \ No newline at end of file
diff --git a/org.jacoco.core.test/src/org/jacoco/core/analysis/AnalyzerTest.java b/org.jacoco.core.test/src/org/jacoco/core/analysis/AnalyzerTest.java
index f63762e9..6a9275e8 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/analysis/AnalyzerTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/analysis/AnalyzerTest.java
@@ -93,7 +93,7 @@ public class AnalyzerTest {
final byte[] bytes = TargetLoader
.getClassDataAsBytes(AnalyzerTest.class);
executionData.get(Long.valueOf(CRC64.checksum(bytes)),
- "org/jacoco/core/analysis/AnalyzerTest", 100);
+ "org/jacoco/core/analysis/AnalyzerTest", 200);
analyzer.analyzeClass(bytes, "Test");
assertFalse(classes.get("org/jacoco/core/analysis/AnalyzerTest")
.isNoMatch());
@@ -102,7 +102,7 @@ public class AnalyzerTest {
@Test
public void testAnalyzeClassNoIdMatch() throws IOException {
executionData.get(Long.valueOf(0),
- "org/jacoco/core/analysis/AnalyzerTest", 100);
+ "org/jacoco/core/analysis/AnalyzerTest", 200);
analyzer.analyzeClass(
TargetLoader.getClassDataAsBytes(AnalyzerTest.class), "Test");
assertTrue(classes.get("org/jacoco/core/analysis/AnalyzerTest")
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelFlowAnalyzerTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelFlowAnalyzerTest.java
index 440b4f38..8081c675 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelFlowAnalyzerTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelFlowAnalyzerTest.java
@@ -12,6 +12,8 @@
package org.jacoco.core.internal.flow;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.objectweb.asm.Opcodes.*;
@@ -36,6 +38,13 @@ public class LabelFlowAnalyzerTest {
}
@Test
+ public void testInit() {
+ assertFalse(analyzer.successor);
+ assertTrue(analyzer.first);
+ assertNull(analyzer.lineStart);
+ }
+
+ @Test
public void testFlowScenario01() {
assertFalse(LabelInfo.isMultiTarget(label));
assertFalse(LabelInfo.isSuccessor(label));
@@ -134,12 +143,6 @@ public class LabelFlowAnalyzerTest {
}
@Test
- public void testInit() {
- assertFalse(analyzer.successor);
- assertTrue(analyzer.first);
- }
-
- @Test
public void testInsn() {
testInsn(NOP, true);
testInsn(ACONST_NULL, true);
@@ -294,17 +297,27 @@ public class LabelFlowAnalyzerTest {
}
@Test
+ public void testLineNumber() {
+ analyzer.visitLineNumber(42, label);
+ assertSame(label, analyzer.lineStart);
+ }
+
+ @Test
public void testMethodInsn() {
+ analyzer.visitLineNumber(42, label);
analyzer.visitMethodInsn(INVOKEVIRTUAL, "Foo", "doit", "()V", false);
assertTrue(analyzer.successor);
assertFalse(analyzer.first);
+ assertTrue(LabelInfo.isMethodInvocationLine(label));
}
@Test
public void testInvokeDynamicInsn() {
+ analyzer.visitLineNumber(42, label);
analyzer.visitInvokeDynamicInsn("foo", "()V", null);
assertTrue(analyzer.successor);
assertFalse(analyzer.first);
+ assertTrue(LabelInfo.isMethodInvocationLine(label));
}
@Test
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelInfoTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelInfoTest.java
index c5d74a6c..d83bee6e 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelInfoTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/flow/LabelInfoTest.java
@@ -37,6 +37,7 @@ public class LabelInfoTest {
public void testDefaults() {
assertFalse(LabelInfo.isMultiTarget(label));
assertFalse(LabelInfo.isSuccessor(label));
+ assertFalse(LabelInfo.isMethodInvocationLine(label));
assertFalse(LabelInfo.isDone(label));
assertEquals(LabelInfo.NO_PROBE, LabelInfo.getProbeId(label));
assertNull(LabelInfo.getIntermediateLabel(label));
@@ -90,6 +91,42 @@ public class LabelInfoTest {
}
@Test
+ public void testMethodInvocationLine() {
+ LabelInfo.setMethodInvocationLine(label);
+ assertTrue(LabelInfo.isMethodInvocationLine(label));
+ }
+
+ @Test
+ public void testNeedsProbe() {
+ testNeedsProbe(false, false, false, false);
+ testNeedsProbe(true, false, false, false);
+ testNeedsProbe(false, true, false, false);
+ testNeedsProbe(true, true, false, false);
+ testNeedsProbe(false, false, true, false);
+ testNeedsProbe(true, false, true, true);
+ testNeedsProbe(false, true, true, true);
+ testNeedsProbe(true, true, true, true);
+ }
+
+ private void testNeedsProbe(boolean multitarget,
+ boolean methodinvocationline, boolean successor, boolean expected) {
+ if (multitarget) {
+ LabelInfo.setTarget(label);
+ LabelInfo.setTarget(label);
+ }
+ if (methodinvocationline) {
+ LabelInfo.setMethodInvocationLine(label);
+ }
+ if (successor) {
+ LabelInfo.setSuccessor(label);
+ }
+ assertTrue(expected == LabelInfo.needsProbe(label));
+
+ // Reset:
+ label = new Label();
+ }
+
+ @Test
public void testSetResetDone1() {
LabelInfo.setDone(label);
assertTrue(LabelInfo.isDone(label));
diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/validation/ExceptionsTest.java b/org.jacoco.core.test/src/org/jacoco/core/test/validation/ExceptionsTest.java
index 03517826..40080914 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/test/validation/ExceptionsTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/test/validation/ExceptionsTest.java
@@ -36,7 +36,7 @@ public class ExceptionsTest extends ValidationTestBase {
// 1. Implicit Exception
// Currently no coverage at all, as we don't see when a block aborts
// somewhere in the middle.
- assertLine("implicitException.before", ICounter.NOT_COVERED);
+ assertLine("implicitException.before", ICounter.FULLY_COVERED);
assertLine("implicitException.exception", ICounter.NOT_COVERED);
assertLine("implicitException.after", ICounter.NOT_COVERED);
@@ -55,7 +55,7 @@ public class ExceptionsTest extends ValidationTestBase {
// somewhere in the middle.
assertLine("implicitExceptionTryCatch.beforeBlock",
ICounter.FULLY_COVERED);
- assertLine("implicitExceptionTryCatch.before", ICounter.NOT_COVERED);
+ assertLine("implicitExceptionTryCatch.before", ICounter.FULLY_COVERED);
assertLine("implicitExceptionTryCatch.exception", ICounter.NOT_COVERED);
assertLine("implicitExceptionTryCatch.after", ICounter.NOT_COVERED);
assertLine("implicitExceptionTryCatch.catchBlock",
@@ -89,7 +89,7 @@ public class ExceptionsTest extends ValidationTestBase {
// Finally block is yellow as the non-exception path is missing.
assertLine("implicitExceptionFinally.beforeBlock",
ICounter.FULLY_COVERED);
- assertLine("implicitExceptionFinally.before", ICounter.NOT_COVERED);
+ assertLine("implicitExceptionFinally.before", ICounter.FULLY_COVERED);
assertLine("implicitExceptionFinally.exception", ICounter.NOT_COVERED);
assertLine("implicitExceptionFinally.after", ICounter.NOT_COVERED);
assertLine("implicitExceptionFinally.finallyBlock",
diff --git a/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataWriter.java b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataWriter.java
index d986e536..1095d95d 100644
--- a/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataWriter.java
+++ b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataWriter.java
@@ -24,7 +24,7 @@ public class ExecutionDataWriter implements ISessionInfoVisitor,
IExecutionDataVisitor {
/** File format version, will be incremented for each incompatible change. */
- public static final char FORMAT_VERSION = 0x1006;
+ public static final char FORMAT_VERSION = 0x1007;
/** Magic number in header for file format identification. */
public static final char MAGIC_NUMBER = 0xC0C0;
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/flow/LabelFlowAnalyzer.java b/org.jacoco.core/src/org/jacoco/core/internal/flow/LabelFlowAnalyzer.java
index 08176dfd..157416ad 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/flow/LabelFlowAnalyzer.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/flow/LabelFlowAnalyzer.java
@@ -54,6 +54,11 @@ public final class LabelFlowAnalyzer extends MethodVisitor {
boolean first = true;
/**
+ * Label instance of the last line start.
+ */
+ Label lineStart = null;
+
+ /**
* Create new instance.
*/
public LabelFlowAnalyzer() {
@@ -94,6 +99,11 @@ public final class LabelFlowAnalyzer extends MethodVisitor {
}
@Override
+ public void visitLineNumber(final int line, final Label start) {
+ lineStart = start;
+ }
+
+ @Override
public void visitTableSwitchInsn(final int min, final int max,
final Label dflt, final Label... labels) {
visitSwitchInsn(dflt, labels);
@@ -174,6 +184,7 @@ public final class LabelFlowAnalyzer extends MethodVisitor {
final String name, final String desc, final boolean itf) {
successor = true;
first = false;
+ markMethodInvocationLine();
}
@Override
@@ -181,6 +192,13 @@ public final class LabelFlowAnalyzer extends MethodVisitor {
final Handle bsm, final Object... bsmArgs) {
successor = true;
first = false;
+ markMethodInvocationLine();
+ }
+
+ private void markMethodInvocationLine() {
+ if (lineStart != null) {
+ LabelInfo.setMethodInvocationLine(lineStart);
+ }
}
@Override
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/flow/LabelInfo.java b/org.jacoco.core/src/org/jacoco/core/internal/flow/LabelInfo.java
index 956761da..c6f1b763 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/flow/LabelInfo.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/flow/LabelInfo.java
@@ -31,6 +31,8 @@ public final class LabelInfo {
private boolean successor = false;
+ private boolean methodInvocationLine = false;
+
private boolean done = false;
private int probeid = NO_PROBE;
@@ -105,6 +107,43 @@ public final class LabelInfo {
}
/**
+ * Mark a given label as the beginning of a line with method invocations.
+ *
+ * @param label
+ * label to mark
+ */
+ public static void setMethodInvocationLine(final Label label) {
+ create(label).methodInvocationLine = true;
+ }
+
+ /**
+ * Checks whether the a given label has been marked as a line with method
+ * invocations.
+ *
+ * @param label
+ * label to check
+ * @return <code>true</code> if the label represents a line with method
+ * invocations
+ */
+ public static boolean isMethodInvocationLine(final Label label) {
+ final LabelInfo info = get(label);
+ return info == null ? false : info.methodInvocationLine;
+ }
+
+ /**
+ * Determines whether the given label needs a probe to be inserted before.
+ *
+ * @param label
+ * label to test
+ * @return <code>true</code> if a probe should be inserted before
+ */
+ public static boolean needsProbe(final Label label) {
+ final LabelInfo info = get(label);
+ return info != null && info.successor
+ && (info.multiTarget || info.methodInvocationLine);
+ }
+
+ /**
* Mark a given label as done.
*
* @param label
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesAdapter.java b/org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesAdapter.java
index 32ef9f10..5081fffc 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesAdapter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/flow/MethodProbesAdapter.java
@@ -68,8 +68,7 @@ public final class MethodProbesAdapter extends MethodVisitor {
// a different label for the try-catch block.
if (tryCatchProbeLabels.containsKey(start)) {
start = tryCatchProbeLabels.get(start);
- } else if (LabelInfo.isMultiTarget(start)
- && LabelInfo.isSuccessor(start)) {
+ } else if (LabelInfo.needsProbe(start)) {
final Label probeLabel = new Label();
LabelInfo.setSuccessor(probeLabel);
tryCatchProbeLabels.put(start, probeLabel);
@@ -80,7 +79,7 @@ public final class MethodProbesAdapter extends MethodVisitor {
@Override
public void visitLabel(final Label label) {
- if (LabelInfo.isMultiTarget(label) && LabelInfo.isSuccessor(label)) {
+ if (LabelInfo.needsProbe(label)) {
if (tryCatchProbeLabels.containsKey(label)) {
probesVisitor.visitLabel(tryCatchProbeLabels.get(label));
}
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index 458f858b..24182079 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -22,6 +22,8 @@
<h3>New Features</h3>
<ul>
+ <li>Better detection of coverage in code blocks with implicit exceptions.
+ (GitHub <a href="https://github.com/jacoco/jacoco/issues/310">#310</a>).</li>
<li>Added lifecycle-mapping-metadata.xml for M2E
(GitHub <a href="https://github.com/jacoco/jacoco/issues/203">#203</a>).</li>
<li>Allow locales with country and variant for Ant report task
@@ -36,6 +38,12 @@
(GitHub <a href="https://github.com/jacoco/jacoco/issues/301">#301</a>).</li>
</ul>
+<h3>API Changes</h3>
+<ul>
+ <li>The exec file version has been updated and is not compatible with previous
+ versions.</li>
+</ul>
+
<h2>Release 0.7.4 (2015/02/26)</h2>
<h3>Fixed Bugs</h3>
diff --git a/org.jacoco.doc/docroot/doc/faq.html b/org.jacoco.doc/docroot/doc/faq.html
index 1748b4dc..62d81143 100644
--- a/org.jacoco.doc/docroot/doc/faq.html
+++ b/org.jacoco.doc/docroot/doc/faq.html
@@ -45,13 +45,13 @@
duplicate classes or create separate reports or report groups for each version.
</p>
-<h3>Code with exceptions shows no coverage. Why?</h3>
+<h3>Source code lines with exceptions show no coverage. Why?</h3>
<p>
JaCoCo determines code execution with so called probes. Probes are inserted
into the control flow at certain positions. Code is considered as executed
when a subsequent probe has been executed. In case of exceptions such a
- sequence of instructions is aborted somewhere in the middle and not marked as
- executed.
+ sequence of instructions is aborted somewhere in the middle and the
+ corresponding line of source code is not marked as covered.
</p>
<h3>Why does the coverage report not show line coverage figures?</h3>
diff --git a/org.jacoco.doc/docroot/doc/flow.html b/org.jacoco.doc/docroot/doc/flow.html
index 8eec88b7..98678bbc 100644
--- a/org.jacoco.doc/docroot/doc/flow.html
+++ b/org.jacoco.doc/docroot/doc/flow.html
@@ -257,6 +257,27 @@ public static example()V
above.
</p>
+<h2>Additional Probes Between Lines</h2>
+
+<p>
+ The probe insertion strategy described so far does not consider implicit
+ exceptions thrown for example from invoked methods. If the control flow
+ between two probes is interrupted by a exception not explicitly created with
+ a <code>throw</code> statement all instruction in between are considered as
+ not covered. This leads to unexpected results especially when the the block of
+ instructions spans multiple lines of source code.
+</p>
+
+<p>
+ Therefore JaCoCo adds an additional probe between the instructions of two
+ lines whenever the subsequent line contains at least one method invocation.
+ This limits the effect of implicit exceptions from method invocations to
+ single lines of source. The approach only works for class files compiled with
+ debug information (line numbers) and does not consider implicit exceptions
+ from other instructions than method invocations (e.g.
+ <code>NullPointerException</code> or <code>ArrayIndexOutOfBoundsException</code>).
+</p>
+
<h2>Probe Implementation</h2>
<p>