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

org.evosuite.continuous.project.ProjectGraph Maven / Gradle / Ivy

There is a newer version: 1.0.6
Show newest version
/**
 * Copyright (C) 2010-2017 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.continuous.project;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.evosuite.continuous.project.ProjectStaticData.ClassInfo;
import org.evosuite.seeding.CastClassAnalyzer;
import org.evosuite.setup.DependencyAnalysis;
import org.evosuite.setup.InheritanceTree;
import org.evosuite.setup.InheritanceTreeGenerator;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 

* Class representing the class graph. For each class (CUT and non-testable), * not only we want to know its hierarchy (parents, interfaces, subclasses, etc) * but also where in the project it is used by other classes as input. *

* *

* For definition of CUT, see {@link ProjectStaticData} *

*/ public class ProjectGraph { /* * Need to guarantee to build the graph once with all needed info, and then the public * methods just query the map data structures directly (instead of recalculating * on the fly) */ private static final Logger logger = LoggerFactory.getLogger(ProjectGraph.class); private final InheritanceTree inheritanceTree; /** * FIXME * Map from TODO (key) to TODO (value) */ private final Map> castInformation; private final ProjectStaticData data; /** * Main constructor * * @param data */ public ProjectGraph(ProjectStaticData data) { this.data = data; inheritanceTree = InheritanceTreeGenerator.createFromClassList(data.getClassNames()); castInformation = new HashMap>(); if(logger.isDebugEnabled()){ logger.debug("Classes in inheritance tree: " + inheritanceTree.getAllClasses()); } } /** *

* Return the full qualifying names of all classes that are CUTs and that * are used as input in any of the public methods of cut (but * not of any of its parent hierarchy). *

* *

* If a method takes as input a reference of a non-SUT class (e.g., * java.lang.Object), but then there is a cast to a CUT (e.g. a * class X), then X will be added in the returned set. *

* *

* If a method takes as input a reference of a SUT class X that is not a CUT * (e.g., an interface with no code), then X will not be added in the * returned set, although based on includeSubclasses we might * add its subclasses. *

* * @param cut * the class under test (CUT) * @param includeSubclasses * If a class X is in the returned set, then normally no subclass * Y of X would be added in the returned set, unless Y is * directly used in the CUT as input. * @return a set of full qualifying names of CUTs * @throws IllegalArgumentException * if the input cut is not a CUT */ public Set getCUTsDirectlyUsedAsInput(String cut, boolean includeSubclasses) throws IllegalArgumentException { checkCUT(cut); Set parameterClasses = recursionToSearchDirectInputs(cut,includeSubclasses); parameterClasses.remove(cut); removeNonCUT(parameterClasses); logger.debug("Parameter classes of " + cut + ": " + parameterClasses); return parameterClasses; } protected Set recursionToSearchDirectInputs(String aClass, boolean includeSubclasses){ if (includeSubclasses) { Set directlyUsed = recursionToSearchDirectInputs(aClass, false); //recursion Set all = new LinkedHashSet(directlyUsed); for (String name : directlyUsed) { all.addAll(getAllCUTsSubclasses(name)); } return all; } Set parameterClasses = getParameterClasses(aClass); parameterClasses.addAll(getCastClasses(aClass)); return parameterClasses; } /** * Calculate all the CUTs that use the given cut as input in * any of their public methods * * @param cut * the class under test (CUT) * @param includeSuperClasses * not only using as input the * cut, but also any of its CUT ancestors/interfaces * @return a set of full qualifying names of CUTs * @throws IllegalArgumentException * if the input cut is not a CUT */ public Set getCUTsThatUseThisCUTasInput(String cut, boolean includeSuperClasses) throws IllegalArgumentException { checkCUT(cut); Set classNames = recursionToSearchWhatUsesItAsInput(cut,includeSuperClasses); classNames.remove(cut); removeNonCUT(classNames); return classNames; } protected Set recursionToSearchWhatUsesItAsInput(String aClass, boolean includeSuperClasses){ if (includeSuperClasses) { Set directlyUsed = recursionToSearchWhatUsesItAsInput(aClass, false); //recursion Set all = new LinkedHashSet(directlyUsed); for (String name : getAllCUTsParents(aClass)) { all.addAll(recursionToSearchWhatUsesItAsInput(name, false)); //recursion } return all; } Set classNames = new LinkedHashSet(); for (String className : inheritanceTree.getAllClasses()) { if (className.equals(aClass)){ continue; } Set inputClasses = getCUTsDirectlyUsedAsInput(className, true); if (inputClasses.contains(aClass)){ classNames.add(className); } } return classNames; } /** * Is the given class name representing an interface in the SUT? * @param className * @return * @throws IllegalArgumentException */ public boolean isInterface(String className) throws IllegalArgumentException { checkClass(className); ClassNode node = getClassNode(className); if ((node.access & Opcodes.ACC_INTERFACE) == Opcodes.ACC_INTERFACE) return true; return false; } /** * Is the given class name representing a concrete class in the SUT? * @param className * @return * @throws IllegalArgumentException */ public boolean isConcrete(String className) throws IllegalArgumentException { checkClass(className); return !isInterface(className) && !isAbstract(className); } /** * Is the given class name representing an abstract class in the SUT? * @param className * @return * @throws IllegalArgumentException */ public boolean isAbstract(String className) throws IllegalArgumentException { checkClass(className); ClassNode node = getClassNode(className); return (node.access & Opcodes.ACC_ABSTRACT) == Opcodes.ACC_ABSTRACT; } /** * Check if the given class does belong to the SUT. * This is independent on whether it is a CUT (ie testable) or not. * * @param className * @throws IllegalArgumentException */ private void checkClass(String className) throws IllegalArgumentException{ if(!data.containsClass(className)){ throw new IllegalArgumentException("Class "+className+" is not part of the SUT"); } } /** * Check if the given class does belong to the SUT and if it is testable (ie a CUT). * * @param cut */ private void checkCUT(String cut) throws IllegalArgumentException{ ClassInfo info = data.getClassInfo(cut); if(info==null){ throw new IllegalArgumentException("Class "+cut+" is not part of the SUT"); } if(!info.isTestable()){ throw new IllegalArgumentException("Class "+cut+" belongs to the SUT, but it is not a CUT (ie testable)"); } } /** * Return all the child hierarchy of the aClass. Include only classes * that are CUTs. * This aClass will not be part of the returned set, even if it is a CUT * * @param aClass * a class belonging to the SUT, but not necessarily a CUT * @return a set of full qualifying names of CUTs * @throws IllegalArgumentException * if the input aClass does not belong to the SUT */ public Set getAllCUTsSubclasses(String aClass) throws IllegalArgumentException { checkClass(aClass); Set set = null; try{ //FIXME set = inheritanceTree.getSubclasses(aClass); } catch(Exception e){ logger.error("Bug in inheritanceTree: "+e); return new HashSet(); } set.remove(aClass); removeNonCUT(set); return set; } /** * Return all the CUT classes that this aClass extends/implements * (ie, parent hierarchy). * This aClass will not be part of the returned set, even if it is a CUT * * @param aClass * a class belonging to the SUT, but not necessarily a CUT * @return a set of full qualifying names of CUTs * @throws IllegalArgumentException * if the input aClass does not belong to the SUT */ public Set getAllCUTsParents(String aClass) throws IllegalArgumentException { checkClass(aClass); Set set = null; try{ //FIXME set = inheritanceTree.getSuperclasses(aClass); } catch(Exception e){ logger.error("Bug in inheritanceTree: "+e); return new HashSet(); } set.remove(aClass); //it seems inheritanceTree returns 'cut' in the set removeNonCUT(set); return set; } private void removeNonCUT(Set set) throws IllegalArgumentException{ Iterator iter = set.iterator(); while(iter.hasNext()){ String name = iter.next(); ClassInfo info = data.getClassInfo(name); if(info==null){ throw new IllegalArgumentException("Class "+name+" does not belong to the SUT"); } if(!info.isTestable()){ iter.remove(); } } } /** * For now use the cache provided by dependency analysis * * @param className * @return */ private ClassNode getClassNode(String className) { return DependencyAnalysis.getClassNode(className); } /** * Determine the set of classes that are used in casts in a CUT * * @param className * @return */ private Set getCastClasses(String className) { if (!castInformation.containsKey(className)) { CastClassAnalyzer analyzer = new CastClassAnalyzer(); // The analyzer gets the classnode from the DependencyAnalysis classnode cache Map castMap = analyzer.analyze(className); Set castClasses = new LinkedHashSet(); for (Type type : castMap.keySet()) { String name = type.getClassName(); if (inheritanceTree.hasClass(name)) { castClasses.add(name); } } castInformation.put(className, castClasses); } return castInformation.get(className); } @SuppressWarnings("unchecked") private Set getParameterClasses(String cut) { Set parameters = new LinkedHashSet(); ClassNode node = getClassNode(cut); List methods = node.methods; for (MethodNode methodNode : methods) { addParameterClasses(methodNode, parameters); } List fields = node.fields; for (FieldNode fieldNode : fields) { addParameterClasses(fieldNode, parameters); } return parameters; } private void addParameterClasses(MethodNode methodNode, Set classNames) { // TODO: Only including public methods for now. Should this be refined to match // TestClusterGenerator.canUse? if ((methodNode.access & Opcodes.ACC_PUBLIC) != Opcodes.ACC_PUBLIC) return; for (Type parameterType : Type.getArgumentTypes(methodNode.desc)) { String name = parameterType.getClassName(); if (inheritanceTree.hasClass(name)) { classNames.add(name); } } } private void addParameterClasses(FieldNode fieldNode, Set classNames) { // TODO: Only including public fields for now. Should this be refined to match // TestClusterGenerator.canUse? if ((fieldNode.access & Opcodes.ACC_PUBLIC) != Opcodes.ACC_PUBLIC) return; String name = Type.getType(fieldNode.desc).getClassName(); if (inheritanceTree.hasClass(name)) { classNames.add(name); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy