org.jacoco.core.internal.analysis.AC_MethodAnalyzerTest.java Source code

Java tutorial

Introduction

Here is the source code for org.jacoco.core.internal.analysis.AC_MethodAnalyzerTest.java

Source

/*******************************************************************************
 * 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.internal.analysis;

import static org.junit.Assert.assertEquals;

import java.io.PrintWriter;
import java.util.ArrayList;

import org.jacoco.core.analysis.ILine;
import org.jacoco.core.analysis.IMethodCoverage;
import org.jacoco.core.internal.flow.IProbeIdGenerator;
import org.jacoco.core.internal.flow.LabelFlowAnalyzer;
import org.jacoco.core.internal.flow.MethodProbesAdapter;
import org.junit.Before;
import org.junit.Test;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
import org.objectweb.asm.util.CheckMethodAdapter;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceMethodVisitor;

/**
 * Unit tests for {@link MethodAnalyzer}.
 */
public class AC_MethodAnalyzerTest implements IProbeIdGenerator {

    private int nextProbeId;

    private boolean[] probes;

    private MethodNode method;

    private IMethodCoverage result;

    @Before
    public void setup() {
        nextProbeId = 0;
        method = new MethodNode();
        method.tryCatchBlocks = new ArrayList<TryCatchBlockNode>();
        probes = new boolean[32];
    }

    public int nextId() {
        return nextProbeId++;
    }

    // === Scenario: linear Sequence without branches ===

    private void createLinearSequence() {
        final Label l0 = new Label();
        method.visitLabel(l0);
        method.visitLineNumber(1001, l0);
        method.visitInsn(Opcodes.NOP);
        final Label l1 = new Label();
        method.visitLabel(l1);
        method.visitLineNumber(1002, l1);
        method.visitInsn(Opcodes.RETURN);
    }

    @Test
    public void testLinearSequenceNotCovered1() {
        createLinearSequence();
        // printMethod(method);
        runMethodAnalzer();
        assertEquals(1, nextProbeId);

        assertLine(1001, 1, 0, 0, 0);
        assertLine(1002, 1, 0, 0, 0);

        assertAcyclicPathCoverage(1, 0);
    }

    @Test
    public void testLinearSequenceNotCovered2() {
        createLinearSequence();
        probes = null;
        runMethodAnalzer();
        assertEquals(1, nextProbeId);

        assertLine(1001, 1, 0, 0, 0);
        assertLine(1002, 1, 0, 0, 0);

        assertAcyclicPathCoverage(1, 0);
    }

    @Test
    public void testLinearSequenceCovered() {
        createLinearSequence();
        probes[0] = true;
        runMethodAnalzer();

        assertLine(1001, 0, 1, 0, 0);
        assertLine(1002, 0, 1, 0, 0);

        assertAcyclicPathCoverage(0, 1);
    }

    // === Scenario: simple if branch ===

    private void createIfBranch() {
        final Label l0 = new Label();
        method.visitLabel(l0);
        method.visitLineNumber(1001, l0);
        method.visitVarInsn(Opcodes.ILOAD, 1);
        Label l1 = new Label();
        method.visitJumpInsn(Opcodes.IFEQ, l1);
        final Label l2 = new Label();
        method.visitLabel(l2);
        method.visitLineNumber(1002, l2);
        method.visitLdcInsn("a");
        method.visitInsn(Opcodes.ARETURN);
        method.visitLabel(l1);
        method.visitLineNumber(1003, l1);
        method.visitLdcInsn("b");
        method.visitInsn(Opcodes.ARETURN);
    }

    @Test
    public void testIfBranchNotCovered() {
        createIfBranch();
        runMethodAnalzer();
        assertEquals(2, nextProbeId);

        assertLine(1001, 2, 0, 2, 0);
        assertLine(1002, 2, 0, 0, 0);
        assertLine(1003, 2, 0, 0, 0);

        assertAcyclicPathCoverage(2, 0);
    }

    @Test
    public void testIfBranchCovered1() {
        createIfBranch();

        probes[0] = true;
        runMethodAnalzer();

        assertLine(1001, 0, 2, 1, 1);
        assertLine(1002, 0, 2, 0, 0);
        assertLine(1003, 2, 0, 0, 0);

        assertAcyclicPathCoverage(1, 1);
    }

    @Test
    public void testIfBranchCovered2() {
        createIfBranch();
        probes[1] = true;
        runMethodAnalzer();

        assertLine(1001, 0, 2, 1, 1);
        assertLine(1002, 2, 0, 0, 0);
        assertLine(1003, 0, 2, 0, 0);

        assertAcyclicPathCoverage(1, 1);
    }

    @Test
    public void testIfBranchCovered3() {
        createIfBranch();
        // simulate two runs
        probes[0] = true;
        probes[1] = true;
        runMethodAnalzer();

        assertLine(1001, 0, 2, 0, 2);
        assertLine(1002, 0, 2, 0, 0);
        assertLine(1003, 0, 2, 0, 0);

        assertAcyclicPathCoverage(0, 2);
    }

    // === Scenario: branch which merges back ===

    private void createIfBranchMerge() {
        final Label l0 = new Label();
        method.visitLabel(l0);
        method.visitLineNumber(1001, l0);
        method.visitVarInsn(Opcodes.ILOAD, 1);
        Label l1 = new Label();
        method.visitJumpInsn(Opcodes.IFEQ, l1);
        final Label l2 = new Label();
        method.visitLabel(l2);
        method.visitLineNumber(1002, l2);
        method.visitInsn(Opcodes.NOP);
        method.visitLabel(l1);
        method.visitLineNumber(1003, l1);
        method.visitInsn(Opcodes.RETURN);
    }

    @Test
    public void testIfBranchMergeNotCovered() {
        createIfBranchMerge();
        // printMethod(method);

        runMethodAnalzer();
        assertEquals(3, nextProbeId);

        assertLine(1001, 2, 0, 2, 0);
        assertLine(1002, 1, 0, 0, 0);
        assertLine(1003, 1, 0, 0, 0);

        // TODO: add assertAcyclicPathCoverage with proper parameters and
        // make it pass.
    }

    @Test
    public void testIfBranchMergeCovered1() {
        createIfBranchMerge();
        probes[0] = true;
        runMethodAnalzer();

        assertLine(1001, 0, 2, 1, 1);
        assertLine(1002, 1, 0, 0, 0);
        assertLine(1003, 1, 0, 0, 0);

        // TODO: add assertAcyclicPathCoverage with proper parameters and
        // make it pass.
    }

    @Test
    public void testIfBranchMergeCovered2() {
        createIfBranchMerge();
        probes[1] = true;
        runMethodAnalzer();

        assertLine(1001, 0, 2, 1, 1);
        assertLine(1002, 0, 1, 0, 0);
        assertLine(1003, 1, 0, 0, 0);

        // TODO: add assertAcyclicPathCoverage with proper parameters and
        // make it pass.
    }

    @Test
    public void testIfBranchMergeCovered3() {
        createIfBranchMerge();
        probes[0] = true;
        probes[1] = true;
        probes[2] = true;
        runMethodAnalzer();

        assertLine(1001, 0, 2, 0, 2);
        assertLine(1002, 0, 1, 0, 0);
        assertLine(1003, 0, 1, 0, 0);

        // TODO: add assertAcyclicPathCoverage with proper parameters and
        // make it pass.
    }

    // === Scenario: branch which jump backwards ===

    private void createJumpBackwards() {
        final Label l0 = new Label();
        method.visitLabel(l0);
        method.visitLineNumber(1001, l0);
        final Label l1 = new Label();
        method.visitJumpInsn(Opcodes.GOTO, l1);
        final Label l2 = new Label();
        method.visitLabel(l2);
        method.visitLineNumber(1002, l2);
        method.visitInsn(Opcodes.RETURN);
        method.visitLabel(l1);
        method.visitLineNumber(1003, l1);
        method.visitJumpInsn(Opcodes.GOTO, l2);
    }

    @Test
    public void testJumpBackwardsNotCovered() {
        createJumpBackwards();
        // printMethod(method);

        runMethodAnalzer();
        assertEquals(1, nextProbeId);

        assertLine(1001, 1, 0, 0, 0);
        assertLine(1002, 1, 0, 0, 0);
        assertLine(1003, 1, 0, 0, 0);

        // TODO: add assertAcyclicPathCoverage with proper parameters and
        // make it pass.
    }

    @Test
    public void testJumpBackwardsCovered() {
        createJumpBackwards();
        probes[0] = true;
        runMethodAnalzer();

        assertLine(1001, 0, 1, 0, 0);
        assertLine(1002, 0, 1, 0, 0);
        assertLine(1003, 0, 1, 0, 0);

        // TODO: add assertAcyclicPathCoverage with proper parameters and
        // make it pass.
    }

    // === Scenario: loops ===
    // similarly to the other scenarios.

    //article: http://blog.jamesdbloom.com/JavaCodeToByteCode_PartOne.html#while_loop
    //docs: http://asm.ow2.org/asm40/javadoc/user/org/objectweb/asm/Opcodes.html
    //docs: http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html
    private void createLoop() {
        final Label l0 = new Label();
        method.visitLabel(l0);
        method.visitLineNumber(1001, l0);
        method.visitInsn(Opcodes.ICONST_0);
        method.visitVarInsn(Opcodes.ISTORE, 1);
        final Label l1 = new Label();
        method.visitLabel(l1);
        method.visitLineNumber(1002, l1);
        method.visitVarInsn(Opcodes.ILOAD, 1);
        method.visitInsn(Opcodes.ICONST_5);
        final Label l2 = new Label();
        method.visitJumpInsn(Opcodes.IF_ICMPGE, l2);
        method.visitIincInsn(1, 1);
        method.visitJumpInsn(Opcodes.GOTO, l1);
        method.visitLabel(l2);
        method.visitLineNumber(1003, l2);
        method.visitInsn(Opcodes.RETURN);
    }

    @Test
    public void testLoopCovered1() {
        createLoop();
        probes[0] = true;
        runMethodAnalzer();

        assertLine(1001, 0, 2, 0, 0);
        assertLine(1002, 5, 0, 2, 0);
        assertLine(1003, 1, 0, 0, 0);
    }

    @Test
    public void testLoopCovered2() {
        createLoop();
        probes[0] = true;
        probes[1] = true;
        runMethodAnalzer();

        assertLine(1001, 0, 2, 0, 0);
        assertLine(1002, 0, 5, 1, 1);
        assertLine(1003, 1, 0, 0, 0);
    }

    @Test
    public void testLoopCovered3() {
        createLoop();
        probes[0] = true;
        probes[1] = true;
        probes[2] = true;
        runMethodAnalzer();

        assertLine(1001, 0, 2, 0, 0);
        assertLine(1002, 0, 5, 0, 2);
        assertLine(1003, 0, 1, 0, 0);
    }

    @Test
    public void testLoopNotCovered() {
        createLoop();
        runMethodAnalzer();

        assertLine(1001, 2, 0, 0, 0);
        assertLine(1002, 5, 0, 2, 0);
        assertLine(1003, 1, 0, 0, 0);
    }

    private void runMethodAnalzer() {
        LabelFlowAnalyzer.markLabels(method);
        final MethodAnalyzer analyzer = new MethodAnalyzer("doit", "()V", null, probes);
        final MethodProbesAdapter probesAdapter = new MethodProbesAdapter(analyzer, this);
        // note that CheckMethodAdapter verifies that this test does not violate
        // contracts of ASM API
        method.accept(new CheckMethodAdapter(probesAdapter));
        result = analyzer.getCoverage();
    }

    // NOTE: You can add more parameters if needed
    private void assertAcyclicPathCoverage(int pathMissed, int pathCovered) {
        // TODO: implement
    }

    private void assertLine(int nr, int insnMissed, int insnCovered, int branchesMissed, int branchesCovered) {
        final ILine line = result.getLine(nr);
        assertEquals("Instructions in line " + nr, CounterImpl.getInstance(insnMissed, insnCovered),
                line.getInstructionCounter());
        assertEquals("Branches in line " + nr, CounterImpl.getInstance(branchesMissed, branchesCovered),
                line.getBranchCounter());
    }

    @SuppressWarnings("unused")
    private void printMethod(MethodNode m) {
        Textifier t = new Textifier();
        m.accept(new TraceMethodVisitor(t));

        PrintWriter writer = new PrintWriter(System.out);
        t.print(writer);
        writer.flush();
    }

    @SuppressWarnings("unused")
    private String branchExample(int i) {
        if (i != 0) {
            return "a";
        } else {
            return "b";
        }
    }
}