aboutsummaryrefslogtreecommitdiffstats
path: root/org.jacoco.core.test/src/org
diff options
context:
space:
mode:
authorEvgeny Mandrikov <Godin@users.noreply.github.com>2016-08-16 04:06:56 +0200
committerMarc R. Hoffmann <hoffmann@mountainminds.com>2016-08-16 04:06:56 +0200
commit28a112ca6c6f46cd385f00aa932ec0e334e045a7 (patch)
tree9d92399e1a2e797a87d560e49fc092e0017a113d /org.jacoco.core.test/src/org
parentc6f2b6b7e887eb645b8aba928ff0134cfe66ec28 (diff)
downloadplatform_external_jacoco-28a112ca6c6f46cd385f00aa932ec0e334e045a7.tar.gz
platform_external_jacoco-28a112ca6c6f46cd385f00aa932ec0e334e045a7.tar.bz2
platform_external_jacoco-28a112ca6c6f46cd385f00aa932ec0e334e045a7.zip
Do not violate JVMS regarding initialization of final fields (#434)
Without this change instrumented classes can't pass checks and cause IllegalAccessError starting from OpenJDK 9 EA b127 (see https://bugs.openjdk.java.net/browse/JDK-8157181).
Diffstat (limited to 'org.jacoco.core.test/src/org')
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/internal/instr/ClassInstrumenterTest.java2
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactoryTest.java133
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeInserterTest.java48
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/test/InstrumentingLoader.java75
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/test/TargetLoader.java6
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/test/validation/BadCycleClassTest.java37
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/test/validation/BadCycleTestBase.java66
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/test/validation/targets/BadCycleClass.java44
8 files changed, 363 insertions, 48 deletions
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ClassInstrumenterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ClassInstrumenterTest.java
index 428ba653..5d417712 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ClassInstrumenterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ClassInstrumenterTest.java
@@ -62,7 +62,7 @@ public class ClassInstrumenterTest implements IProbeArrayStrategy {
// === IProbeArrayStrategy ===
- public int storeInstance(MethodVisitor mv, int variable) {
+ public int storeInstance(MethodVisitor mv, boolean clinit, int variable) {
return 0;
}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactoryTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactoryTest.java
index c8da6d6d..3ecbd3ad 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactoryTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeArrayStrategyFactoryTest.java
@@ -14,7 +14,10 @@ package org.jacoco.core.internal.instr;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.List;
import org.jacoco.core.runtime.IExecutionDataAccessorGenerator;
import org.jacoco.core.runtime.OfflineInstrumentationAccessGenerator;
@@ -98,9 +101,8 @@ public class ProbeArrayStrategyFactoryTest {
assertDataField(InstrSupport.DATAFIELD_ACC);
assertInitMethod(true);
- final ClassVisitorMock cv = new ClassVisitorMock();
- strategy.storeInstance(cv.visitMethod(0, null, null, null, null), 0);
- assertFalse(cv.interfaceMethod);
+ strategy.storeInstance(cv.visitMethod(0, null, null, null, null), false,
+ 0);
}
@Test
@@ -119,21 +121,21 @@ public class ProbeArrayStrategyFactoryTest {
@Test(expected = UnsupportedOperationException.class)
public void testEmptyInterface7StoreInstance() {
- IProbeArrayStrategy strategy = test(Opcodes.V1_7,
- Opcodes.ACC_INTERFACE, false, false);
- strategy.storeInstance(null, 0);
+ IProbeArrayStrategy strategy = test(Opcodes.V1_7, Opcodes.ACC_INTERFACE,
+ false, false);
+ strategy.storeInstance(null, false, 0);
}
@Test
public void testInterface8() {
+ cv.isInterface = true;
final IProbeArrayStrategy strategy = test(Opcodes.V1_8,
Opcodes.ACC_INTERFACE, false, true);
assertDataField(InstrSupport.DATAFIELD_INTF_ACC);
- assertInitMethod(true);
+ assertInitAndClinitMethods();
- final ClassVisitorMock cv = new ClassVisitorMock();
- strategy.storeInstance(cv.visitMethod(0, null, null, null, null), 0);
- assertTrue(cv.interfaceMethod);
+ strategy.storeInstance(cv.visitMethod(0, null, null, null, null), false,
+ 0);
}
@Test
@@ -143,6 +145,13 @@ public class ProbeArrayStrategyFactoryTest {
assertNoInitMethod();
}
+ @Test(expected = UnsupportedOperationException.class)
+ public void testEmptyInterface8StoreInstance() {
+ final IProbeArrayStrategy strategy = test(Opcodes.V1_8,
+ Opcodes.ACC_INTERFACE, false, false);
+ strategy.storeInstance(null, false, 0);
+ }
+
@Test
public void testClinitInterface8() {
test(Opcodes.V1_8, Opcodes.ACC_INTERFACE, true, false);
@@ -150,6 +159,18 @@ public class ProbeArrayStrategyFactoryTest {
assertNoInitMethod();
}
+ @Test
+ public void testClinitAndMethodsInterface8() {
+ cv.isInterface = true;
+ final IProbeArrayStrategy strategy = test(Opcodes.V1_8,
+ Opcodes.ACC_INTERFACE, true, true);
+ assertDataField(InstrSupport.DATAFIELD_INTF_ACC);
+ assertInitAndClinitMethods();
+
+ strategy.storeInstance(cv.visitMethod(0, "<clinit>", null, null, null),
+ true, 0);
+ }
+
private IProbeArrayStrategy test(int version, int access, boolean clinit,
boolean method) {
ClassWriter writer = new ClassWriter(0);
@@ -179,16 +200,40 @@ public class ProbeArrayStrategyFactoryTest {
return strategy;
}
+ private static class AddedMethod {
+ private final int access;
+ private final String name;
+ private final String desc;
+ private boolean frames;
+
+ AddedMethod(int access, String name, String desc) {
+ this.access = access;
+ this.name = name;
+ this.desc = desc;
+ }
+
+ void assertInitMethod(boolean frames) {
+ assertEquals(InstrSupport.INITMETHOD_NAME, name);
+ assertEquals(InstrSupport.INITMETHOD_DESC, desc);
+ assertEquals(InstrSupport.INITMETHOD_ACC, access);
+ assertEquals(Boolean.valueOf(frames), Boolean.valueOf(frames));
+ }
+
+ void assertClinit() {
+ assertEquals(InstrSupport.CLINIT_NAME, name);
+ assertEquals(InstrSupport.CLINIT_DESC, desc);
+ assertEquals(InstrSupport.CLINIT_ACC, access);
+ assertEquals(Boolean.valueOf(false), Boolean.valueOf(frames));
+ }
+ }
+
private static class ClassVisitorMock extends ClassVisitor {
+ private boolean isInterface;
+
private int fieldAccess;
private String fieldName;
-
- private int methodAccess;
- private String methodName;
-
- private boolean frames;
- private boolean interfaceMethod;
+ private final List<AddedMethod> methods = new ArrayList<AddedMethod>();
ClassVisitorMock() {
super(Opcodes.ASM5);
@@ -206,20 +251,51 @@ public class ProbeArrayStrategyFactoryTest {
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
- assertNull(methodName);
- methodAccess = access;
- methodName = name;
+ final AddedMethod m = new AddedMethod(access, name, desc);
+ methods.add(m);
return new MethodVisitor(Opcodes.ASM5) {
@Override
public void visitFrame(int type, int nLocal, Object[] local,
int nStack, Object[] stack) {
- frames = true;
+ m.frames = true;
+ }
+
+ @Override
+ public void visitFieldInsn(int opcode, String owner,
+ String name, String desc) {
+ assertEquals(InstrSupport.DATAFIELD_NAME, name);
+ assertEquals(InstrSupport.DATAFIELD_DESC, desc);
+
+ if (opcode == Opcodes.GETSTATIC) {
+ assertEquals(InstrSupport.INITMETHOD_NAME,
+ methods.get(methods.size() - 1).name);
+ } else if (opcode == Opcodes.PUTSTATIC) {
+ if (isInterface) {
+ assertEquals(InstrSupport.CLINIT_NAME,
+ methods.get(methods.size() - 1).name);
+ } else {
+ assertEquals(InstrSupport.INITMETHOD_NAME,
+ methods.get(methods.size() - 1).name);
+ }
+ } else {
+ fail();
+ }
}
@Override
public void visitMethodInsn(int opcode, String owner,
String name, String desc, boolean itf) {
- interfaceMethod = itf;
+ if ("getProbes".equals(name)) {
+ // method's owner is not interface:
+ assertFalse(itf);
+ return;
+ }
+ assertEquals(itf, isInterface);
+
+ assertEquals(Opcodes.INVOKESTATIC, opcode);
+ assertEquals("Foo", owner);
+ assertEquals(InstrSupport.INITMETHOD_NAME, name);
+ assertEquals(InstrSupport.INITMETHOD_DESC, desc);
}
};
}
@@ -235,13 +311,18 @@ public class ProbeArrayStrategyFactoryTest {
}
void assertInitMethod(boolean frames) {
- assertEquals(InstrSupport.INITMETHOD_NAME, cv.methodName);
- assertEquals(InstrSupport.INITMETHOD_ACC, cv.methodAccess);
- assertEquals(Boolean.valueOf(frames), Boolean.valueOf(cv.frames));
+ assertEquals(cv.methods.size(), 1);
+ cv.methods.get(0).assertInitMethod(frames);
+ }
+
+ void assertInitAndClinitMethods() {
+ assertEquals(2, cv.methods.size());
+ cv.methods.get(0).assertInitMethod(true);
+ cv.methods.get(1).assertClinit();
}
void assertNoInitMethod() {
- assertNull(cv.methodName);
+ assertEquals(0, cv.methods.size());
}
}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeInserterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeInserterTest.java
index 6f2d7ec4..6a2f9c69 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeInserterTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/ProbeInserterTest.java
@@ -39,9 +39,8 @@ public class ProbeInserterTest {
expected = new MethodRecorder();
expectedVisitor = expected.getVisitor();
arrayStrategy = new IProbeArrayStrategy() {
-
- public int storeInstance(MethodVisitor mv, int variable) {
- mv.visitLdcInsn("init");
+ public int storeInstance(MethodVisitor mv, boolean clinit, int variable) {
+ mv.visitLdcInsn(clinit ? "clinit" : "init");
return 5;
}
@@ -57,7 +56,7 @@ public class ProbeInserterTest {
@Test
public void testVariableStatic() {
- ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "()V",
+ ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "m", "()V",
actualVisitor, arrayStrategy);
pi.insertProbe(0);
@@ -69,7 +68,7 @@ public class ProbeInserterTest {
@Test
public void testVariableNonStatic() {
- ProbeInserter pi = new ProbeInserter(0, "()V", actualVisitor,
+ ProbeInserter pi = new ProbeInserter(0, "m", "()V", actualVisitor,
arrayStrategy);
pi.insertProbe(0);
@@ -81,7 +80,7 @@ public class ProbeInserterTest {
@Test
public void testVariableNonStatic_IZObject() {
- ProbeInserter pi = new ProbeInserter(0, "(IZLjava/lang/Object;)V",
+ ProbeInserter pi = new ProbeInserter(0, "m", "(IZLjava/lang/Object;)V",
actualVisitor, arrayStrategy);
pi.insertProbe(0);
@@ -93,7 +92,7 @@ public class ProbeInserterTest {
@Test
public void testVariableNonStatic_JD() {
- ProbeInserter pi = new ProbeInserter(0, "(JD)V", actualVisitor,
+ ProbeInserter pi = new ProbeInserter(0, "m", "(JD)V", actualVisitor,
arrayStrategy);
pi.insertProbe(0);
@@ -105,7 +104,7 @@ public class ProbeInserterTest {
@Test
public void testVisitCode() {
- ProbeInserter pi = new ProbeInserter(0, "()V", actualVisitor,
+ ProbeInserter pi = new ProbeInserter(0, "m", "()V", actualVisitor,
arrayStrategy);
pi.visitCode();
@@ -113,8 +112,17 @@ public class ProbeInserterTest {
}
@Test
+ public void testVisitClinit() {
+ ProbeInserter pi = new ProbeInserter(0, "<clinit>", "()V",
+ actualVisitor, arrayStrategy);
+ pi.visitCode();
+
+ expectedVisitor.visitLdcInsn("clinit");
+ }
+
+ @Test
public void testVisitVarIns() {
- ProbeInserter pi = new ProbeInserter(0, "(II)V", actualVisitor,
+ ProbeInserter pi = new ProbeInserter(0, "m", "(II)V", actualVisitor,
arrayStrategy);
pi.visitVarInsn(Opcodes.ALOAD, 0);
@@ -135,7 +143,7 @@ public class ProbeInserterTest {
@Test
public void testVisitIincInsn() {
- ProbeInserter pi = new ProbeInserter(0, "(II)V", actualVisitor,
+ ProbeInserter pi = new ProbeInserter(0, "m", "(II)V", actualVisitor,
arrayStrategy);
pi.visitIincInsn(0, 100);
pi.visitIincInsn(1, 101);
@@ -155,7 +163,7 @@ public class ProbeInserterTest {
@Test
public void testVisitLocalVariable() {
- ProbeInserter pi = new ProbeInserter(0, "(II)V", actualVisitor,
+ ProbeInserter pi = new ProbeInserter(0, "m", "(II)V", actualVisitor,
arrayStrategy);
pi.visitLocalVariable(null, null, null, null, null, 0);
@@ -176,7 +184,7 @@ public class ProbeInserterTest {
@Test
public void testVisitMaxs1() {
- ProbeInserter pi = new ProbeInserter(0, "(II)V", actualVisitor,
+ ProbeInserter pi = new ProbeInserter(0, "m", "(II)V", actualVisitor,
arrayStrategy);
pi.visitCode();
pi.visitMaxs(0, 8);
@@ -187,7 +195,7 @@ public class ProbeInserterTest {
@Test
public void testVisitMaxs2() {
- ProbeInserter pi = new ProbeInserter(0, "(II)V", actualVisitor,
+ ProbeInserter pi = new ProbeInserter(0, "m", "(II)V", actualVisitor,
arrayStrategy);
pi.visitCode();
pi.visitMaxs(10, 8);
@@ -198,7 +206,7 @@ public class ProbeInserterTest {
@Test
public void testVisitFrame() {
- ProbeInserter pi = new ProbeInserter(0, "(J)V", actualVisitor,
+ ProbeInserter pi = new ProbeInserter(0, "m", "(J)V", actualVisitor,
arrayStrategy);
pi.visitFrame(Opcodes.F_NEW, 3, new Object[] { "Foo", Opcodes.LONG,
@@ -210,7 +218,7 @@ public class ProbeInserterTest {
@Test
public void testVisitFrameNoLocals() {
- ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "()V",
+ ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "m", "()V",
actualVisitor, arrayStrategy);
pi.visitFrame(Opcodes.F_NEW, 0, new Object[] {}, 0, new Object[0]);
@@ -221,7 +229,7 @@ public class ProbeInserterTest {
@Test
public void testVisitFrameProbeAt0() {
- ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "()V",
+ ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "m", "()V",
actualVisitor, arrayStrategy);
pi.visitFrame(Opcodes.F_NEW, 2, new Object[] { Opcodes.DOUBLE, "Foo" },
@@ -233,7 +241,7 @@ public class ProbeInserterTest {
@Test
public void testFillOneWord() {
- ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "(I)V",
+ ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "m", "(I)V",
actualVisitor, arrayStrategy);
pi.visitFrame(Opcodes.F_NEW, 0, new Object[] {}, 0, new Object[] {});
@@ -245,7 +253,7 @@ public class ProbeInserterTest {
@Test
public void testFillTwoWord() {
- ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "(J)V",
+ ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "m", "(J)V",
actualVisitor, arrayStrategy);
pi.visitFrame(Opcodes.F_NEW, 0, new Object[] {}, 0, new Object[] {});
@@ -257,7 +265,7 @@ public class ProbeInserterTest {
@Test
public void testFillPartly() {
- ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "(DIJ)V",
+ ProbeInserter pi = new ProbeInserter(Opcodes.ACC_STATIC, "m", "(DIJ)V",
actualVisitor, arrayStrategy);
pi.visitFrame(Opcodes.F_NEW, 1, new Object[] { Opcodes.DOUBLE }, 0,
@@ -271,7 +279,7 @@ public class ProbeInserterTest {
@Test(expected = IllegalArgumentException.class)
public void testVisitFrame_invalidType() {
- ProbeInserter pi = new ProbeInserter(0, "()V", actualVisitor,
+ ProbeInserter pi = new ProbeInserter(0, "m", "()V", actualVisitor,
arrayStrategy);
pi.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/InstrumentingLoader.java b/org.jacoco.core.test/src/org/jacoco/core/test/InstrumentingLoader.java
new file mode 100644
index 00000000..99bbaedb
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/test/InstrumentingLoader.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2016 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;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.jacoco.core.data.ExecutionDataStore;
+import org.jacoco.core.data.SessionInfoStore;
+import org.jacoco.core.instr.Instrumenter;
+import org.jacoco.core.runtime.IRuntime;
+import org.jacoco.core.runtime.RuntimeData;
+import org.jacoco.core.runtime.SystemPropertiesRuntime;
+
+public final class InstrumentingLoader extends ClassLoader {
+
+ private final RuntimeData data;
+ private final IRuntime runtime;
+
+ public InstrumentingLoader() throws Exception {
+ data = new RuntimeData();
+ runtime = new SystemPropertiesRuntime();
+ runtime.startup(data);
+ }
+
+ @Override
+ protected synchronized Class<?> loadClass(String name, boolean resolve)
+ throws ClassNotFoundException {
+ if (name.startsWith("org.jacoco.core.test.validation.targets.")) {
+ final byte[] bytes;
+ try {
+ bytes = getClassBytes(name);
+ } catch (IOException e) {
+ throw new ClassNotFoundException("Unable to load", e);
+ }
+ final byte[] instrumented;
+ try {
+ instrumented = new Instrumenter(runtime).instrument(bytes,
+ name);
+ } catch (IOException e) {
+ throw new ClassNotFoundException("Unable to instrument", e);
+ }
+ final Class<?> c = defineClass(name, instrumented, 0,
+ instrumented.length);
+ if (resolve) {
+ resolveClass(c);
+ }
+ return c;
+ }
+ return super.loadClass(name, resolve);
+ }
+
+ public byte[] getClassBytes(String name) throws IOException {
+ final String resource = "/" + name.replace('.', '/') + ".class";
+ final InputStream in = getClass().getResourceAsStream(resource);
+ return TargetLoader.getClassDataAsBytes(in);
+ }
+
+ public ExecutionDataStore collect() {
+ final ExecutionDataStore store = new ExecutionDataStore();
+ data.collect(store, new SessionInfoStore(), false);
+ runtime.shutdown();
+ return store;
+ }
+
+}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/TargetLoader.java b/org.jacoco.core.test/src/org/jacoco/core/test/TargetLoader.java
index 00bab63e..de54015f 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/test/TargetLoader.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/test/TargetLoader.java
@@ -58,7 +58,11 @@ public class TargetLoader extends ClassLoader {
}
public static byte[] getClassDataAsBytes(Class<?> clazz) throws IOException {
- InputStream in = getClassData(clazz);
+ return getClassDataAsBytes(getClassData(clazz));
+ }
+
+ public static byte[] getClassDataAsBytes(InputStream in) throws
+ IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[0x100];
int len;
diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/validation/BadCycleClassTest.java b/org.jacoco.core.test/src/org/jacoco/core/test/validation/BadCycleClassTest.java
new file mode 100644
index 00000000..47820e68
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/test/validation/BadCycleClassTest.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2016 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;
+
+import org.jacoco.core.analysis.ICounter;
+import org.jacoco.core.test.validation.targets.BadCycleClass;
+import org.junit.Test;
+
+/**
+ * Test of "bad cycles" with classes.
+ */
+public class BadCycleClassTest extends BadCycleTestBase {
+
+ public BadCycleClassTest() throws Exception {
+ super(BadCycleClass.class);
+ }
+
+ @Test
+ public void test() throws Exception {
+ loader.loadClass(BadCycleClass.Child.class.getName()).newInstance();
+
+ analyze(BadCycleClass.Child.class);
+ assertLine("1", ICounter.FULLY_COVERED);
+ assertLine("2", ICounter.FULLY_COVERED);
+ assertLine("3", ICounter.FULLY_COVERED);
+ }
+
+}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/validation/BadCycleTestBase.java b/org.jacoco.core.test/src/org/jacoco/core/test/validation/BadCycleTestBase.java
new file mode 100644
index 00000000..dd7b4992
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/test/validation/BadCycleTestBase.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2016 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;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import org.jacoco.core.analysis.Analyzer;
+import org.jacoco.core.analysis.CoverageBuilder;
+import org.jacoco.core.analysis.IClassCoverage;
+import org.jacoco.core.analysis.ISourceFileCoverage;
+import org.jacoco.core.data.ExecutionDataStore;
+import org.jacoco.core.test.InstrumentingLoader;
+
+class BadCycleTestBase extends ValidationTestBase {
+
+ protected final InstrumentingLoader loader = new InstrumentingLoader();
+
+ BadCycleTestBase(final Class<?> target) throws Exception {
+ super(target);
+ }
+
+ BadCycleTestBase(final String srcFolder, final Class<?> target)
+ throws Exception {
+ super(srcFolder, target);
+ }
+
+ @Override
+ public final void setup() throws Exception {
+ // nop
+ }
+
+ @Override
+ protected final void run(Class<?> targetClass) throws Exception {
+ // nop
+ }
+
+ final void analyze(Class<?> cls) throws IOException {
+ final byte[] bytes = loader.getClassBytes(cls.getName());
+ final ExecutionDataStore store = loader.collect();
+
+ final CoverageBuilder builder = new CoverageBuilder();
+ final Analyzer analyzer = new Analyzer(store, builder);
+ analyzer.analyzeClass(bytes, "TestTarget");
+ final Collection<IClassCoverage> classes = builder.getClasses();
+ assertEquals(1, classes.size(), 0.0);
+ classCoverage = classes.iterator().next();
+ final Collection<ISourceFileCoverage> files = builder.getSourceFiles();
+ assertEquals(1, files.size(), 0.0);
+ sourceCoverage = files.iterator().next();
+
+ source = Source.getSourceFor(srcFolder, target);
+ }
+
+}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/validation/targets/BadCycleClass.java b/org.jacoco.core.test/src/org/jacoco/core/test/validation/targets/BadCycleClass.java
new file mode 100644
index 00000000..44e40cd1
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/test/validation/targets/BadCycleClass.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2016 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.targets;
+
+public class BadCycleClass {
+
+ public static class Base {
+ static final Child b = new Child();
+
+ static {
+ b.someMethod();
+ }
+ }
+
+ public static class Child extends Base {
+
+ static {
+ Stubs.nop("child clinit"); // $line-3$
+ }
+
+ public Child() {
+ Stubs.nop("child init"); // $line-1$
+ }
+
+ void someMethod() {
+ Stubs.nop("child someMethod"); // $line-2$
+ }
+
+ }
+
+ public static void main(String[] args) {
+ new Child();
+ }
+
+}