org.jacoco.core.runtime.RuntimeTestBase.java Source code

Java tutorial

Introduction

Here is the source code for org.jacoco.core.runtime.RuntimeTestBase.java

Source

/*******************************************************************************
 * Copyright (c) 2009, 2013 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.assertNull;
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(Opcodes.ASM4) {
            @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]);
    }

    @Test
    public void testDisconnect() throws Exception {
        final ITarget target = generateAndInstantiateClass(1001);
        target.a();
        runtime.disconnect(target.getClass());
        assertNull(target.get());
        data.collect(storage, storage, false);
        storage.assertSize(1);
        final boolean[] data = storage.getData(1001).getProbes();
        assertTrue(data[0]);
    }

    @Test
    public void testDisconnectInterface() throws Exception {
        // No effect:
        runtime.disconnect(ITarget.class);
    }

    /**
     * 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(className.replace('/', '.'), writer.toByteArray());
        return (ITarget) loader.newTargetInstance();
    }

    /**
     * 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();

    }

}