All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.evosuite.setup.DependencyAnalysis Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 2010-2018 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 .
 */
/**
 * 
 */
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 classCache = new LinkedHashMap();

	private static Map callGraphs = new LinkedHashMap();

	private static InheritanceTree inheritanceTree = null;

	private static Set targetClasses = null;

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

	private static void initInheritanceTree(List classPath) {
		logger.debug("Calculate inheritance hierarchy");
		inheritanceTree = InheritanceTreeGenerator.createFromClassPath(classPath);
		TestClusterGenerator clusterGenerator = new TestClusterGenerator(inheritanceTree);
		TestGenerationContext.getInstance().setTestClusterGenerator(clusterGenerator);
		InheritanceTreeGenerator.gatherStatistics(inheritanceTree);
	}

	private static void analyze(String className, List 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
		TestGenerationContext.getInstance().getTestClusterGenerator().generateCluster(callGraph);

		gatherStatistics();
	}

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

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

	/**
	 * Start analysis from target
	 * 
	 * @param target (e.g., directory, or jar file)
	 */
	public static Set analyzeTarget(String target, List 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 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;
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy