diff options
Diffstat (limited to 'org.jacoco.core.test/src')
5 files changed, 852 insertions, 49 deletions
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/FinallyFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/FinallyFilterTest.java new file mode 100644 index 00000000..64ad884d --- /dev/null +++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/FinallyFilterTest.java @@ -0,0 +1,421 @@ +/******************************************************************************* + * Copyright (c) 2009, 2017 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.HashSet; +import java.util.Set; + +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.AbstractInsnNode; +import org.objectweb.asm.tree.MethodNode; + +public class FinallyFilterTest implements IFilterOutput { + + private final IFilter filter = new FinallyFilter(); + + private final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0, + "name", "()V", null, null); + + /** + * <pre> + * try { + * ... + * if (...) { + * ... + * return; + * } else { + * ... + * return; + * } + * } finally { + * ... + * } + * </pre> + */ + @Test + public void should_analyze_control_flow() { + final Label start1 = new Label(); + final Label end1 = new Label(); + final Label start2 = new Label(); + final Label end2 = new Label(); + final Label finallyStart = new Label(); + + m.visitTryCatchBlock(start1, end1, finallyStart, null); + m.visitTryCatchBlock(start2, end2, finallyStart, null); + + m.visitLabel(start1); + // jump to another region associated with same handler: + m.visitJumpInsn(Opcodes.IFEQ, start2); + m.visitInsn(Opcodes.NOP); + m.visitLabel(end1); + + m.visitInsn(Opcodes.NOP); // finally block + shouldMergeLast(); + m.visitInsn(Opcodes.RETURN); + + m.visitLabel(start2); + m.visitInsn(Opcodes.NOP); + m.visitLabel(end2); + m.visitInsn(Opcodes.NOP); // finally block + shouldMergeLast(); + m.visitInsn(Opcodes.RETURN); + + m.visitLabel(finallyStart); + m.visitVarInsn(Opcodes.ASTORE, 1); + shouldIgnoreLast(); + m.visitInsn(Opcodes.NOP); // finally block + shouldMergeLast(); + m.visitVarInsn(Opcodes.ALOAD, 1); + shouldIgnoreLast(); + m.visitInsn(Opcodes.ATHROW); + shouldIgnoreLast(); + + execute(); + } + + // === try/catch/finally === + + @Test + public void javac_try_catch_finally() { + final Label tryStart = new Label(); + final Label tryEnd = new Label(); + final Label catchStart = new Label(); + final Label catchEnd = new Label(); + final Label finallyStart = new Label(); + final Label finallyEnd = new Label(); + + m.visitTryCatchBlock(tryStart, tryEnd, catchStart, + "java/lang/Exception"); + m.visitTryCatchBlock(catchStart, catchEnd, finallyStart, null); + m.visitTryCatchBlock(tryStart, tryEnd, finallyStart, null); + + m.visitLabel(tryStart); + m.visitInsn(Opcodes.NOP); // try body + m.visitLabel(tryEnd); + m.visitInsn(Opcodes.NOP); // finally body + shouldMergeLast(); + m.visitJumpInsn(Opcodes.GOTO, finallyEnd); + shouldIgnoreLast(); + + m.visitLabel(catchStart); + m.visitInsn(Opcodes.NOP); // catch body + m.visitLabel(catchEnd); + m.visitInsn(Opcodes.NOP); // finally body + shouldMergeLast(); + m.visitInsn(Opcodes.ATHROW); + + m.visitLabel(finallyStart); + m.visitVarInsn(Opcodes.ASTORE, 1); + shouldIgnoreLast(); + m.visitInsn(Opcodes.NOP); // finally body + shouldMergeLast(); + m.visitVarInsn(Opcodes.ALOAD, 1); + shouldIgnoreLast(); + m.visitInsn(Opcodes.ATHROW); + shouldIgnoreLast(); + m.visitLabel(finallyEnd); + + m.visitInsn(Opcodes.NOP); + + execute(); + } + + @Test + public void ecj_try_catch_finally() { + final Label tryStart = new Label(); + final Label tryEnd = new Label(); + final Label catchStart = new Label(); + final Label catchEnd = new Label(); + final Label finallyStart = new Label(); + final Label finallyEnd = new Label(); + final Label after = new Label(); + + m.visitTryCatchBlock(tryStart, tryEnd, catchStart, + "java/lang/Exception"); + m.visitTryCatchBlock(tryStart, catchEnd, finallyStart, null); + + m.visitLabel(tryStart); + m.visitInsn(Opcodes.NOP); // try body + m.visitLabel(tryEnd); + m.visitJumpInsn(Opcodes.GOTO, finallyEnd); + + m.visitLabel(catchStart); + m.visitInsn(Opcodes.POP); + m.visitInsn(Opcodes.NOP); // catch body + m.visitLabel(catchEnd); + m.visitInsn(Opcodes.NOP); // finally body + shouldMergeLast(); + m.visitJumpInsn(Opcodes.GOTO, after); + shouldIgnoreLast(); + + m.visitLabel(finallyStart); + m.visitVarInsn(Opcodes.ASTORE, 1); + shouldIgnoreLast(); + m.visitInsn(Opcodes.NOP); // finally body + shouldMergeLast(); + m.visitVarInsn(Opcodes.ALOAD, 1); + shouldIgnoreLast(); + m.visitInsn(Opcodes.ATHROW); + shouldIgnoreLast(); + m.visitLabel(finallyEnd); + + m.visitInsn(Opcodes.NOP); // finally body + shouldMergeLast(); + m.visitLabel(after); + m.visitInsn(Opcodes.NOP); + + execute(); + } + + // === empty catch === + + /** + * javac 1.5 - 1.7 + */ + @Test + public void javac_empty_catch() { + final Label tryStart = new Label(); + final Label tryEnd = new Label(); + final Label catchStart = new Label(); + final Label catchEnd = new Label(); + final Label finallyStart = new Label(); + final Label finallyEnd = new Label(); + + m.visitTryCatchBlock(tryStart, tryEnd, catchStart, + "java/lang/Exception"); + m.visitTryCatchBlock(tryStart, tryEnd, finallyStart, null); + m.visitTryCatchBlock(catchStart, catchEnd, finallyStart, null); + // actually one more useless TryCatchBlock for ASTORE in finally + + m.visitLabel(tryStart); + m.visitInsn(Opcodes.NOP); // try body + m.visitLabel(tryEnd); + m.visitInsn(Opcodes.NOP); // finally body + shouldMergeLast(); + m.visitJumpInsn(Opcodes.GOTO, finallyEnd); + shouldIgnoreLast(); + + m.visitLabel(catchStart); + m.visitVarInsn(Opcodes.ASTORE, 1); + m.visitLabel(catchEnd); + m.visitInsn(Opcodes.NOP); // finally body + shouldMergeLast(); + m.visitJumpInsn(Opcodes.GOTO, finallyEnd); + shouldIgnoreLast(); + + m.visitLabel(finallyStart); + m.visitVarInsn(Opcodes.ASTORE, 1); + shouldIgnoreLast(); + m.visitInsn(Opcodes.NOP); // finally body + shouldMergeLast(); + m.visitVarInsn(Opcodes.ALOAD, 1); + shouldIgnoreLast(); + m.visitInsn(Opcodes.ATHROW); + shouldIgnoreLast(); + m.visitLabel(finallyEnd); + + m.visitInsn(Opcodes.NOP); + + execute(); + } + + /** + * javac >= 1.8 + * + * Probably related to https://bugs.openjdk.java.net/browse/JDK-7093325 + */ + @Test + public void javac_8_empty_catch() throws Exception { + final Label tryStart = new Label(); + final Label tryEnd = new Label(); + final Label catchStart = new Label(); + final Label finallyStart = new Label(); + final Label finallyEnd = new Label(); + + m.visitTryCatchBlock(tryStart, tryEnd, catchStart, + "java/lang/Exception"); + m.visitTryCatchBlock(tryStart, tryEnd, finallyStart, null); + + m.visitLabel(tryStart); + m.visitInsn(Opcodes.NOP); // try body + m.visitLabel(tryEnd); + m.visitInsn(Opcodes.NOP); // finally body + shouldMergeLast(); + m.visitJumpInsn(Opcodes.GOTO, finallyEnd); + shouldIgnoreLast(); + shouldIgnoreLast(); + + m.visitLabel(catchStart); + m.visitVarInsn(Opcodes.ASTORE, 1); + m.visitInsn(Opcodes.NOP); // finally body + shouldMergeLast(); + m.visitJumpInsn(Opcodes.GOTO, finallyEnd); + shouldIgnoreLast(); + + m.visitLabel(finallyStart); + m.visitVarInsn(Opcodes.ASTORE, 1); + shouldIgnoreLast(); + m.visitInsn(Opcodes.NOP); // finally body + shouldMergeLast(); + m.visitVarInsn(Opcodes.ALOAD, 1); + shouldIgnoreLast(); + m.visitInsn(Opcodes.ATHROW); + shouldIgnoreLast(); + m.visitLabel(finallyEnd); + + execute(); + } + + @Test + public void ecj_empty_catch() { + final Label tryStart = new Label(); + final Label tryEnd = new Label(); + final Label catchStart = new Label(); + final Label catchEnd = new Label(); + final Label finallyStart = new Label(); + final Label finallyEnd = new Label(); + final Label after = new Label(); + + m.visitTryCatchBlock(tryStart, tryEnd, catchStart, + "java/lang/Exception"); + m.visitTryCatchBlock(tryStart, catchEnd, finallyStart, null); + + m.visitLabel(tryStart); + m.visitInsn(Opcodes.NOP); // try body + m.visitLabel(tryEnd); + m.visitJumpInsn(Opcodes.GOTO, finallyEnd); + + m.visitLabel(catchStart); + m.visitInsn(Opcodes.POP); + m.visitLabel(catchEnd); + m.visitInsn(Opcodes.NOP); // finally body + shouldMergeLast(); + m.visitJumpInsn(Opcodes.GOTO, after); + shouldIgnoreLast(); + + m.visitLabel(finallyStart); + m.visitVarInsn(Opcodes.ASTORE, 1); + shouldIgnoreLast(); + m.visitInsn(Opcodes.NOP); // finally body + shouldMergeLast(); + m.visitVarInsn(Opcodes.ALOAD, 1); + shouldIgnoreLast(); + m.visitInsn(Opcodes.ATHROW); + shouldIgnoreLast(); + m.visitLabel(finallyEnd); + + m.visitInsn(Opcodes.NOP); // finally body + shouldMergeLast(); + m.visitLabel(after); + m.visitInsn(Opcodes.NOP); + + execute(); + } + + // === always completes abruptly === + + @Test + public void javac_always_completes_abruptly() { + final Label tryStart = new Label(); + final Label tryEnd = new Label(); + final Label finallyStart = new Label(); + + m.visitTryCatchBlock(tryStart, tryEnd, finallyStart, null); + + m.visitLabel(tryStart); + m.visitInsn(Opcodes.NOP); // try body + m.visitLabel(tryEnd); + m.visitInsn(Opcodes.RETURN); // finally body + + m.visitLabel(finallyStart); + m.visitVarInsn(Opcodes.ASTORE, 1); + m.visitInsn(Opcodes.RETURN); // finally body + + execute(); + } + + @Test + public void ecj_always_completes_abruptly() { + final Label tryStart = new Label(); + final Label tryEnd = new Label(); + final Label finallyStart = new Label(); + + m.visitTryCatchBlock(tryStart, tryEnd, tryEnd, null); + + m.visitLabel(tryStart); + m.visitInsn(Opcodes.NOP); // try body + m.visitJumpInsn(Opcodes.GOTO, finallyStart); + m.visitLabel(tryEnd); + + m.visitInsn(Opcodes.POP); + m.visitLabel(finallyStart); + m.visitInsn(Opcodes.RETURN); // finally body + + execute(); + } + + private final Set<AbstractInsnNode> expectedIgnored = new HashSet<AbstractInsnNode>(); + private final Set<AbstractInsnNode> actualIgnored = new HashSet<AbstractInsnNode>(); + private final Set<AbstractInsnNode> expectedMerged = new HashSet<AbstractInsnNode>(); + private final Set<AbstractInsnNode> actualMerged = new HashSet<AbstractInsnNode>(); + + private void shouldMergeLast() { + expectedMerged.add(m.instructions.getLast()); + } + + private void shouldIgnoreLast() { + expectedIgnored.add(m.instructions.getLast()); + } + + private void execute() { + filter.filter("", "", m, this); + assertEquals("ignored", toIndexes(expectedIgnored), + toIndexes(actualIgnored)); + assertEquals("merged", toIndexes(expectedMerged), + toIndexes(actualMerged)); + } + + @SuppressWarnings("boxing") + private Set<Integer> toIndexes(Set<AbstractInsnNode> set) { + final Set<Integer> result = new HashSet<Integer>(); + for (final AbstractInsnNode i : set) { + result.add(m.instructions.indexOf(i)); + } + return result; + } + + public void ignore(final AbstractInsnNode fromInclusive, + final AbstractInsnNode toInclusive) { + for (AbstractInsnNode i = fromInclusive; i != toInclusive; i = i + .getNext()) { + actualIgnored.add(i); + } + actualIgnored.add(toInclusive); + } + + public void merge(final AbstractInsnNode i1, final AbstractInsnNode i2) { + if (actualMerged.isEmpty() || actualMerged.contains(i1) + || actualMerged.contains(i2)) { + actualMerged.add(i1); + actualMerged.add(i2); + } else { + fail(); + } + } + +} diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/filter/FinallyTest.java b/org.jacoco.core.test/src/org/jacoco/core/test/filter/FinallyTest.java new file mode 100644 index 00000000..7658a118 --- /dev/null +++ b/org.jacoco.core.test/src/org/jacoco/core/test/filter/FinallyTest.java @@ -0,0 +1,266 @@ +/******************************************************************************* + * Copyright (c) 2009, 2017 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.filter; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Pattern; + +import org.jacoco.core.analysis.ICounter; +import org.jacoco.core.test.TargetLoader; +import org.jacoco.core.test.filter.targets.Finally; +import org.jacoco.core.test.validation.Source; +import org.jacoco.core.test.validation.ValidationTestBase; +import org.junit.Test; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.LineNumberNode; +import org.objectweb.asm.tree.MethodNode; + +/** + * Test of filtering of duplicated bytecode that is generated for finally block. + */ +public class FinallyTest extends ValidationTestBase { + + private static boolean isJDK8 = !Pattern.compile("1\\.[567]\\.0_(\\d++)") + .matcher(System.getProperty("java.version")).matches(); + + public FinallyTest() { + super(Finally.class); + } + + /** + * {@link Finally#example(boolean)} + */ + @Test + public void example() { + if (isJDKCompiler) { + assertLine("example.0", ICounter.EMPTY); + } else { + assertLine("example.0", ICounter.FULLY_COVERED); + } + assertLine("example.1", ICounter.FULLY_COVERED, 0, 2); + assertLine("example.2", ICounter.FULLY_COVERED); + assertLine("example.3", ICounter.EMPTY); + assertLine("example.4", ICounter.EMPTY); + } + + /** + * GOTO instructions at the end of duplicates of finally block might have + * line number of a last instruction of finally block and hence lead to + * unexpected coverage results, like for example in case of ECJ for + * {@link Finally#catchNotExecuted()}, {@link Finally#emptyCatch()}. So we + * decided to ignore them, even if they can correspond to a real break + * statement. + * <p> + * See also <a href= + * "https://bugs.openjdk.java.net/browse/JDK-8180141">JDK-8180141</a> and + * <a href= + * "https://bugs.openjdk.java.net/browse/JDK-7008643">JDK-7008643</a>. + * <p> + * {@link Finally#breakStatement()} + */ + @Test + public void breakStatement() { + assertLine("breakStatement", ICounter.EMPTY); + + assertLine("breakStatement.1", ICounter.FULLY_COVERED); + assertLine("breakStatement.2", ICounter.EMPTY); + } + + /** + * {@link Finally#catchNotExecuted()} + */ + @Test + public void catchNotExecuted() { + assertLine("catchNotExecuted.catch", ICounter.NOT_COVERED); + assertLine("catchNotExecuted.0", ICounter.EMPTY); + assertLine("catchNotExecuted.1", ICounter.FULLY_COVERED); + assertLine("catchNotExecuted.2", ICounter.EMPTY); + } + + /** + * {@link Finally#emptyCatch()} + */ + @Test + public void emptyCatch() { + assertLine("emptyCatch.0", ICounter.EMPTY); + assertLine("emptyCatch.1", ICounter.FULLY_COVERED); + assertLine("emptyCatch.2", ICounter.EMPTY); + } + + /** + * {@link Finally#twoRegions()} + */ + @Test + public void twoRegions() { + assertLine("twoRegions.0", ICounter.EMPTY); + if (isJDKCompiler && !isJDK8) { + // https://bugs.openjdk.java.net/browse/JDK-7008643 + assertLine("twoRegions.1", ICounter.PARTLY_COVERED); + assertLine("twoRegions.return.1", ICounter.EMPTY); + assertLine("twoRegions.return.2", ICounter.EMPTY); + } else { + assertLine("twoRegions.1", ICounter.FULLY_COVERED); + assertLine("twoRegions.return.1", ICounter.FULLY_COVERED); + assertLine("twoRegions.return.2", ICounter.NOT_COVERED); + } + assertLine("twoRegions.2", ICounter.EMPTY); + + assertLine("twoRegions.if", ICounter.FULLY_COVERED); + assertLine("twoRegions.region.1", ICounter.FULLY_COVERED); + assertLine("twoRegions.region.2", ICounter.NOT_COVERED); + } + + /** + * {@link Finally#nested()} + */ + @Test + public void nested() { + if (isJDKCompiler) { + assertLine("nested.0", ICounter.EMPTY); + } else { + assertLine("nested.0", ICounter.FULLY_COVERED); + } + assertLine("nested.1", ICounter.EMPTY); + assertLine("nested.2", ICounter.FULLY_COVERED); + if (isJDKCompiler) { + assertLine("nested.3", ICounter.EMPTY); + } else { + assertLine("nested.3", ICounter.FULLY_COVERED); + } + assertLine("nested.4", ICounter.FULLY_COVERED); + } + + /** + * {@link Finally#emptyTry()} + */ + @Test + public void emptyTry() { + assertLine("emptyTry.0", ICounter.EMPTY); + if (!isJDKCompiler || isJDK8) { + assertLine("emptyTry.1", ICounter.FULLY_COVERED); + assertLine("emptyTry.2", ICounter.EMPTY); + } else { + // compiler bug fixed in javac >= 1.8: + assertLine("emptyTry.1", ICounter.PARTLY_COVERED); + assertLine("emptyTry.2", ICounter.FULLY_COVERED); + } + } + + /** + * {@link Finally#alwaysCompletesAbruptly()} + */ + @Test + public void alwaysCompletesAbruptly() { + if (isJDKCompiler) { + // uncovered case: + assertLine("alwaysCompletesAbruptly.0", ICounter.EMPTY); + assertLine("alwaysCompletesAbruptly.1", ICounter.PARTLY_COVERED); + } else { + assertLine("alwaysCompletesAbruptly.0", ICounter.PARTLY_COVERED); + assertLine("alwaysCompletesAbruptly.1", ICounter.FULLY_COVERED); + } + assertLine("alwaysCompletesAbruptly.2", ICounter.EMPTY); + } + + /** + * This test studies placement of GOTO instructions. + */ + @Test + public void gotos() throws IOException { + final Source source = Source.getSourceFor("src", Finally.class); + + final ClassNode classNode = new ClassNode(); + new ClassReader(TargetLoader.getClassDataAsBytes(Finally.class)) + .accept(classNode, 0); + final Set<String> tags = new HashSet<String>(); + for (final MethodNode m : classNode.methods) { + if ("main".equals(m.name)) { + // skip it + continue; + } + int lineNumber = -1; + for (AbstractInsnNode i = m.instructions + .getFirst(); i != null; i = i.getNext()) { + if (AbstractInsnNode.LINE == i.getType()) { + lineNumber = ((LineNumberNode) i).line; + } + if (Opcodes.GOTO == i.getOpcode()) { + final String line = source.getLine(lineNumber); + if (line.indexOf('$') < 0) { + throw new AssertionError( + "No tag at line " + lineNumber); + } + final String tag = line.substring( + line.indexOf('$') + "$line-".length(), + line.lastIndexOf('$')); + tags.add(tag); + } + } + } + + final Set<String> expected = new HashSet<String>(); + + if (isJDKCompiler) { + expected.add("example.2"); + } else { + expected.add("example.0"); + } + + expected.add("breakStatement.for"); + if (isJDKCompiler) { + expected.add("breakStatement.1"); + expected.add("breakStatement.2"); + } else { + expected.add("breakStatement"); + } + + if (isJDKCompiler) { + expected.add("emptyCatch.2"); + } else { + expected.add("emptyCatch"); + expected.add("emptyCatch.1"); + } + + if (isJDKCompiler) { + expected.add("catchNotExecuted.2"); + } else { + expected.add("catchNotExecuted"); + expected.add("catchNotExecuted.1"); + } + + if (isJDKCompiler) { + expected.add("nested.5"); + expected.add("nested.6"); + } else { + expected.add("nested.0"); + expected.add("nested.3"); + } + + if (isJDKCompiler && !isJDK8) { + expected.add("emptyTry.2"); + } + + if (!isJDKCompiler) { + expected.add("alwaysCompletesAbruptly.0"); + } + + assertEquals(expected, tags); + } + +} diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/filter/targets/Finally.java b/org.jacoco.core.test/src/org/jacoco/core/test/filter/targets/Finally.java new file mode 100644 index 00000000..04950e91 --- /dev/null +++ b/org.jacoco.core.test/src/org/jacoco/core/test/filter/targets/Finally.java @@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (c) 2009, 2017 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.filter.targets; + +import static org.jacoco.core.test.validation.targets.Stubs.ex; +import static org.jacoco.core.test.validation.targets.Stubs.f; +import static org.jacoco.core.test.validation.targets.Stubs.nop; +import static org.jacoco.core.test.validation.targets.Stubs.t; + +public class Finally { + + /** + * <pre> + * InputStream in = null; + * try { + * in = ...; + * ... + * } finally { + * if (in != null) { + * in.close(); + * } + * } + * </pre> + */ + private static void example(boolean t) { + Object in = null; + try { + in = open(t); + } finally { // $line-example.0$ + if (in != null) { // $line-example.1$ + nop(); // $line-example.2$ + } // $line-example.3$ + } // $line-example.4$ + } + + private static Object open(boolean t) { + ex(t); + return new Object(); + } + + private static void breakStatement() { + for (int i = 0; i < 1; i++) { // $line-breakStatement.for$ + try { + if (f()) { + break; // $line-breakStatement$ + } + } finally { + nop("finally"); // $line-breakStatement.1$ + } // $line-breakStatement.2$ + } + } + + private static void catchNotExecuted() { + try { + nop("try"); + } catch (Exception e) { // $line-catchNotExecuted$ + nop("catch"); // $line-catchNotExecuted.catch$ + } finally { // $line-catchNotExecuted.0$ + nop("finally"); // $line-catchNotExecuted.1$ + } // $line-catchNotExecuted.2$ + } + + private static void emptyCatch() { + try { + nop("try"); + } catch (Exception e) { // $line-emptyCatch$ + // empty + } finally { // $line-emptyCatch.0$ + nop("finally"); // $line-emptyCatch.1$ + } // $line-emptyCatch.2$ + } + + private static void twoRegions() { + try { + // jump to another region associated with same handler: + if (t()) { // $line-twoRegions.if$ + nop(); // $line-twoRegions.region.1$ + return; // $line-twoRegions.return.1$ + } else { + nop(); // $line-twoRegions.region.2$ + return; // $line-twoRegions.return.2$ + } + } finally { // $line-twoRegions.0$ + nop(); // $line-twoRegions.1$ + } // $line-twoRegions.2$ + } + + private static void nested() { + try { + nop(); + } finally { // $line-nested.0$ + try { // $line-nested.1$ + nop(); // $line-nested.2$ + } finally { // $line-nested.3$ + nop(); // $line-nested.4$ + } // $line-nested.5$ + } // $line-nested.6$ + } + + private static void emptyTry() { + try { + // empty + } finally { // $line-emptyTry.0$ + nop(); // $line-emptyTry.1$ + } // $line-emptyTry.2$ + } + + @SuppressWarnings("finally") + private static void alwaysCompletesAbruptly() { + try { + nop(); + } finally { // $line-alwaysCompletesAbruptly.0$ + return; // $line-alwaysCompletesAbruptly.1$ + } // $line-alwaysCompletesAbruptly.2$ + } + + public static void main(String[] args) { + example(false); + try { + example(true); + } catch (Exception ignore) { + } + + breakStatement(); + + catchNotExecuted(); + + emptyCatch(); + + twoRegions(); + + nested(); + + emptyTry(); + + alwaysCompletesAbruptly(); + } + +} 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 e1baec7d..3ec77f33 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 @@ -15,27 +15,11 @@ import org.jacoco.core.analysis.ICounter; import org.jacoco.core.test.validation.targets.Target03; import org.junit.Test; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - /** * Tests of exception based control flow. */ public class ExceptionsTest extends ValidationTestBase { - /** - * https://bugs.openjdk.java.net/browse/JDK-8180660 - */ - private static final boolean isJDK8u152; - private static final boolean isJDK10; - - static { - final Matcher m = Pattern.compile("1\\.8\\.0_(\\d++)(-ea)?") - .matcher(System.getProperty("java.version")); - isJDK8u152 = m.matches() && Integer.parseInt(m.group(1)) >= 152; - isJDK10 = System.getProperty("java.version").startsWith("10"); - } - public ExceptionsTest() { super(Target03.class); } @@ -109,45 +93,25 @@ public class ExceptionsTest extends ValidationTestBase { ICounter.FULLY_COVERED); // 7. Finally Block Without Exception Thrown - // Finally block is yellow as the exception path is missing. assertLine("noExceptionFinally.beforeBlock", ICounter.FULLY_COVERED); assertLine("noExceptionFinally.tryBlock", ICounter.FULLY_COVERED); assertLine("noExceptionFinally.finally", - isJDKCompiler ? ICounter.EMPTY : ICounter.PARTLY_COVERED); - assertLine("noExceptionFinally.finallyBlock", ICounter.PARTLY_COVERED); - if (!isJDKCompiler) { - assertLine("noExceptionFinally.finallyBlockEnd", - ICounter.NOT_COVERED); - } else if (isJDK8u152 || isJDK10) { - assertLine("noExceptionFinally.finallyBlockEnd", - ICounter.PARTLY_COVERED); - } else { - assertLine("noExceptionFinally.finallyBlockEnd", - ICounter.FULLY_COVERED); - } + isJDKCompiler ? ICounter.EMPTY : ICounter.FULLY_COVERED); + assertLine("noExceptionFinally.finallyBlock", ICounter.FULLY_COVERED); + assertLine("noExceptionFinally.finallyBlockEnd", ICounter.EMPTY); assertLine("noExceptionFinally.afterBlock", ICounter.FULLY_COVERED); // 8. Finally Block With Implicit Exception - // Finally block is yellow as the non-exception path is missing. assertLine("implicitExceptionFinally.beforeBlock", ICounter.FULLY_COVERED); assertLine("implicitExceptionFinally.before", ICounter.FULLY_COVERED); assertLine("implicitExceptionFinally.exception", ICounter.NOT_COVERED); assertLine("implicitExceptionFinally.after", ICounter.NOT_COVERED); assertLine("implicitExceptionFinally.finally", - isJDKCompiler ? ICounter.EMPTY : ICounter.PARTLY_COVERED); + isJDKCompiler ? ICounter.EMPTY : ICounter.NOT_COVERED); assertLine("implicitExceptionFinally.finallyBlock", - ICounter.PARTLY_COVERED); - if (!isJDKCompiler) { - assertLine("implicitExceptionFinally.finallyBlockEnd", - ICounter.FULLY_COVERED); - } else if (isJDK8u152 || isJDK10) { - assertLine("implicitExceptionFinally.finallyBlockEnd", - ICounter.PARTLY_COVERED); - } else { - assertLine("implicitExceptionFinally.finallyBlockEnd", - ICounter.NOT_COVERED); - } + ICounter.FULLY_COVERED); + assertLine("implicitExceptionFinally.finallyBlockEnd", ICounter.EMPTY); assertLine("implicitExceptionFinally.afterBlock", ICounter.NOT_COVERED); // 9. Finally Block With Exception Thrown Explicitly @@ -159,13 +123,8 @@ public class ExceptionsTest extends ValidationTestBase { isJDKCompiler ? ICounter.EMPTY : ICounter.FULLY_COVERED); assertLine("explicitExceptionFinally.finallyBlock", ICounter.FULLY_COVERED); - if (!isJDKCompiler || isJDK8u152 || isJDK10) { - assertLine("explicitExceptionFinally.finallyBlockEnd", - ICounter.FULLY_COVERED); - } else { - assertLine("explicitExceptionFinally.finallyBlockEnd", - ICounter.EMPTY); - } + assertLine("explicitExceptionFinally.finallyBlockEnd", + isJDKCompiler ? ICounter.EMPTY : ICounter.FULLY_COVERED); assertLine("explicitExceptionFinally.afterBlock", ICounter.EMPTY); } diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/validation/targets/Stubs.java b/org.jacoco.core.test/src/org/jacoco/core/test/validation/targets/Stubs.java index 97b49e4b..8200fcf7 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/test/validation/targets/Stubs.java +++ b/org.jacoco.core.test/src/org/jacoco/core/test/validation/targets/Stubs.java @@ -108,6 +108,15 @@ public class Stubs { } /** + * Throws a {@link RuntimeException} if given argument is <code>true</code>. + */ + public static void ex(boolean t) { + if (t) { + throw new StubException(); + } + } + + /** * Directly executes the given runnable. */ public static void exec(Runnable task) { |