org.evosuite.setup.DependencyAnalysis.java Source code

Java tutorial

Introduction

Here is the source code for org.evosuite.setup.DependencyAnalysis.java

Source

/**
 * Copyright (C) 2010-2016 Gordon Fraser, Andrea Arcuri and EvoSuite
 * contributors
 *
 * This file is part of EvoSuite.
 *
 * EvoSuite is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3.0 of the License, or
 * (at your option) any later version.
 *
 * EvoSuite is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with EvoSuite. If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * 
 */
package org.evosuite.setup;

import org.evosuite.PackageInfo;
import org.evosuite.Properties;
import org.evosuite.Properties.Criterion;
import org.evosuite.TestGenerationContext;
import org.evosuite.classpath.ResourceList;
import org.evosuite.coverage.branch.BranchPool;
import org.evosuite.coverage.dataflow.DefUsePool;
import org.evosuite.coverage.mutation.MutationPool;
import org.evosuite.graphs.cfg.CFGMethodAdapter;
import org.evosuite.instrumentation.LinePool;
import org.evosuite.junit.CoverageAnalysis;
import org.evosuite.rmi.ClientServices;
import org.evosuite.setup.callgraph.CallGraph;
import org.evosuite.setup.callgraph.CallGraphGenerator;
import org.evosuite.statistics.RuntimeVariable;
import org.evosuite.utils.ArrayUtil;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * This class performs static analysis before everything else initializes
 * 
 * @author Gordon Fraser
 * 
 */
public class DependencyAnalysis {

    private static Logger logger = LoggerFactory.getLogger(DependencyAnalysis.class);

    private static Map<String, ClassNode> classCache = new LinkedHashMap<String, ClassNode>();

    private static Map<String, CallGraph> callGraphs = new LinkedHashMap<String, CallGraph>();

    private static InheritanceTree inheritanceTree = null;

    private static Set<String> targetClasses = null;

    /**
     * @return the inheritanceTree
     */
    public static InheritanceTree getInheritanceTree() {
        return inheritanceTree;
    }

    private static void initInheritanceTree(List<String> classPath) {
        logger.debug("Calculate inheritance hierarchy");
        inheritanceTree = InheritanceTreeGenerator.createFromClassPath(classPath);
        InheritanceTreeGenerator.gatherStatistics(inheritanceTree);
    }

    private static void analyze(String className, List<String> classPath)
            throws RuntimeException, ClassNotFoundException {

        if (!inheritanceTree.hasClass(Properties.TARGET_CLASS)) {
            throw new ClassNotFoundException("Target class not found in inheritance tree");
        }

        logger.debug("Calculate call tree");
        CallGraph callGraph = CallGraphGenerator.analyze(className);
        callGraphs.put(className, callGraph);
        loadCallTreeClasses(callGraph);

        // include all the project classes in the inheritance tree and in the callgraph.
        if (ArrayUtil.contains(Properties.CRITERION, Criterion.IBRANCH) || Properties.INSTRUMENT_CONTEXT) {

            for (String classn : inheritanceTree.getAllClasses()) {
                if (isTargetProject(classn)) {
                    CallGraphGenerator.analyzeOtherClasses(callGraph, classn);
                }
            }
        }

        // TODO: Need to make sure that all classes in calltree are instrumented

        logger.debug("Update call tree with calls to overridden methods");
        CallGraphGenerator.update(callGraph, inheritanceTree);

        logger.debug("Create test cluster");

        // if a class is not instrumented but part of the callgraph, the
        // generateCluster method will instrument it
        // update: we instrument only classes reachable from the class
        // under test, the callgraph is populated with all classes, but only the
        // set of relevant ones are instrumented - mattia
        TestClusterGenerator clusterGenerator = new TestClusterGenerator(inheritanceTree);
        clusterGenerator.generateCluster(callGraph);

        gatherStatistics();
    }

    /**
     * Start analysis from target class
     * 
     * @param className
     */
    public static void analyzeClass(String className, List<String> classPath)
            throws RuntimeException, ClassNotFoundException {

        initInheritanceTree(classPath);
        analyze(className, classPath);
    }

    /**
     * Start analysis from target
     * 
     * @param target (e.g., directory, or jar file)
     */
    public static Set<String> analyzeTarget(String target, List<String> classPath)
            throws RuntimeException, ClassNotFoundException {

        initInheritanceTree(classPath);

        targetClasses = ResourceList.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT())
                .getAllClasses(target, false);
        for (String className : targetClasses) {
            Properties.TARGET_CLASS = className;
            analyze(className, classPath);
        }

        return targetClasses;
    }

    private static void loadCallTreeClasses(CallGraph callGraph) {
        for (String className : callGraph.getClasses()) {
            if (className.startsWith(Properties.TARGET_CLASS + "$")) {
                try {
                    Class.forName(className, true, TestGenerationContext.getInstance().getClassLoaderForSUT());
                } catch (ClassNotFoundException e) {
                    logger.debug("Error loading " + className + ": " + e);
                }
            }
        }
    }

    /**
     * 
     * @param className
     * @return the CallGraph of className
     */
    public static CallGraph getCallGraph(String className) {
        return callGraphs.get(className);
    }

    /**
     * 
     * @return the CallGraph of Properties.TARGET_CLASS
     */
    public static CallGraph getCallGraph() {
        return callGraphs.get(Properties.TARGET_CLASS);
    }

    /**
     * Determine if the given class is the target class
     * 
     * @param className
     * @return
     */
    public static boolean isTargetClassName(String className) {
        if (!Properties.TARGET_CLASS_PREFIX.isEmpty() && className.startsWith(Properties.TARGET_CLASS_PREFIX)) {
            // exclude existing tests from the target project
            try {
                Class<?> clazz = Class.forName(className);
                return !CoverageAnalysis.isTest(clazz);
            } catch (ClassNotFoundException e) {
                logger.info("Could not find class " + className);
            }
        }
        if (className.equals(Properties.TARGET_CLASS) || className.startsWith(Properties.TARGET_CLASS + "$")) {
            return true;
        }
        if (targetClasses != null && targetClasses.contains(className)) {
            return true;
        }
        return false;
    }

    // TODO implement something that takes parameters using properties -
    // generalize this method.
    public static boolean isTargetProject(String className) {
        return (className.startsWith(Properties.PROJECT_PREFIX) || (!Properties.TARGET_CLASS_PREFIX.isEmpty()
                && className.startsWith(Properties.TARGET_CLASS_PREFIX))) && !className.startsWith("java.")
                && !className.startsWith("sun.") && !className.startsWith(PackageInfo.getEvoSuitePackage())
                && !className.startsWith("org.exsyst") && !className.startsWith("de.unisb.cs.st.evosuite")
                && !className.startsWith("de.unisb.cs.st.specmate") && !className.startsWith("javax.")
                && !className.startsWith("org.xml") && !className.startsWith("org.w3c")
                && !className.startsWith("apple.") && !className.startsWith("com.apple.")
                && !className.startsWith("org.omg.") && !className.startsWith("sunw.")
                && !className.startsWith("org.jcp.") && !className.startsWith("org.ietf.")
                && !className.startsWith("daikon.");
    }

    //   private static String getProjectPackageApprox(String qualifiedName) {
    //      if (qualifiedName == null)
    //         throw new IllegalArgumentException();
    //      String[] splitted = qualifiedName.split("\\.");
    //      String result = "";
    //      if (splitted.length == 0)
    //         result = qualifiedName;
    //      else if (splitted.length == 1)
    //         result = splitted[0];
    //      else if (splitted.length == 2)
    //         result = splitted[0];
    //      else if (splitted[0].equals("com") || splitted[0].equals("org")
    //            || splitted[0].equals("net") || splitted[0].equals("de")
    //            || splitted[0].equals("it") || splitted[0].equals("ch") || splitted[0].equals("fr")
    //            || splitted[0].equals("br") || splitted[0].equals("edu")
    //            || splitted[0].equals("osa") || splitted[0].equals("uk")
    //            || splitted[0].equals("gov") || splitted[0].equals("dk")) {
    //         result = splitted[0] + "." + splitted[1];
    //      } else
    //         result = splitted[0];
    //
    //      return result;
    //   }

    /**
     * Determine if the given class should be analyzed or instrumented
     * 
     * @param className
     * @return
     */
    public static boolean shouldAnalyze(String className) {
        // Always analyze if it is a target class
        if (isTargetClassName(className))
            return true;

        if (inheritanceTree == null) {
            return false;
        }
        // Also analyze if it is a superclass and instrument_parent = true
        if (Properties.INSTRUMENT_PARENT) {
            if (inheritanceTree.getSuperclasses(Properties.TARGET_CLASS).contains(className))
                return true;
        }

        // Also analyze if it is in the calltree and we are considering the
        // context
        if (Properties.INSTRUMENT_CONTEXT || ArrayUtil.contains(Properties.CRITERION, Criterion.DEFUSE)) {
            CallGraph callGraph = callGraphs.get(Properties.TARGET_CLASS);
            if (callGraph != null && callGraph.isCalledClass(className)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Determine if the given method should be instrumented
     * 
     * @param className
     * @param methodName
     * @return
     */
    public static boolean shouldInstrument(String className, String methodName) {
        // Always analyze if it is a target class
        if (isTargetClassName(className))
            return true;

        // Also analyze if it is a superclass and instrument_parent = true
        if (Properties.INSTRUMENT_PARENT) {
            if (inheritanceTree.getSuperclasses(Properties.TARGET_CLASS).contains(className))
                return true;
        }

        // Also analyze if it is in the calltree and we are considering the
        // context
        if (Properties.INSTRUMENT_CONTEXT) {

            CallGraph callGraph = callGraphs.get(Properties.TARGET_CLASS);
            if (callGraph != null && callGraph.isCalledMethod(className, methodName)) {
                if (Properties.INSTRUMENT_LIBRARIES || DependencyAnalysis.isTargetProject(className))
                    return true;
            }
        }

        return false;
    }

    public static ClassNode getClassNode(String className) {
        if (!classCache.containsKey(className)) {
            try {
                classCache.put(className, loadClassNode(className));
            } catch (IOException e) {
                classCache.put(className, null);
            }
        }

        return classCache.get(className);

    }

    public static Collection<ClassNode> getAllClassNodes() {
        return classCache.values();
    }

    private static ClassNode loadClassNode(String className) throws IOException {

        InputStream classStream = ResourceList
                .getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT())
                .getClassAsStream(className);
        if (classStream == null) {
            // This used to throw an IOException that leads to null being
            // returned, so for now we're just returning null directly
            // TODO: Proper treatment of missing classes (can also be
            //       invalid calls, e.g. [L/java/lang/Object;)
            logger.info("Could not find class file: " + className);
            return null;
        }
        ClassNode cn = new ClassNode();
        try {
            ClassReader reader = new ClassReader(classStream);
            reader.accept(cn, ClassReader.SKIP_FRAMES); // |
            // ClassReader.SKIP_DEBUG);
        } finally {
            classStream.close(); // ASM does not close the stream
        }
        return cn;
    }

    private static void gatherStatistics() {
        ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Predicates, BranchPool
                .getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT()).getBranchCounter());
        ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Instrumented_Predicates,
                BranchPool.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT())
                        .getNumArtificialBranches());
        int numBranches = BranchPool.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT())
                .getBranchCounter() * 2;
        ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Total_Branches,
                numBranches);
        ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Total_Branches_Real,
                ((BranchPool.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT())
                        .getBranchCounter()
                        - BranchPool.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT())
                                .getNumArtificialBranches()))
                        / 2);
        ClientServices.getInstance().getClientNode().trackOutputVariable(
                RuntimeVariable.Total_Branches_Instrumented,
                (BranchPool.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT())
                        .getNumArtificialBranches()));
        ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Branchless_Methods,
                BranchPool.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT())
                        .getBranchlessMethods().size());
        ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Total_Methods,
                CFGMethodAdapter.getNumMethods(TestGenerationContext.getInstance().getClassLoaderForSUT()));

        ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Lines,
                LinePool.getNumLines());

        for (Properties.Criterion pc : Properties.CRITERION) {
            switch (pc) {
            case DEFUSE:
            case ALLDEFS:
                ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Definitions,
                        DefUsePool.getDefCounter());
                ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Uses,
                        DefUsePool.getUseCounter());
                break;

            case WEAKMUTATION:
            case STRONGMUTATION:
            case MUTATION:
                ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.Mutants,
                        MutationPool.getMutantCounter());
                break;

            default:
                break;
            }
        }
    }
}