From adfe9de68814640a8f6919281a4c9713e9c1344e Mon Sep 17 00:00:00 2001 From: Jonathan Bell Date: Fri, 28 Dec 2018 15:47:05 -0500 Subject: [PATCH] Enhance coverage collection performance by caching probe arrays locally within classes --- .../AlreadyInstrumentedException.java | 7 + .../pitest/coverage/CoverageClassVisitor.java | 77 ++- .../pitest/coverage/CoverageTransformer.java | 17 +- .../analysis/AbstractCoverageStrategy.java | 31 +- .../ArrayProbeCoverageMethodVisitor.java | 95 +++- .../coverage/analysis/CoverageAnalyser.java | 32 +- .../LocalVariableCoverageMethodVisitor.java | 65 --- .../analysis/SimpleBlockCoverageVisitor.java | 172 ------ .../execute/JavassistCoverageInterceptor.java | 12 +- .../java/sun/pitest/CodeCoverageStore.java | 532 ++---------------- .../codeassist/CoverageClassVisitorTest.java | 50 -- .../sun/pitest/CodeCoverageStoreTest.java | 193 +------ 12 files changed, 221 insertions(+), 1062 deletions(-) create mode 100644 pitest/src/main/java/org/pitest/coverage/AlreadyInstrumentedException.java delete mode 100644 pitest/src/main/java/org/pitest/coverage/analysis/LocalVariableCoverageMethodVisitor.java delete mode 100644 pitest/src/main/java/org/pitest/coverage/analysis/SimpleBlockCoverageVisitor.java delete mode 100644 pitest/src/test/java/org/pitest/coverage/codeassist/CoverageClassVisitorTest.java diff --git a/pitest/src/main/java/org/pitest/coverage/AlreadyInstrumentedException.java b/pitest/src/main/java/org/pitest/coverage/AlreadyInstrumentedException.java new file mode 100644 index 000000000..63a629b35 --- /dev/null +++ b/pitest/src/main/java/org/pitest/coverage/AlreadyInstrumentedException.java @@ -0,0 +1,7 @@ +package org.pitest.coverage; + +public class AlreadyInstrumentedException extends IllegalArgumentException { + public AlreadyInstrumentedException(String msg) { + super(msg); + } +} diff --git a/pitest/src/main/java/org/pitest/coverage/CoverageClassVisitor.java b/pitest/src/main/java/org/pitest/coverage/CoverageClassVisitor.java index fb0e686d7..29e9d76db 100644 --- a/pitest/src/main/java/org/pitest/coverage/CoverageClassVisitor.java +++ b/pitest/src/main/java/org/pitest/coverage/CoverageClassVisitor.java @@ -18,11 +18,12 @@ package org.pitest.coverage; import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; import org.pitest.classinfo.BridgeMethodFilter; import org.pitest.classinfo.MethodFilteringAdapter; import org.pitest.coverage.analysis.CoverageAnalyser; - import sun.pitest.CodeCoverageStore; /** @@ -31,7 +32,14 @@ public class CoverageClassVisitor extends MethodFilteringAdapter { private final int classId; - private int probeCount = 0; + /** + * Probe count starts at 1, because probe "0" indicates that the class was hit + * by this test. + */ + private int probeCount = 1; + + private String className; + private boolean foundClinit; public CoverageClassVisitor(final int classId, final ClassWriter writer) { super(writer, BridgeMethodFilter.INSTANCE); @@ -42,19 +50,82 @@ public void registerProbes(final int number) { this.probeCount = this.probeCount + number; } + @Override + public void visit(int version, int access, String name, String signature, + String superName, String[] interfaces) { + super.visit(version, access, name, signature, superName, interfaces); + this.className = name; + } + + @Override public MethodVisitor visitMethodIfRequired(final int access, final String name, final String desc, final String signature, final String[] exceptions, final MethodVisitor methodVisitor) { + if (name.equals("")) { + foundClinit = true; + } + return new CoverageAnalyser(this, this.classId, this.probeCount, methodVisitor, access, name, desc, signature, exceptions); } + @Override + public FieldVisitor visitField(int access, String name, String descriptor, + String signature, Object value) { + + /* + If this class was already instrumented, then do not do it again! + */ + if (name.equals(CodeCoverageStore.PROBE_FIELD_NAME)) { + throw new AlreadyInstrumentedException("Class " + getClassName() + + " already has coverage instrumentation, but asked to do it again!"); + } + return super.visitField(access, name, descriptor, signature, value); + } + @Override public void visitEnd() { - CodeCoverageStore.registerClassProbes(this.classId, this.probeCount); + addCoverageProbeField(); } + private void addCoverageProbeField() { + + super.visitField(Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_PUBLIC + | Opcodes.ACC_SYNTHETIC, CodeCoverageStore.PROBE_FIELD_NAME, "[Z", null, + null); + + super.visitField(Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_PUBLIC + | Opcodes.ACC_SYNTHETIC, CodeCoverageStore.PROBE_LENGTH_FIELD_NAME, "I", + null, this.probeCount + 1); + + //If there is no , then generate one that sets the probe field directly + if (!foundClinit) { + MethodVisitor clinitMv = this.cv + .visitMethod(Opcodes.ACC_STATIC, "", "()V", null, null); + clinitMv.visitCode(); + + clinitMv.visitIntInsn( + (this.classId <= Byte.MAX_VALUE ? Opcodes.BIPUSH : Opcodes.SIPUSH), + this.classId); + clinitMv.visitIntInsn( + (1 + this.probeCount <= Byte.MAX_VALUE ? Opcodes.BIPUSH : Opcodes.SIPUSH), + 1 + this.probeCount); + clinitMv + .visitMethodInsn(Opcodes.INVOKESTATIC, CodeCoverageStore.CLASS_NAME, + "getOrRegisterClassProbes", "(II)[Z", false); + + clinitMv.visitFieldInsn(Opcodes.PUTSTATIC, className, + CodeCoverageStore.PROBE_FIELD_NAME, "[Z"); + clinitMv.visitInsn(Opcodes.RETURN); + clinitMv.visitMaxs(0, 0); + clinitMv.visitEnd(); + } + } + + public String getClassName() { + return className; + } } diff --git a/pitest/src/main/java/org/pitest/coverage/CoverageTransformer.java b/pitest/src/main/java/org/pitest/coverage/CoverageTransformer.java index e7e2d02df..7f34ad90e 100644 --- a/pitest/src/main/java/org/pitest/coverage/CoverageTransformer.java +++ b/pitest/src/main/java/org/pitest/coverage/CoverageTransformer.java @@ -46,14 +46,25 @@ public byte[] transform(final ClassLoader loader, final String className, private byte[] transformBytes(final ClassLoader loader, final String className, final byte[] classfileBuffer) { final ClassReader reader = new ClassReader(classfileBuffer); + + /* + Make sure that this class has not already been instrumented for coverage + generation. It is possible that some other bytecode instrumentation tool + would try to redefine a class (that we already added coverage tracking to), + in which case we will just allow that previous coverage tracking to stand. + */ final ClassWriter writer = new ComputeClassWriter( new ClassloaderByteArraySource(loader), this.computeCache, FrameOptions.pickFlags(classfileBuffer)); final int id = CodeCoverageStore.registerClass(className); - reader.accept(new CoverageClassVisitor(id, writer), - ClassReader.EXPAND_FRAMES); - return writer.toByteArray(); + try { + reader.accept(new CoverageClassVisitor(id, writer), + ClassReader.EXPAND_FRAMES); + return writer.toByteArray(); + } catch (AlreadyInstrumentedException ex) { + return null; + } } private boolean shouldInclude(final String className) { diff --git a/pitest/src/main/java/org/pitest/coverage/analysis/AbstractCoverageStrategy.java b/pitest/src/main/java/org/pitest/coverage/analysis/AbstractCoverageStrategy.java index 28a64ea22..17ed132af 100644 --- a/pitest/src/main/java/org/pitest/coverage/analysis/AbstractCoverageStrategy.java +++ b/pitest/src/main/java/org/pitest/coverage/analysis/AbstractCoverageStrategy.java @@ -12,6 +12,7 @@ abstract class AbstractCoverageStrategy extends AdviceAdapter { + protected final String className; protected final MethodVisitor methodVisitor; protected final int classId; protected final int probeOffset; @@ -19,23 +20,15 @@ abstract class AbstractCoverageStrategy extends AdviceAdapter { private final InstructionCounter counter; - /** - * label to mark start of try finally block that is added to each method - */ - private final Label before = new Label(); - - /** - * label to mark handler block of try finally - */ - private final Label handler = new Label(); - protected int probeCount = 0; AbstractCoverageStrategy(List blocks, InstructionCounter counter, final int classId, final MethodVisitor writer, final int access, - final String name, final String desc, final int probeOffset) { + final String className, final String name, final String desc, final int probeOffset) { super(ASMVersion.ASM_VERSION, writer, access, name, desc); + + this.className = className; this.methodVisitor = writer; this.classId = classId; this.counter = counter; @@ -54,22 +47,6 @@ public void visitCode() { super.visitCode(); prepare(); - - this.mv.visitLabel(this.before); - } - - @Override - public void visitMaxs(final int maxStack, final int maxLocals) { - - this.mv.visitTryCatchBlock(this.before, this.handler, this.handler, null); - this.mv.visitLabel(this.handler); - - generateProbeReportCode(); - - this.mv.visitInsn(ATHROW); - - // values actually unimportant as we're using compute max - this.mv.visitMaxs(maxStack, this.nextLocal); } @Override diff --git a/pitest/src/main/java/org/pitest/coverage/analysis/ArrayProbeCoverageMethodVisitor.java b/pitest/src/main/java/org/pitest/coverage/analysis/ArrayProbeCoverageMethodVisitor.java index 7a4d057f6..3f59b718a 100644 --- a/pitest/src/main/java/org/pitest/coverage/analysis/ArrayProbeCoverageMethodVisitor.java +++ b/pitest/src/main/java/org/pitest/coverage/analysis/ArrayProbeCoverageMethodVisitor.java @@ -23,65 +23,100 @@ import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.pitest.mutationtest.engine.gregor.analysis.InstructionCounter; - import sun.pitest.CodeCoverageStore; /** - * Instruments a method adding probes at each line. The strategy requires the - * compiler to be configured to add line number debug information. * - * Probes are implemented by adding an array to each method. Lines hits are - * registered by a write to this local array. Each method exit point is then - * augmented with a call that passes this array to the coverage store class that - * handles communication of this data back to the parent process on the - * completion of each test. + * Instruments a method adding probes at each block. + * + * Probes are implemented by adding an array to each class. Block hits are + * registered by a write to this local array. The array is registered upon class + * initialization with the CodeCoverageStore, and all methods in the same class + * share the same array. The coverage store class reads this array at the end of + * the test and handles communication of this data back to the parent process. + * + * The old approach was to allocate an array in *each* invocation of each method, + * and merge this in to a global array, which could get flushed between test runs. + * The approach implemented here requires far fewer allocations and is faster, plus + * it's better from a concurrency perspective (no locking needed except when first + * initializing the coverage probe array). + * + * + * Here's a source-level example of the instrumentation result: * - * All methods are wrapped in a try finally block to ensure that coverage data - * is sent in the event of a runtime exception. + * public class Foo { + * public static final int $$pitCoverageProbeSize = 10; //however many blocks there are + 1 + * public static final byte[] $$pitCoverageProbes = CodeCoverageStore.getOrRegisterClassProbes(thisClassID,$$pitCoverageProbeSize); + * + * private void bar(){ + * byte[] localRefToProbes = $$pitCoverageProbes; + * //line of code + * localRefToProbes[1] = 1; //assuming above line was probe 1 + * } + * + * } + * + * CodeCoverageStore maintains a reference to all of these $$pitCoverageProbes arrays + * and empties them out between each test. * - * Creating a new array on each method entry is not cheap - other coverage - * systems add a static field used across all methods. We must clear down all - * coverage history for each test however. Resetting static fields in all loaded - * classes would be messy to implement - it may or may not be faster than the - * current approach. */ public class ArrayProbeCoverageMethodVisitor extends AbstractCoverageStrategy { - private int probeHitArrayLocal; + private int probeHitArrayLocal; public ArrayProbeCoverageMethodVisitor(List blocks, InstructionCounter counter, final int classId, - final MethodVisitor writer, final int access, final String name, + final MethodVisitor writer, final int access, final String className, final String name, final String desc, final int probeOffset) { - super(blocks, counter, classId, writer, access, name, desc, probeOffset); + super(blocks, counter, classId, writer, access, className, name, desc, probeOffset); + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, + String desc, boolean itf) { + + super.visitMethodInsn(opcode, owner, name, desc, itf); + } + + @Override + public void visitFieldInsn(int opcode, String owner, String name, + String desc) { + super.visitFieldInsn(opcode, owner, name, desc); } @Override void prepare() { + if (getName().equals("")) { + this.mv.visitIntInsn(Opcodes.SIPUSH, this.classId); + this.mv.visitFieldInsn(Opcodes.GETSTATIC, this.className, CodeCoverageStore.PROBE_LENGTH_FIELD_NAME,"I"); + this.mv + .visitMethodInsn(Opcodes.INVOKESTATIC, CodeCoverageStore.CLASS_NAME, + "getOrRegisterClassProbes", "(II)[Z", false); + this.mv.visitFieldInsn(Opcodes.PUTSTATIC, className, + CodeCoverageStore.PROBE_FIELD_NAME, "[Z"); + } this.probeHitArrayLocal = newLocal(Type.getType("[Z")); - pushConstant(this.blocks.size()); - this.mv.visitIntInsn(NEWARRAY, T_BOOLEAN); + this.mv.visitFieldInsn(Opcodes.GETSTATIC, className, + CodeCoverageStore.PROBE_FIELD_NAME, "[Z"); + + //Make sure that we recorded that the class was hit + this.mv.visitInsn(DUP); + this.mv.visitInsn(ICONST_0); + this.mv.visitInsn(ICONST_1); + this.mv.visitInsn(BASTORE); this.mv.visitVarInsn(ASTORE, this.probeHitArrayLocal); } @Override void generateProbeReportCode() { - - pushConstant(this.classId); - pushConstant(this.probeOffset); - this.mv.visitVarInsn(ALOAD, this.probeHitArrayLocal); - - this.methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, - CodeCoverageStore.CLASS_NAME, CodeCoverageStore.PROBE_METHOD_NAME, - "(II[Z)V", false); } @Override void insertProbe() { this.mv.visitVarInsn(ALOAD, this.probeHitArrayLocal); - pushConstant(this.probeCount); - pushConstant(1); + pushConstant(this.probeOffset + this.probeCount); + this.mv.visitInsn(ICONST_1); this.mv.visitInsn(BASTORE); } diff --git a/pitest/src/main/java/org/pitest/coverage/analysis/CoverageAnalyser.java b/pitest/src/main/java/org/pitest/coverage/analysis/CoverageAnalyser.java index d2108756a..f5753ae25 100644 --- a/pitest/src/main/java/org/pitest/coverage/analysis/CoverageAnalyser.java +++ b/pitest/src/main/java/org/pitest/coverage/analysis/CoverageAnalyser.java @@ -8,7 +8,6 @@ import org.pitest.coverage.CoverageClassVisitor; import org.pitest.mutationtest.engine.gregor.analysis.DefaultInstructionCounter; import org.pitest.mutationtest.engine.gregor.analysis.InstructionTrackingMethodVisitor; - import sun.pitest.CodeCoverageStore; /** @@ -45,34 +44,11 @@ public void visitEnd() { CodeCoverageStore.registerMethod(this.classId, this.name, this.desc, this.probeOffset, (this.probeOffset + blocks.size()) - 1); - // according to the jvm spec - // "There must never be an uninitialized class instance in a local variable in code protected by an exception handler" - // the code to add finally blocks used by the local variable and array based - // probe approaches is not currently - // able to meet this guarantee for constructors. Although they appear to - // work, they are rejected by the - // java 7 verifier - hence fall back to a simple but slow approach. final DefaultInstructionCounter counter = new DefaultInstructionCounter(); - - if ((blockCount == 1) || this.name.equals("")) { - accept(new InstructionTrackingMethodVisitor( - new SimpleBlockCoverageVisitor(blocks, counter, this.classId, - this.mv, this.access, this.name, this.desc, this.probeOffset), - counter)); - } else if ((blockCount <= MAX_SUPPORTED_LOCAL_PROBES) && (blockCount >= 1)) { - accept(new InstructionTrackingMethodVisitor( - new LocalVariableCoverageMethodVisitor(blocks, counter, this.classId, - this.mv, this.access, this.name, this.desc, this.probeOffset), - counter)); - } else { - // for now fall back to the naive implementation - could instead use array - // passing version - accept(new InstructionTrackingMethodVisitor( - new ArrayProbeCoverageMethodVisitor(blocks, counter, this.classId, - this.mv, this.access, this.name, this.desc, this.probeOffset), - counter)); - } - + accept(new InstructionTrackingMethodVisitor( + new ArrayProbeCoverageMethodVisitor(blocks, counter, this.classId, + this.mv, this.access, parent.getClassName(), this.name, this.desc, + this.probeOffset), counter)); } private List findRequriedProbeLocations() { diff --git a/pitest/src/main/java/org/pitest/coverage/analysis/LocalVariableCoverageMethodVisitor.java b/pitest/src/main/java/org/pitest/coverage/analysis/LocalVariableCoverageMethodVisitor.java deleted file mode 100644 index 171d3a477..000000000 --- a/pitest/src/main/java/org/pitest/coverage/analysis/LocalVariableCoverageMethodVisitor.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.pitest.coverage.analysis; - -import java.util.List; - -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.pitest.mutationtest.engine.gregor.analysis.InstructionCounter; - -import sun.pitest.CodeCoverageStore; - -/** - * Uses local variables as block probes within methods. - * - * Inserts a finally block the method, posting the probes hits to the code - * coverage store via specialised methods for each number of probes. - * - * The range of methods this approach can be applied to is limited by the - * maximum airty of the overloaded methods on the coverage store. - */ -class LocalVariableCoverageMethodVisitor extends AbstractCoverageStrategy { - - private int[] locals; - - LocalVariableCoverageMethodVisitor(final List blocks, - final InstructionCounter counter, final int classId, - final MethodVisitor writer, final int access, final String name, - final String desc, final int probeOffset) { - super(blocks, counter, classId, writer, access, name, desc, probeOffset); - } - - @Override - void prepare() { - this.locals = new int[this.blocks.size()]; - for (int i = 0; i != this.blocks.size(); i++) { - this.locals[i] = newLocal(Type.getType("Z")); - pushConstant(0); - this.mv.visitVarInsn(ISTORE, this.locals[i]); - } - } - - @Override - void insertProbe() { - pushConstant(1); - this.mv.visitVarInsn(ISTORE, this.locals[this.probeCount]); - } - - @Override - protected void generateProbeReportCode() { - - pushConstant(this.classId); - pushConstant(this.probeOffset); - - for (final int i : this.locals) { - this.mv.visitVarInsn(ILOAD, i); - } - - this.methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, - CodeCoverageStore.CLASS_NAME, CodeCoverageStore.PROBE_METHOD_NAME, - "(II" - + String.format(String.format("%%0%dd", this.blocks.size()), 0) - .replace("0", "Z") + ")V", false); - } - -} diff --git a/pitest/src/main/java/org/pitest/coverage/analysis/SimpleBlockCoverageVisitor.java b/pitest/src/main/java/org/pitest/coverage/analysis/SimpleBlockCoverageVisitor.java deleted file mode 100644 index 004d95cbf..000000000 --- a/pitest/src/main/java/org/pitest/coverage/analysis/SimpleBlockCoverageVisitor.java +++ /dev/null @@ -1,172 +0,0 @@ -package org.pitest.coverage.analysis; - -import java.util.List; - -import org.objectweb.asm.Handle; -import org.objectweb.asm.Label; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.pitest.bytecode.ASMVersion; -import org.pitest.mutationtest.engine.gregor.analysis.InstructionCounter; - -import sun.pitest.CodeCoverageStore; - -/** - * Instruments via a method call at each line. - * - * This simplistic approach is generally slow, but does not require finally - * blocks that are difficult to generate correctly for constructors. - * - * This simple approach should however provide better performance for single - * line methods. - */ -public class SimpleBlockCoverageVisitor extends MethodVisitor { - private final MethodVisitor methodVisitor; - private final int classId; - - private final int probeOffset; - - private final InstructionCounter counter; - private final List blocks; - - private int probeCount = 0; - - public SimpleBlockCoverageVisitor(List blocks, - InstructionCounter counter, final int classId, - final MethodVisitor writer, final int access, final String name, - final String desc, final int probeOffset) { - super(ASMVersion.ASM_VERSION, writer); - - this.counter = counter; - this.methodVisitor = writer; - this.classId = classId; - this.blocks = blocks; - - this.probeOffset = probeOffset; - } - - @Override - public void visitFrame(final int type, final int nLocal, - final Object[] local, final int nStack, final Object[] stack) { - insertProbeIfAppropriate(); - super.visitFrame(type, nLocal, local, nStack, stack); - } - - @Override - public void visitInsn(final int opcode) { - insertProbeIfAppropriate(); - super.visitInsn(opcode); - } - - @Override - public void visitIntInsn(final int opcode, final int operand) { - insertProbeIfAppropriate(); - super.visitIntInsn(opcode, operand); - } - - @Override - public void visitVarInsn(final int opcode, final int var) { - insertProbeIfAppropriate(); - super.visitVarInsn(opcode, var); - } - - @Override - public void visitTypeInsn(final int opcode, final String type) { - insertProbeIfAppropriate(); - super.visitTypeInsn(opcode, type); - } - - @Override - public void visitFieldInsn(final int opcode, final String owner, - final String name, final String desc) { - insertProbeIfAppropriate(); - super.visitFieldInsn(opcode, owner, name, desc); - } - - @Override - public void visitMethodInsn(final int opcode, final String owner, - final String name, final String desc, boolean itf) { - insertProbeIfAppropriate(); - super.visitMethodInsn(opcode, owner, name, desc, itf); - } - - @Override - public void visitInvokeDynamicInsn(final String name, final String desc, - final Handle bsm, final Object... bsmArgs) { - insertProbeIfAppropriate(); - super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); - } - - @Override - public void visitJumpInsn(final int opcode, final Label label) { - insertProbeIfAppropriate(); - super.visitJumpInsn(opcode, label); - } - - @Override - public void visitLabel(final Label label) { - super.visitLabel(label); - // note - probe goes after the label - insertProbeIfAppropriate(); - } - - @Override - public void visitLdcInsn(final Object cst) { - insertProbeIfAppropriate(); - super.visitLdcInsn(cst); - } - - @Override - public void visitIincInsn(final int var, final int increment) { - insertProbeIfAppropriate(); - super.visitIincInsn(var, increment); - } - - @Override - public void visitTableSwitchInsn(final int min, final int max, - final Label dflt, final Label... labels) { - insertProbeIfAppropriate(); - super.visitTableSwitchInsn(min, max, dflt, labels); - } - - @Override - public void visitLookupSwitchInsn(final Label dflt, final int[] keys, - final Label[] labels) { - insertProbeIfAppropriate(); - super.visitLookupSwitchInsn(dflt, keys, labels); - } - - @Override - public void visitMultiANewArrayInsn(final String desc, final int dims) { - insertProbeIfAppropriate(); - super.visitMultiANewArrayInsn(desc, dims); - } - - @Override - public void visitLineNumber(final int line, final Label start) { - insertProbeIfAppropriate(); - super.visitLineNumber(line, start); - } - - private void insertProbeIfAppropriate() { - if (needsProbe(this.counter.currentInstructionCount())) { - this.methodVisitor.visitLdcInsn(this.classId); - this.methodVisitor.visitLdcInsn(this.probeCount + this.probeOffset); - - this.methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, - CodeCoverageStore.CLASS_NAME, "visitSingleProbe", "(II)V", false); - - this.probeCount++; - } - } - - private boolean needsProbe(int currentInstructionCount) { - for (final Block each : this.blocks) { - if (each.firstInstructionIs(currentInstructionCount - 1)) { - return true; - } - } - return false; - } - -} diff --git a/pitest/src/main/java/org/pitest/coverage/execute/JavassistCoverageInterceptor.java b/pitest/src/main/java/org/pitest/coverage/execute/JavassistCoverageInterceptor.java index 0a05de8c9..1f46c3def 100644 --- a/pitest/src/main/java/org/pitest/coverage/execute/JavassistCoverageInterceptor.java +++ b/pitest/src/main/java/org/pitest/coverage/execute/JavassistCoverageInterceptor.java @@ -25,12 +25,12 @@ import org.pitest.bytecode.FrameOptions; import org.pitest.classinfo.ComputeClassWriter; import org.pitest.classpath.ClassloaderByteArraySource; +import org.pitest.coverage.AlreadyInstrumentedException; import org.pitest.coverage.CoverageClassVisitor; import org.pitest.reflection.Reflection; import org.pitest.util.IsolationUtils; import org.pitest.util.StreamUtil; import org.pitest.util.Unchecked; - import sun.pitest.CodeCoverageStore; public final class JavassistCoverageInterceptor { @@ -80,9 +80,13 @@ private static byte[] transformBytes(final ClassLoader loader, // to support powermock if this assumption changed, so this code would most likely be deleted. final int id = CodeCoverageStore.registerClass(className); - reader.accept(new CoverageClassVisitor(id, writer), - ClassReader.EXPAND_FRAMES); - return writer.toByteArray(); + try { + reader.accept(new CoverageClassVisitor(id, writer), + ClassReader.EXPAND_FRAMES); + return writer.toByteArray(); + } catch (AlreadyInstrumentedException ex) { + return null; + } } private static InputStream returnNormalBytes(final Object classPath, diff --git a/pitest/src/main/java/sun/pitest/CodeCoverageStore.java b/pitest/src/main/java/sun/pitest/CodeCoverageStore.java index f946ddcb7..5be77ddff 100644 --- a/pitest/src/main/java/sun/pitest/CodeCoverageStore.java +++ b/pitest/src/main/java/sun/pitest/CodeCoverageStore.java @@ -21,6 +21,7 @@ package sun.pitest; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Map; import java.util.Map.Entry; @@ -43,6 +44,9 @@ public final class CodeCoverageStore { private static InvokeReceiver invokeQueue; private static int classId = 0; + public static final String PROBE_FIELD_NAME = "$$pitCoverageProbes"; + public static final String PROBE_LENGTH_FIELD_NAME = "$$pitCoverageProbeSize"; + // array of probe hits, first slot indicates any hits to the class. // testing suggests boolean array with synchronization to ensure happens // before relationship significantly outperforms @@ -58,504 +62,11 @@ public static void init(final InvokeReceiver invokeQueue) { private CodeCoverageStore() { } - public static void visitSingleProbe(final int classId, final int probe) { // NO_UCD - final boolean[] bs = CLASS_HITS.get(classId); - bs[CLASS_HIT_INDEX] = true; - bs[probe + 1] = true; - } - - public static void visitProbes(final int classId, final int offset, - final boolean[] probes) { // NO_UCD - final boolean[] bs = CLASS_HITS.get(classId); - bs[CLASS_HIT_INDEX] = true; - for (int i = 0; i != probes.length; i++) { - if (probes[i]) { - bs[i + offset + 1] = true; - } - } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Overloaded special case implementations for methods with 1 to N probes. - // Allows probes to be implemented as - // local variables and the loop in the array based version to be unrolled. - // - - public static void visitProbes(final int classId, final int offset, - final boolean p0) { // NO_UCD - final boolean[] bs = CLASS_HITS.get(classId); - bs[CLASS_HIT_INDEX] = true; - if (p0) { - bs[offset + 1] = true; - } - } - - public static void visitProbes(final int classId, final int offset, - final boolean p0, final boolean p1) { // NO_UCD - final boolean[] bs = CLASS_HITS.get(classId); - bs[CLASS_HIT_INDEX] = true; - if (p0) { - bs[offset + 1] = true; - } - if (p1) { - bs[offset + 2] = true; - } - } - - public static void visitProbes(final int classId, final int offset, - final boolean p0, final boolean p1, final boolean p2) { // NO_UCD - final boolean[] bs = CLASS_HITS.get(classId); - bs[CLASS_HIT_INDEX] = true; - if (p0) { - bs[offset + 1] = true; - } - if (p1) { - bs[offset + 2] = true; - } - if (p2) { - bs[offset + 3] = true; - } - } - - public static void visitProbes(final int classId, final int offset, - final boolean p0, final boolean p1, final boolean p2, final boolean p3) { // NO_UCD - final boolean[] bs = CLASS_HITS.get(classId); - bs[CLASS_HIT_INDEX] = true; - if (p0) { - bs[offset + 1] = true; - } - if (p1) { - bs[offset + 2] = true; - } - if (p2) { - bs[offset + 3] = true; - } - if (p3) { - bs[offset + 4] = true; - } - } - - public static void visitProbes(final int classId, final int offset, - final boolean p0, final boolean p1, final boolean p2, final boolean p3, - final boolean p4) { // NO_UCD - final boolean[] bs = CLASS_HITS.get(classId); - bs[CLASS_HIT_INDEX] = true; - if (p0) { - bs[offset + 1] = true; - } - if (p1) { - bs[offset + 2] = true; - } - if (p2) { - bs[offset + 3] = true; - } - if (p3) { - bs[offset + 4] = true; - } - if (p4) { - bs[offset + 5] = true; - } - } - - public static void visitProbes(final int classId, final int offset, - final boolean p0, final boolean p1, final boolean p2, final boolean p3, - final boolean p4, final boolean p5) { // NO_UCD - final boolean[] bs = CLASS_HITS.get(classId); - bs[CLASS_HIT_INDEX] = true; - if (p0) { - bs[offset + 1] = true; - } - if (p1) { - bs[offset + 2] = true; - } - if (p2) { - bs[offset + 3] = true; - } - if (p3) { - bs[offset + 4] = true; - } - if (p4) { - bs[offset + 5] = true; - } - if (p5) { - bs[offset + 6] = true; - } - } - - public static void visitProbes(final int classId, final int offset, - final boolean p0, final boolean p1, final boolean p2, final boolean p3, - final boolean p4, final boolean p5, final boolean p6) { // NO_UCD - final boolean[] bs = CLASS_HITS.get(classId); - bs[CLASS_HIT_INDEX] = true; - if (p0) { - bs[offset + 1] = true; - } - if (p1) { - bs[offset + 2] = true; - } - if (p2) { - bs[offset + 3] = true; - } - if (p3) { - bs[offset + 4] = true; - } - if (p4) { - bs[offset + 5] = true; - } - if (p5) { - bs[offset + 6] = true; - } - if (p6) { - bs[offset + 7] = true; - } - } - - public static void visitProbes(final int classId, final int offset, - final boolean p0, final boolean p1, final boolean p2, final boolean p3, - final boolean p4, final boolean p5, final boolean p6, final boolean p7) { // NO_UCD - final boolean[] bs = CLASS_HITS.get(classId); - bs[CLASS_HIT_INDEX] = true; - if (p0) { - bs[offset + 1] = true; - } - if (p1) { - bs[offset + 2] = true; - } - if (p2) { - bs[offset + 3] = true; - } - if (p3) { - bs[offset + 4] = true; - } - if (p4) { - bs[offset + 5] = true; - } - if (p5) { - bs[offset + 6] = true; - } - if (p6) { - bs[offset + 7] = true; - } - if (p7) { - bs[offset + 8] = true; - } - } - - public static void visitProbes(final int classId, final int offset, - final boolean p0, final boolean p1, final boolean p2, final boolean p3, - final boolean p4, final boolean p5, final boolean p6, final boolean p7, - final boolean p8) { // NO_UCD - final boolean[] bs = CLASS_HITS.get(classId); - bs[CLASS_HIT_INDEX] = true; - if (p0) { - bs[offset + 1] = true; - } - if (p1) { - bs[offset + 2] = true; - } - if (p2) { - bs[offset + 3] = true; - } - if (p3) { - bs[offset + 4] = true; - } - if (p4) { - bs[offset + 5] = true; - } - if (p5) { - bs[offset + 6] = true; - } - if (p6) { - bs[offset + 7] = true; - } - if (p7) { - bs[offset + 8] = true; - } - if (p8) { - bs[offset + 9] = true; - } - } - - public static void visitProbes(final int classId, final int offset, - final boolean p0, final boolean p1, final boolean p2, final boolean p3, - final boolean p4, final boolean p5, final boolean p6, final boolean p7, - final boolean p8, final boolean p9) { // NO_UCD - final boolean[] bs = CLASS_HITS.get(classId); - bs[CLASS_HIT_INDEX] = true; - if (p0) { - bs[offset + 1] = true; - } - if (p1) { - bs[offset + 2] = true; - } - if (p2) { - bs[offset + 3] = true; - } - if (p3) { - bs[offset + 4] = true; - } - if (p4) { - bs[offset + 5] = true; - } - if (p5) { - bs[offset + 6] = true; - } - if (p6) { - bs[offset + 7] = true; - } - if (p7) { - bs[offset + 8] = true; - } - if (p8) { - bs[offset + 9] = true; - } - if (p9) { - bs[offset + 10] = true; - } - } - - public static void visitProbes(final int classId, final int offset, - final boolean p0, final boolean p1, final boolean p2, final boolean p3, - final boolean p4, final boolean p5, final boolean p6, final boolean p7, - final boolean p8, final boolean p9, final boolean p10) { // NO_UCD - final boolean[] bs = CLASS_HITS.get(classId); - bs[CLASS_HIT_INDEX] = true; - if (p0) { - bs[offset + 1] = true; - } - if (p1) { - bs[offset + 2] = true; - } - if (p2) { - bs[offset + 3] = true; - } - if (p3) { - bs[offset + 4] = true; - } - if (p4) { - bs[offset + 5] = true; - } - if (p5) { - bs[offset + 6] = true; - } - if (p6) { - bs[offset + 7] = true; - } - if (p7) { - bs[offset + 8] = true; - } - if (p8) { - bs[offset + 9] = true; - } - if (p9) { - bs[offset + 10] = true; - } - if (p10) { - bs[offset + 11] = true; - } - } - - public static void visitProbes(final int classId, final int offset, - final boolean p0, final boolean p1, final boolean p2, final boolean p3, - final boolean p4, final boolean p5, final boolean p6, final boolean p7, - final boolean p8, final boolean p9, final boolean p10, final boolean p11) { // NO_UCD - final boolean[] bs = CLASS_HITS.get(classId); - bs[CLASS_HIT_INDEX] = true; - if (p0) { - bs[offset + 1] = true; - } - if (p1) { - bs[offset + 2] = true; - } - if (p2) { - bs[offset + 3] = true; - } - if (p3) { - bs[offset + 4] = true; - } - if (p4) { - bs[offset + 5] = true; - } - if (p5) { - bs[offset + 6] = true; - } - if (p6) { - bs[offset + 7] = true; - } - if (p7) { - bs[offset + 8] = true; - } - if (p8) { - bs[offset + 9] = true; - } - if (p9) { - bs[offset + 10] = true; - } - if (p10) { - bs[offset + 11] = true; - } - if (p11) { - bs[offset + 12] = true; - } - } - - public static void visitProbes(final int classId, final int offset, - final boolean p0, final boolean p1, final boolean p2, final boolean p3, - final boolean p4, final boolean p5, final boolean p6, final boolean p7, - final boolean p8, final boolean p9, final boolean p10, final boolean p11, - final boolean p12) { // NO_UCD - final boolean[] bs = CLASS_HITS.get(classId); - bs[CLASS_HIT_INDEX] = true; - if (p0) { - bs[offset + 1] = true; - } - if (p1) { - bs[offset + 2] = true; - } - if (p2) { - bs[offset + 3] = true; - } - if (p3) { - bs[offset + 4] = true; - } - if (p4) { - bs[offset + 5] = true; - } - if (p5) { - bs[offset + 6] = true; - } - if (p6) { - bs[offset + 7] = true; - } - if (p7) { - bs[offset + 8] = true; - } - if (p8) { - bs[offset + 9] = true; - } - if (p9) { - bs[offset + 10] = true; - } - if (p10) { - bs[offset + 11] = true; - } - if (p11) { - bs[offset + 12] = true; - } - if (p12) { - bs[offset + 13] = true; - } - } - - public static void visitProbes(final int classId, final int offset, - final boolean p0, final boolean p1, final boolean p2, final boolean p3, - final boolean p4, final boolean p5, final boolean p6, final boolean p7, - final boolean p8, final boolean p9, final boolean p10, final boolean p11, - final boolean p12, final boolean p13) { // NO_UCD - final boolean[] bs = CLASS_HITS.get(classId); - bs[CLASS_HIT_INDEX] = true; - if (p0) { - bs[offset + 1] = true; - } - if (p1) { - bs[offset + 2] = true; - } - if (p2) { - bs[offset + 3] = true; - } - if (p3) { - bs[offset + 4] = true; - } - if (p4) { - bs[offset + 5] = true; - } - if (p5) { - bs[offset + 6] = true; - } - if (p6) { - bs[offset + 7] = true; - } - if (p7) { - bs[offset + 8] = true; - } - if (p8) { - bs[offset + 9] = true; - } - if (p9) { - bs[offset + 10] = true; - } - if (p10) { - bs[offset + 11] = true; - } - if (p11) { - bs[offset + 12] = true; - } - if (p12) { - bs[offset + 13] = true; - } - if (p13) { - bs[offset + 14] = true; - } - } - - public static void visitProbes(final int classId, final int offset, - final boolean p0, final boolean p1, final boolean p2, final boolean p3, - final boolean p4, final boolean p5, final boolean p6, final boolean p7, - final boolean p8, final boolean p9, final boolean p10, final boolean p11, - final boolean p12, final boolean p13, final boolean p14) { // NO_UCD - final boolean[] bs = CLASS_HITS.get(classId); - bs[CLASS_HIT_INDEX] = true; - if (p0) { - bs[offset + 1] = true; - } - if (p1) { - bs[offset + 2] = true; - } - if (p2) { - bs[offset + 3] = true; - } - if (p3) { - bs[offset + 4] = true; - } - if (p4) { - bs[offset + 5] = true; - } - if (p5) { - bs[offset + 6] = true; - } - if (p6) { - bs[offset + 7] = true; - } - if (p7) { - bs[offset + 8] = true; - } - if (p8) { - bs[offset + 9] = true; - } - if (p9) { - bs[offset + 10] = true; - } - if (p10) { - bs[offset + 11] = true; - } - if (p11) { - bs[offset + 12] = true; - } - if (p12) { - bs[offset + 13] = true; - } - if (p13) { - bs[offset + 14] = true; - } - if (p14) { - bs[offset + 15] = true; - } - } - public static synchronized void reset() { for (final Entry each : CLASS_HITS.entrySet()) { - CLASS_HITS.put(each.getKey(), new boolean[each.getValue().length]); + if (each.getValue()[0]) { //Probe 0 gets covered by any method that runs + Arrays.fill(each.getValue(), false); + } } } @@ -571,7 +82,7 @@ public static synchronized Collection getHits() { // final int[] mapping = classProbeToBlockMapping.get(classId); for (int probeId = 1; probeId != bs.length; probeId++) { if (bs[probeId]) { - blockHits.add(encode(classId, probeId - 1)); + blockHits.add(encode(classId, probeId)); } } } @@ -606,8 +117,31 @@ public static long encode(final int classId, final int line) { return ((long) classId << 32) | line; } - public static void registerClassProbes(final int classId, int probeCount) { - CLASS_HITS.put(classId, new boolean[probeCount + 1]); + public static boolean[] getOrRegisterClassProbes(final int classId, + int probeCount) { + boolean[] ret = CLASS_HITS.putIfAbsent(classId, new boolean[probeCount + 1]); + if (ret == null) { + return CLASS_HITS.get(classId); + } + /* + It's possible that some other java agent has transformed this class, which has + resulted in it getting more blocks. It seems like our intended behavior is to + still collect coverage of these new synthetic blocks, so we need to + make sure that our coverage array grows when the class is re-transformed, + and it's possible that we have already instrumented the class, causing its + coverage array to get set up at the wrong size. + */ + if (ret.length < probeCount + 1) { + synchronized (CLASS_HITS) { + ret = CLASS_HITS.get(classId); + if (ret.length < probeCount + 1) { + ret = new boolean[probeCount + 1]; + CLASS_HITS.put(classId, ret); + return ret; + } + } + } + return ret; } public static void resetAllStaticState() { diff --git a/pitest/src/test/java/org/pitest/coverage/codeassist/CoverageClassVisitorTest.java b/pitest/src/test/java/org/pitest/coverage/codeassist/CoverageClassVisitorTest.java deleted file mode 100644 index af277dda6..000000000 --- a/pitest/src/test/java/org/pitest/coverage/codeassist/CoverageClassVisitorTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.pitest.coverage.codeassist; - -import static org.junit.Assert.assertEquals; - -import java.util.Arrays; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.objectweb.asm.ClassWriter; -import org.pitest.coverage.CoverageClassVisitor; - -import sun.pitest.CodeCoverageStore; -import sun.pitest.InvokeReceiver; - -public class CoverageClassVisitorTest { - - private CoverageClassVisitor testee; - private int classId; - - @Mock - private ClassWriter w; - - @Mock - private InvokeReceiver invokeQueue; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - CodeCoverageStore.init(this.invokeQueue); - CodeCoverageStore.reset(); - this.classId = CodeCoverageStore.registerClass("foo"); - this.testee = new CoverageClassVisitor(this.classId, this.w); - } - - @Test - public void shouldRegisterProbesWithCodeCoverageStore() { - this.testee.registerProbes(6); - this.testee.visitEnd(); - - CodeCoverageStore.visitProbes(this.classId, 0, new boolean[] { false, - false, true, false, false, false, false }); - - assertEquals(Arrays.asList(CodeCoverageStore.encode(this.classId, 2)), - CodeCoverageStore.getHits()); - - } - -} diff --git a/pitest/src/test/java/sun/pitest/CodeCoverageStoreTest.java b/pitest/src/test/java/sun/pitest/CodeCoverageStoreTest.java index b27dec122..fab09aa03 100644 --- a/pitest/src/test/java/sun/pitest/CodeCoverageStoreTest.java +++ b/pitest/src/test/java/sun/pitest/CodeCoverageStoreTest.java @@ -90,9 +90,10 @@ public void shouldCodeAndEncodeWhenClassIdAndLineNumberAreZero() { @Test public void shouldClearHitCountersWhenReset() { final int classId = CodeCoverageStore.registerClass("foo"); - CodeCoverageStore.registerClassProbes(classId, 1); - CodeCoverageStore.visitProbes(classId, 0, new boolean[] { true }); + boolean[] ar = CodeCoverageStore.getOrRegisterClassProbes(classId, 2); + ar[0] = true; + ar[1] = true; CodeCoverageStore.reset(); final Collection actual = CodeCoverageStore.getHits(); @@ -103,14 +104,16 @@ public void shouldClearHitCountersWhenReset() { public void shouldBeSafeToAccessAcrossMultipleThreads() throws InterruptedException, ExecutionException { - CodeCoverageStore.registerClass("foo"); - CodeCoverageStore.registerClassProbes(0, 1); + int classId = CodeCoverageStore.registerClass("foo"); + boolean[] ar = CodeCoverageStore.getOrRegisterClassProbes(classId, 2); + ar[0] = true; + ar[1] = true; final Callable read = makeReader(); final ExecutorService pool = Executors.newFixedThreadPool(13); for (int i = 1; i != 13; i++) { - pool.submit(makeWriter(i)); + pool.submit(makeWriter(i, ar)); } final Future future = pool.submit(read); pool.shutdown(); @@ -119,179 +122,6 @@ public void shouldBeSafeToAccessAcrossMultipleThreads() } - @Test - public void shouldReportCorrectCoverageForSpecialisation1() { - final SideEffect2 se = (classId, probes) -> CodeCoverageStore.visitProbes(classId, 0, probes[0]); - assertLineCombinations(1, se); - } - - @Test - public void shouldReportCorrectCoverageForSpecialisation2() { - final SideEffect2 se = (classId, probes) -> CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1]); - assertLineCombinations(2, se); - } - - @Test - public void shouldReportCorrectCoverageForSpecialisation3() { - final SideEffect2 se = (classId, probes) -> CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], - probes[2]); - assertLineCombinations(3, se); - } - - @Test - public void shouldReportCorrectCoverageForSpecialisation4() { - final SideEffect2 se = (classId, probes) -> CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], - probes[2], probes[3]); - assertLineCombinations(4, se); - } - - @Test - public void shouldReportCorrectCoverageForSpecialisation5() { - final SideEffect2 se = (classId, probes) -> CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], - probes[2], probes[3], probes[4]); - assertLineCombinations(5, se); - } - - @Test - public void shouldReportCorrectCoverageForSpecialisation6() { - final SideEffect2 se = (classId, probes) -> CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], - probes[2], probes[3], probes[4], probes[5]); - assertLineCombinations(6, se); - } - - @Test - public void shouldReportCorrectCoverageForSpecialisation7() { - final SideEffect2 se = (classId, probes) -> CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], - probes[2], probes[3], probes[4], probes[5], probes[6]); - assertLineCombinations(7, se); - } - - @Test - public void shouldReportCorrectCoverageForSpecialisation8() { - final SideEffect2 se = (classId, probes) -> CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], - probes[2], probes[3], probes[4], probes[5], probes[6], probes[7]); - assertLineCombinations(8, se); - } - - @Test - public void shouldReportCorrectCoverageForSpecialisation9() { - final SideEffect2 se = (classId, probes) -> CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], - probes[2], probes[3], probes[4], probes[5], probes[6], probes[7], - probes[8]); - assertLineCombinations(9, se); - } - - @Test - public void shouldReportCorrectCoverageForSpecialisation10() { - final SideEffect2 se = (classId, probes) -> CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], - probes[2], probes[3], probes[4], probes[5], probes[6], probes[7], - probes[8], probes[9]); - assertLineCombinations(10, se); - } - - @Test - public void shouldReportCorrectCoverageForSpecialisation11() { - final SideEffect2 se = (classId, probes) -> CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], - probes[2], probes[3], probes[4], probes[5], probes[6], probes[7], - probes[8], probes[9], probes[10]); - assertLineCombinations(11, se); - } - - @Test - public void shouldReportCorrectCoverageForSpecialisation12() { - final SideEffect2 se = (classId, probes) -> CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], - probes[2], probes[3], probes[4], probes[5], probes[6], probes[7], - probes[8], probes[9], probes[10], probes[11]); - assertLineCombinations(12, se); - } - - @Test - public void shouldReportCorrectCoverageForSpecialisation13() { - final SideEffect2 se = (classId, probes) -> CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], - probes[2], probes[3], probes[4], probes[5], probes[6], probes[7], - probes[8], probes[9], probes[10], probes[11], probes[12]); - assertLineCombinations(13, se); - } - - @Test - public void shouldReportCorrectCoverageForSpecialisation14() { - final SideEffect2 se = (classId, probes) -> CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], - probes[2], probes[3], probes[4], probes[5], probes[6], probes[7], - probes[8], probes[9], probes[10], probes[11], probes[12], - probes[13]); - assertLineCombinations(14, se); - } - - @Test - public void shouldReportCorrectCoverageForSpecialisation15() { - final SideEffect2 se = (classId, probes) -> CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], - probes[2], probes[3], probes[4], probes[5], probes[6], probes[7], - probes[8], probes[9], probes[10], probes[11], probes[12], - probes[13], probes[14]); - assertLineCombinations(15, se); - } - - private void assertLineCombinations(final int size, - final SideEffect2 function) { - ascendingPermuation(size, function); - CodeCoverageStore.resetAllStaticState(); - descendingPermutation(size, function); - } - - private void ascendingPermuation(final int size, - final SideEffect2 function) { - final int classId = CodeCoverageStore.registerClass("foo"); - CodeCoverageStore.registerClassProbes(classId, 15); - final boolean[] probes = new boolean[size]; - - function.apply(classId, probes); - assertDoesNotHitLine(classId, 1, 2, 3); - - for (int i = 0; i != probes.length; i++) { - probes[i] = true; - function.apply(classId, probes); - for (int j = 0; j <= i; j++) { - assertHitsLine(classId, j); - } - for (int j = i + 1; j != probes.length; j++) { - assertDoesNotHitLine(classId, j); - } - } - } - - private void descendingPermutation(final int size, - final SideEffect2 function) { - final int classId = CodeCoverageStore.registerClass("foo"); - CodeCoverageStore.registerClassProbes(classId, 15); - final boolean[] probes = new boolean[size]; - - for (int i = probes.length - 1; i != 0; i--) { - probes[i] = true; - function.apply(classId, probes); - for (int j = 0; j != i; j++) { - assertDoesNotHitLine(classId, j); - } - for (int j = probes.length; j != i; j--) { - assertHitsLine(classId, j - 1); - } - } - } - - private void assertHitsLine(final int classId, final int... i) { - final Collection actual = CodeCoverageStore.getHits(); - for (final int probe : i) { - assertThat(actual).contains(CodeCoverageStore.encode(classId, probe)); - } - } - - private void assertDoesNotHitLine(final int classId, final int... i) { - final Collection actual = CodeCoverageStore.getHits(); - for (final int probe : i) { - assertThat(actual).doesNotContain( - CodeCoverageStore.encode(classId, probe)); - } - } - private Callable makeReader() { final Callable read = new Callable() { @Override @@ -323,15 +153,16 @@ private long pointlesslyIterateCollection() { return read; } - private static Runnable makeWriter(final int sleepPeriod) { + private static Runnable makeWriter(final int sleepPeriod, final boolean[] ar) { final Runnable write = () -> { for (int i = 0; i != 1000; i++) { try { Thread.sleep(sleepPeriod); } catch (final InterruptedException e) { } - final boolean b[] = new boolean[1000]; - CodeCoverageStore.visitProbes(0, 0, b); + ar[0] = true; + ar[1] = true; + ar[2] = true; } }; return write;