org.evosuite.setup.callgraph.CallGraph 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.callgraph;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.evosuite.Properties;
import org.evosuite.classpath.ResourceList;
import org.evosuite.setup.Call;
import org.evosuite.setup.CallContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* CallGraph implementation. Based on the previous implementation in the
* CallTree class. This class is a wrapper of the Graph class. I didn't use the
* jgrapht graph classes on purpose because I had problems with the DFS
* algorithms implemented for them. On the bright hand, this implementation
* should be more efficient.
*
* @author mattia
*
*/
public class CallGraph implements Iterable {
/**
* The CallGraphImpl class is a wrap of the Graph class. Internally the
* graph is represented reversed, i.e. if a method m1 points to a method m2,
* the graph connects the methods with an edge from m2 to m1. The methods in
* this class however mask this representation.
*/
private ReverseCallGraph graph = new ReverseCallGraph();
@SuppressWarnings("unused")
private static final Logger logger = LoggerFactory
.getLogger(CallGraph.class);
private final String className;
private final Set cutNodes = Collections.synchronizedSet(new LinkedHashSet());
private final Set callGraphClasses = Collections.synchronizedSet(new LinkedHashSet());
private final Set toTestClasses = Collections.synchronizedSet(new LinkedHashSet());
private final Set toTestMethods = Collections.synchronizedSet(new LinkedHashSet());
private final Set notToTestClasses = Collections.synchronizedSet(new LinkedHashSet());
private final Set publicMethods = Collections.synchronizedSet(new LinkedHashSet());
public CallGraph(String className) {
this.className = className;
}
public ReverseCallGraph getGraph() {
return graph;
}
public void removeClasses(Collection vertexes){
for (CallGraphEntry vertex : vertexes) {
graph.removeVertex(vertex);
}
}
public void removeClass(CallGraphEntry vertex){
graph.removeVertex(vertex);
}
/**
* add public methods
* @param className
* @param methodName
*/
public void addPublicMethod(String className, String methodName) {
publicMethods.add(new CallContext(ResourceList
.getClassNameFromResourcePath(className), methodName));
}
/**
* add call to the call graph
*
* @param sourceClass
* @param sourceMethod
* @param targetClass
* @param targetMethod
*/
public boolean addCall(String sourceClass, String sourceMethod,
String targetClass, String targetMethod) {
CallGraphEntry from = new CallGraphEntry(targetClass, targetMethod);
CallGraphEntry to = new CallGraphEntry(sourceClass, sourceMethod);
// logger.info("Adding new call from: " + to + " -> " + from);
if (sourceClass.equals(className))
cutNodes.add(to);
if (!graph.containsEdge(from, to)) {
graph.addEdge(from, to);
callGraphClasses.add(targetClass.replaceAll("/", "."));
return true;
}
return false;
}
/**
* @return true if the method is in the callgraph.
*/
public boolean hasMethod(String classname, String methodName) {
return graph.containsVertex(new CallGraphEntry(classname, methodName));
}
/**
* @return true if the call is in the callgraph.
*/
public boolean hasCall(String owner, String methodName, String targetClass,
String targetMethod) {
CallGraphEntry from = new CallGraphEntry(targetClass, targetMethod);
CallGraphEntry to = new CallGraphEntry(owner, methodName);
return graph.getEdges().containsKey(to)
&& graph.getEdges().get(to).contains(from);
}
/**
* @return calls exiting from the method, empty set if the call is not in
* the graph.
*/
public Set getCallsFrom(String owner, String methodName) {
CallGraphEntry call = new CallGraphEntry(owner, methodName);
return getCallsFromMethod(call);
}
/**
* @return calls exiting from the method, empty set if the call is not in
* the graph
*/
public Set getCallsFromMethod(CallGraphEntry call) {
if (graph.getEdges().containsKey(call))
return graph.getEdges().get(call);
else {
return new HashSet<>();
}
}
/**
* computes and returns the call contexts of the specific method
*
* @param className
* @param methodName
* @return
*/
public Set getMethodEntryPoint(String className, String methodName) {
Set contexts = new HashSet<>();
List cont = new ArrayList<>();
cont.add(new Call(className, methodName));
CallContext context = new CallContext(cont);
if(publicMethods.contains(context)){
contexts.add(context);
}else{
contexts.add(new CallContext());
}
return contexts;
}
/**
* computes and returns the call contexts that starts from the target class
* and end in the specific method
*
* @param className
* @param methodName
* @return
*/
public Set getAllContextsFromTargetClass(String className, String methodName) {
CallGraphEntry root = new CallGraphEntry(className, methodName);
Set> paths = PathFinder.getPahts(graph, root);
Set contexts = convertIntoCallContext(paths);
if(!Properties.EXCLUDE_IBRANCHES_CUT)
addPublicClassMethod(className, methodName, contexts);
return contexts;
}
private void addPublicClassMethod(String className, String methodName, Set contexts){
List calls = new ArrayList<>();
Call call = new Call(className, methodName);
calls.add(call);
CallContext context = new CallContext(calls);
if(publicMethods.contains(context)&&className.equals(this.className))
contexts.add(context);
}
private Set convertIntoCallContext(
Set> paths) {
Set contexts = new HashSet<>();
// return only context that starts from the class under test
for (List list : paths) {
boolean insert = false;
List cont = new ArrayList<>();
for (int i = list.size() - 1; i >= 0; i--) {
if (!insert && list.get(i).getClassName().equals(className)) {
insert = true;
}
if (insert)
cont.add(new Call(list.get(i).getClassName(), list.get(i)
.getMethodName()));
}
contexts.add(new CallContext(cont));
}
return contexts;
}
/**
* @return the className
*/
public String getClassName() {
return className;
}
/**
*
* @return classes reachable from the class under test
*/
public Set getClassesUnderTest() {
if(toTestClasses.isEmpty())
computeInterestingClasses(graph);
return toTestClasses;
}
/**
* Determine if className can be reached from the class under test
*
*
* @param className
* @return
*/
public boolean isCalledClass(String className) {
if(toTestClasses.isEmpty())
computeInterestingClasses(graph);
if(toTestClasses.contains(className)) return true;
return false;
}
public boolean isCalledClassOld(String className) {
if(toTestClasses.contains(className)) return true;
if(notToTestClasses.contains(className)) return false;
for (CallGraphEntry e : graph.getEdges().keySet()) {
if (e.getClassName().equals(className)) {
if(checkClassInPaths(this.className, graph, e))
return true;
}
}
return false;
}
private boolean computeInterestingClasses(Graph g) {
Set startingVertices = new HashSet<>();
for (CallGraphEntry e : graph.getVertexSet()) {
if (e.getClassName().equals(className)) {
startingVertices.add(e);
}
}
Set classes = new HashSet<>();
Set methodclasses = new HashSet<>();
for (CallGraphEntry startingVertex : startingVertices) {
PathFinderDFSIterator dfs = new PathFinderDFSIterator(
g, startingVertex, true);
while (dfs.hasNext()) {
CallGraphEntry e = dfs.next();
classes.add(e.getClassName());
methodclasses.add(e.getClassName()+e.getMethodName());
}
}
toTestMethods.addAll(methodclasses);
toTestClasses.addAll(classes);
return true;
}
private boolean checkClassInPaths(String targetClass, Graph g, CallGraphEntry startingVertex) {
if(!g.containsVertex(startingVertex)){
return false;
}
Set classes = new HashSet<>();
PathFinderDFSIterator dfs = new PathFinderDFSIterator(g, startingVertex);
while (dfs.hasNext()) {
CallGraphEntry e = dfs.next();
classes.add(e.getClassName());
if(e.getClassName().equals(targetClass)){
toTestClasses.addAll(classes);
return true;
}
}
notToTestClasses.addAll(classes);
return false;
}
/**
* Determine if methodName of className can be called through the target
* class
*
* @param className
* @param methodName
* @return
*/
public boolean isCalledMethod(String className, String methodName) {
if (toTestMethods.isEmpty())
computeInterestingClasses(graph);
if (toTestMethods.contains(className + methodName)) {
return true;
}
return false;
}
public boolean isCalledMethodOld(String className, String methodName) {
CallGraphEntry tmp = new CallGraphEntry(className, methodName);
for (CallGraphEntry e : graph.getEdges().keySet()) {
if (e.equals(tmp)) {
for (List c : PathFinder.getPahts(graph, e)) {
for (CallGraphEntry entry : c) {
if (entry.getClassName().equals(this.className))
return true;
}
}
}
}
return false;
}
/*
* (non-Javadoc)
*
* @see java.lang.Iterable#iterator()
*/
@Override
public Iterator iterator() {
return graph.getVertexSet().iterator();
}
/**
* @return a copy of the current vertexset
*/
public Set getViewOfCurrentMethods() {
return new LinkedHashSet(graph.getVertexSet());
}
/**
* @return set of class names.
*/
public Set getClasses() {
return callGraphClasses;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy