blob: 2ed92fa3f7ff21894c35d99ae6b5b4d9d1e1385a [file] [log] [blame]
/*******************************************************************************
* 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:
* Marc R. Hoffmann - initial API and implementation
*
*******************************************************************************/
package org.jacoco.core.runtime;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.jacoco.core.internal.instr.InstrSupport;
import org.jacoco.core.test.TargetLoader;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;
/**
* Abstract test base for {@link IRuntime} implementations.
*/
public abstract class RuntimeTestBase {
private RuntimeData data;
private IRuntime runtime;
private TestStorage storage;
abstract IRuntime createRuntime();
@Before
public void setup() throws Exception {
data = new RuntimeData();
runtime = createRuntime();
runtime.startup(data);
storage = new TestStorage();
}
@After
public void shutdown() {
runtime.shutdown();
}
@Test
public void testDataAccessor() throws InstantiationException,
IllegalAccessException {
ITarget t = generateAndInstantiateClass(1234);
data.collect(storage, storage, false);
storage.assertData(1234, t.get());
}
@Test
public void testNoLocalVariablesInDataAccessor()
throws InstantiationException, IllegalAccessException {
runtime.generateDataAccessor(1001, "Target", 5, new MethodVisitor(
InstrSupport.ASM_API_VERSION) {
@Override
public void visitVarInsn(int opcode, int var) {
fail("No usage of local variables allowed.");
}
});
}
@Test
public void testExecutionRecording() throws InstantiationException,
IllegalAccessException {
generateAndInstantiateClass(1001).a();
data.collect(storage, storage, false);
storage.assertSize(1);
final boolean[] data = storage.getData(1001).getProbes();
assertTrue(data[0]);
assertFalse(data[1]);
}
@Test
public void testLoadSameClassTwice() throws InstantiationException,
IllegalAccessException {
generateAndInstantiateClass(1001).a();
generateAndInstantiateClass(1001).b();
data.collect(storage, storage, false);
storage.assertSize(1);
final boolean[] data = storage.getData(1001).getProbes();
assertTrue(data[0]);
assertTrue(data[1]);
}
/**
* Creates a new class with the given id, loads this class and instantiates
* it. The constructor of the generated class will request the probe array
* from the runtime under test.
*/
private ITarget generateAndInstantiateClass(int classid)
throws InstantiationException, IllegalAccessException {
final String className = "org/jacoco/test/targets/RuntimeTestTarget_"
+ classid;
Type classType = Type.getObjectType(className);
final ClassWriter writer = new ClassWriter(0);
writer.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, className, null,
"java/lang/Object",
new String[] { Type.getInternalName(ITarget.class) });
writer.visitField(InstrSupport.DATAFIELD_ACC,
InstrSupport.DATAFIELD_NAME, InstrSupport.DATAFIELD_DESC, null,
null);
// Constructor
GeneratorAdapter gen = new GeneratorAdapter(writer.visitMethod(
Opcodes.ACC_PUBLIC, "<init>", "()V", null, new String[0]),
Opcodes.ACC_PUBLIC, "<init>", "()V");
gen.visitCode();
gen.loadThis();
gen.invokeConstructor(Type.getType(Object.class), new Method("<init>",
"()V"));
gen.loadThis();
final int size = runtime.generateDataAccessor(classid, className, 2,
gen);
gen.putStatic(classType, InstrSupport.DATAFIELD_NAME,
Type.getObjectType(InstrSupport.DATAFIELD_DESC));
gen.returnValue();
gen.visitMaxs(size + 1, 0);
gen.visitEnd();
// get()
gen = new GeneratorAdapter(writer.visitMethod(Opcodes.ACC_PUBLIC,
"get", "()[Z", null, new String[0]), Opcodes.ACC_PUBLIC, "get",
"()[Z");
gen.visitCode();
gen.getStatic(classType, InstrSupport.DATAFIELD_NAME,
Type.getObjectType(InstrSupport.DATAFIELD_DESC));
gen.returnValue();
gen.visitMaxs(1, 0);
gen.visitEnd();
// a()
gen = new GeneratorAdapter(writer.visitMethod(Opcodes.ACC_PUBLIC, "a",
"()V", null, new String[0]), Opcodes.ACC_PUBLIC, "a", "()V");
gen.visitCode();
gen.getStatic(classType, InstrSupport.DATAFIELD_NAME,
Type.getObjectType(InstrSupport.DATAFIELD_DESC));
gen.push(0);
gen.push(1);
gen.arrayStore(Type.BOOLEAN_TYPE);
gen.returnValue();
gen.visitMaxs(3, 0);
gen.visitEnd();
// b()
gen = new GeneratorAdapter(writer.visitMethod(Opcodes.ACC_PUBLIC, "b",
"()V", null, new String[0]), Opcodes.ACC_PUBLIC, "b", "()V");
gen.visitCode();
gen.getStatic(classType, InstrSupport.DATAFIELD_NAME,
Type.getObjectType(InstrSupport.DATAFIELD_DESC));
gen.push(1);
gen.push(1);
gen.arrayStore(Type.BOOLEAN_TYPE);
gen.returnValue();
gen.visitMaxs(3, 0);
gen.visitEnd();
writer.visitEnd();
final TargetLoader loader = new TargetLoader();
return (ITarget) loader.add(className.replace('/', '.'),
writer.toByteArray()).newInstance();
}
/**
* With this interface we modify and read coverage data of the generated
* class.
*/
public interface ITarget {
/**
* Returns a reference to the probe array.
*
* @return the probe array
*/
boolean[] get();
/**
* The implementation will mark probe 0 as executed
*/
void a();
/**
* The implementation will mark probe 1 as executed
*/
void b();
}
}