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

org.evosuite.graphs.EvoSuiteGraph Maven / Gradle / Ivy

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

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;

import org.evosuite.utils.LoggingUtils;
import org.jgrapht.DirectedGraph;
import org.jgrapht.alg.DijkstraShortestPath;
import org.jgrapht.ext.DOTExporter;
import org.jgrapht.ext.IntegerNameProvider;
import org.jgrapht.ext.StringEdgeNameProvider;
import org.jgrapht.ext.StringNameProvider;
import org.jgrapht.ext.ComponentAttributeProvider;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.graph.DefaultEdge;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Supposed to become the super class of all kinds of graphs used within
 * EvoSuite Examples are the raw and minimal Control Flow Graph and hopefully at
 * one point the Control Dependency Tree
 *
 * This class is supposed to hide the jGraph library from the rest of EvoSuite
 * and is supposed to serve as an interface for all kinds of primitive graph-
 * functionality such as asking for information about the nodes and edges of the
 * graph and the relations between them.
 *
 * Hopefully at some point only this class and it's sub classes are the only
 * files in EvoSuite that import anything from the jGraph library - at least
 * that's the idea This is very similar to the way cfg.ASMWrapper is supposed to
 * hide the ASM library and serve as an interface for BytecodeInstrucions
 *
 * So most of this class' methods are just wrappers that redirect the specific
 * call to the corresponding jGraph-method
 *
 * For now an EvoSuiteGraph can always be represented by a DefaultDirectedGraph
 * from the jGraph library - that is a directed graph not allowed to contain
 * multiple edges between to nodes but allowed to contain cycles
 *
 * @author Andre Mis
 */
public abstract class EvoSuiteGraph {

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

	private static int evoSuiteGraphs = 0;
	protected int graphId;

	protected DirectedGraph graph;
	protected Class edgeClass;

	// for .dot functionality
	// TODO need jgrapht-0.8.3
	ComponentAttributeProvider vertexAttributeProvider = null;
	ComponentAttributeProvider edgeAttributeProvider = null;

	/**
	 * 

Constructor for EvoSuiteGraph.

* * @param edgeClass a {@link java.lang.Class} object. * @param a V object. * @param a E object. */ protected EvoSuiteGraph(Class edgeClass) { graph = new DefaultDirectedGraph(edgeClass); this.edgeClass = edgeClass; setId(); } /** *

Constructor for EvoSuiteGraph.

* * @param graph a {@link org.jgrapht.DirectedGraph} object. * @param edgeClass a {@link java.lang.Class} object. */ protected EvoSuiteGraph(DirectedGraph graph, Class edgeClass) { if (graph == null || edgeClass == null) throw new IllegalArgumentException("null given"); this.graph = graph; this.edgeClass = edgeClass; setId(); } private void setId() { evoSuiteGraphs++; graphId = evoSuiteGraphs; } // retrieving nodes and edges /** *

getEdgeSource

* * @param e a E object. * @return a V object. */ public V getEdgeSource(E e) { if (!containsEdge(e)) throw new IllegalArgumentException("edge not in graph"); return graph.getEdgeSource(e); } /** *

getEdgeTarget

* * @param e a E object. * @return a V object. */ public V getEdgeTarget(E e) { if (!containsEdge(e)) throw new IllegalArgumentException("edge not in graph"); return graph.getEdgeTarget(e); } /** *

outgoingEdgesOf

* * @param node a V object. * @return a {@link java.util.Set} object. */ public Set outgoingEdgesOf(V node) { if (!containsVertex(node)) // should this just return null? throw new IllegalArgumentException( "node not contained in this graph"); // TODO hash set? can't be sure E implements hash correctly return new LinkedHashSet(graph.outgoingEdgesOf(node)); } /** *

incomingEdgesOf

* * @param node a V object. * @return a {@link java.util.Set} object. */ public Set incomingEdgesOf(V node) { if (!containsVertex(node)) // should this just return null? throw new IllegalArgumentException("node not contained in this graph "); // TODO hash set? can't be sure E implements hash correctly return new LinkedHashSet(graph.incomingEdgesOf(node)); } /** *

getChildren

* * @param node a V object. * @return a {@link java.util.Set} object. */ public Set getChildren(V node) { if (!containsVertex(node)){ LoggingUtils.getEvoLogger().warn("getChildren call requests a node not contained in the current graph. Node: "+node); return null; } //TODO check why in project 57_hft-bomberman class client.gui.StartFrame this happens // throw new IllegalArgumentException( // "node not contained in this graph"); // TODO hash set? can't be sure V implements hash correctly Set r = new LinkedHashSet(); for (E e : outgoingEdgesOf(node)) r.add(getEdgeTarget(e)); // sanity check if (r.size() != outDegreeOf(node)) throw new IllegalStateException( "expect children count and size of set of all children of a graphs node to be equal"); return r; } /** *

getParents

* * @param node a V object. * @return a {@link java.util.Set} object. */ public Set getParents(V node) { if (!containsVertex(node)) // should this just return null? throw new IllegalArgumentException( "node not contained in this graph"); // TODO hash set? can't be sure V implements hash correctly Set r = new LinkedHashSet(); for (E e : incomingEdgesOf(node)) r.add(getEdgeSource(e)); // sanity check if (r.size() != inDegreeOf(node)) throw new IllegalStateException( "expect parent count and size of set of all parents of a graphs node to be equal"); return r; } /** *

vertexSet

* * @return a {@link java.util.Set} object. */ public Set vertexSet() { // TODO hash set? can't be sure V implements hash correctly return new LinkedHashSet(graph.vertexSet()); /* * Set r = new HashSet(); * * for (V v : graph.vertexSet()) r.add(v); * * return r; */ } /** *

edgeSet

* * @return a {@link java.util.Set} object. */ public Set edgeSet() { // TODO hash set? can't be sure E implements hash correctly return new LinkedHashSet(graph.edgeSet()); /* * Set r = new HashSet(); * * for (E e : graph.edgeSet()) r.add(e); * * return r; */ } /** * If the given node is contained within this graph and has exactly one * child v this method will return v. Otherwise it will return null * * @param node a V object. * @return a V object. */ public V getSingleChild(V node) { if (node == null) return null; if (!graph.containsVertex(node)) return null; if (outDegreeOf(node) != 1) return null; for (V r : getChildren(node)) return r; // should be unreachable return null; } // building the graph /** *

addVertices

* * @param other a {@link org.evosuite.graphs.EvoSuiteGraph} object. */ protected void addVertices(EvoSuiteGraph other) { addVertices(other.vertexSet()); } /** *

addVertices

* * @param vs a {@link java.util.Collection} object. */ protected void addVertices(Collection vs) { if (vs == null) throw new IllegalArgumentException("null given"); for (V v : vs) if (!addVertex(v)) throw new IllegalArgumentException( "unable to add all nodes in given collection: " + v.toString()); } /** *

addVertex

* * @param v a V object. * @return a boolean. */ protected boolean addVertex(V v) { return graph.addVertex(v); } /** *

addEdge

* * @param src a V object. * @param target a V object. * @return a E object. */ protected E addEdge(V src, V target) { return graph.addEdge(src, target); } /** *

addEdge

* * @param src a V object. * @param target a V object. * @param e a E object. * @return a boolean. */ protected boolean addEdge(V src, V target, E e) { return graph.addEdge(src, target, e); } /** * Redirects all edges going into node from to the node newStart and all * edges going out of node from to the node newEnd. * * All three edges have to be present in the graph prior to a call to this * method. * * @param from a V object. * @param newStart a V object. * @param newEnd a V object. * @return a boolean. */ protected boolean redirectEdges(V from, V newStart, V newEnd) { if (!(containsVertex(from) && containsVertex(newStart) && containsVertex(newEnd))) throw new IllegalArgumentException( "expect all given nodes to be present in this graph"); if (!redirectIncomingEdges(from, newStart)) return false; if (!redirectOutgoingEdges(from, newEnd)) return false; return true; } /** * Redirects all incoming edges to oldNode to node newNode by calling * redirectEdgeTarget for each incoming edge of oldNode * * @param oldNode a V object. * @param newNode a V object. * @return a boolean. */ protected boolean redirectIncomingEdges(V oldNode, V newNode) { Set incomings = incomingEdgesOf(oldNode); for (E incomingEdge : incomings) { if (!redirectEdgeTarget(incomingEdge, newNode)) return false; } return true; } /** * Redirects all outgoing edges to oldNode to node newNode by calling * redirectEdgeSource for each outgoing edge of oldNode * * @param oldNode a V object. * @param newNode a V object. * @return a boolean. */ protected boolean redirectOutgoingEdges(V oldNode, V newNode) { Set outgoings = outgoingEdgesOf(oldNode); for (E outgoingEdge : outgoings) { if (!redirectEdgeSource(outgoingEdge, newNode)) return false; } return true; } /** * Redirects the edge target of the given edge to the given node by removing * the given edge from the graph and reinserting it from the original source * node to the given node * * @param edge a E object. * @param node a V object. * @return a boolean. */ protected boolean redirectEdgeTarget(E edge, V node) { if (!(containsVertex(node) && containsEdge(edge))) throw new IllegalArgumentException( "edge and node must be present in this graph"); V edgeSource = graph.getEdgeSource(edge); if (!graph.removeEdge(edge)) return false; if (!addEdge(edgeSource, node, edge)) return false; return true; } /** * Redirects the edge source of the given edge to the given node by removing * the given edge from the graph and reinserting it from the given node to * the original target node * * @param edge a E object. * @param node a V object. * @return a boolean. */ protected boolean redirectEdgeSource(E edge, V node) { if (!(containsVertex(node) && containsEdge(edge))) throw new IllegalArgumentException( "edge and node must be present in this graph"); V edgeTarget = graph.getEdgeTarget(edge); if (!graph.removeEdge(edge)) return false; if (!addEdge(node, edgeTarget, edge)) return false; return true; } // different counts /** *

vertexCount

* * @return a int. */ public int vertexCount() { return graph.vertexSet().size(); } /** *

edgeCount

* * @return a int. */ public int edgeCount() { return graph.edgeSet().size(); } /** *

outDegreeOf

* * @param node a V object. * @return a int. */ public int outDegreeOf(V node) { // TODO rename to sth. like childCount() if (node == null || !containsVertex(node)) return -1; return graph.outDegreeOf(node); } /** *

inDegreeOf

* * @param node a V object. * @return a int. */ public int inDegreeOf(V node) { // TODO rename sth. like parentCount() if (node == null || !containsVertex(node)) return -1; return graph.inDegreeOf(node); } // some queries /** *

getEdge

* * @param v1 a V object. * @param v2 a V object. * @return a E object. */ public E getEdge(V v1, V v2) { return graph.getEdge(v1, v2); } /** *

containsVertex

* * @param v a V object. * @return a boolean. */ public boolean containsVertex(V v) { // documentation says containsVertex() returns false on when given null return graph.containsVertex(v); } /** *

containsEdge

* * @param v1 a V object. * @param v2 a V object. * @return a boolean. */ public boolean containsEdge(V v1, V v2) { return graph.containsEdge(v1, v2); } /** *

containsEdge

* * @param e a E object. * @return a boolean. */ public boolean containsEdge(E e) { return graph.containsEdge(e); // TODO this seems to be buggy, at least // for ControlFlowEdges } /** *

isEmpty

* * @return a boolean. */ public boolean isEmpty() { return graph.vertexSet().isEmpty(); } /** * Checks whether each vertex inside this graph is reachable from some other * vertex * * @return a boolean. */ public boolean isConnected() { if (vertexCount() < 2) return true; V start = getRandomVertex(); Set connectedToStart = determineConnectedVertices(start); return connectedToStart.size() == vertexSet().size(); } /** *

determineEntryPoints

* * @return Set containing all nodes with in degree 0 */ public Set determineEntryPoints() { Set r = new LinkedHashSet(); for (V instruction : vertexSet()) if (inDegreeOf(instruction) == 0) { r.add(instruction); } return r; } /** *

determineExitPoints

* * @return Set containing all nodes with out degree 0 */ public Set determineExitPoints() { Set r = new LinkedHashSet(); for (V instruction : vertexSet()) if (outDegreeOf(instruction) == 0) r.add(instruction); return r; } /** * Follows all edges adjacent to the given vertex v ignoring edge directions * and returns a set containing all vertices visited that way * * @param v a V object. * @return a {@link java.util.Set} object. */ public Set determineConnectedVertices(V v) { Set visited = new LinkedHashSet(); Queue queue = new LinkedList(); queue.add(v); while (!queue.isEmpty()) { V current = queue.poll(); if (visited.contains(current)) continue; visited.add(current); queue.addAll(getParents(current)); queue.addAll(getChildren(current)); } return visited; } /** * Returns true iff whether the given node is not null, in this graph and * has exactly n parents and m children. * * @param node a V object. * @param n a int. * @param m a int. * @return a boolean. */ public boolean hasNPartentsMChildren(V node, int n, int m) { if (node == null || !containsVertex(node)) return false; return inDegreeOf(node) == n && outDegreeOf(node) == m; } /** * Returns a Set of all nodes within this graph that neither have incoming * nor outgoing edges. * * @return a {@link java.util.Set} object. */ public Set getIsolatedNodes() { Set r = new LinkedHashSet(); for (V node : graph.vertexSet()) if (inDegreeOf(node) == 0 && outDegreeOf(node) == 0) r.add(node); return r; } /** * Returns a Set containing every node in this graph that has no outgoing * edges. * * @return a {@link java.util.Set} object. */ public Set getNodesWithoutChildren() { Set r = new LinkedHashSet(); for (V node : graph.vertexSet()) if (outDegreeOf(node) == 0) r.add(node); return r; } // utilities /** *

getRandomVertex

* * @return a V object. */ public V getRandomVertex() { // TODO that's not really random for (V v : graph.vertexSet()) return v; return null; } /** *

getDistance

* * @param v1 a V object. * @param v2 a V object. * @return a int. */ public int getDistance(V v1, V v2) { DijkstraShortestPath d = new DijkstraShortestPath(graph, v1, v2); return (int) Math.round(d.getPathLength()); } /** *

isDirectSuccessor

* * @param v1 a V object. * @param v2 a V object. * @return a boolean. */ public boolean isDirectSuccessor(V v1, V v2) { return (containsEdge(v1, v2) && inDegreeOf(v2) == 1); } // TODO make like determineEntry/ExitPoints /** *

determineBranches

* * @return a {@link java.util.Set} object. */ public Set determineBranches() { Set r = new LinkedHashSet(); for (V instruction : graph.vertexSet()) if (outDegreeOf(instruction) > 1) r.add(instruction); return r; } /** *

determineJoins

* * @return a {@link java.util.Set} object. */ public Set determineJoins() { Set r = new LinkedHashSet(); for (V instruction : vertexSet()) if (inDegreeOf(instruction) > 1) r.add(instruction); return r; } // building up the reverse graph /** * Returns a reverted version of this graph in a jGraph * * That is a graph containing exactly the same nodes as this one but for * each edge from v1 to v2 in this graph the resulting graph will contain an * edge from v2 to v1 - or in other words the reverted edge * * This is used to revert CFGs in order to determine control dependencies * for example * * @return a {@link org.jgrapht.graph.DefaultDirectedGraph} object. */ protected DefaultDirectedGraph computeReverseJGraph() { DefaultDirectedGraph r = new DefaultDirectedGraph(edgeClass); for (V v : vertexSet()) if (!r.addVertex(v)) throw new IllegalStateException( "internal error while adding vertices"); for (E e : edgeSet()) { V src = getEdgeSource(e); V target = getEdgeTarget(e); if (r.addEdge(target, src) == null) throw new IllegalStateException( "internal error while adding reverse edges"); } return r; } // visualizing the graph TODO clean up! /** *

toDot

*/ public void toDot() { createGraphDirectory(); String dotFileName = getGraphDirectory() + toFileString(getName()) + ".dot"; toDot(dotFileName); createToPNGScript(dotFileName); } private String getGraphDirectory() { return "evosuite-graphs/" + dotSubFolder(); } /** * Subclasses can overwrite this method in order to separate their .dot and * .png export to a special folder. * * @return a {@link java.lang.String} object. */ protected String dotSubFolder() { return ""; } /** *

toFileString

* * @param name a {@link java.lang.String} object. * @return a {@link java.lang.String} object. */ protected String toFileString(String name) { return name.replaceAll("\\(", "_").replaceAll("\\)", "_") .replaceAll(";", "_").replaceAll("/", "_").replaceAll("<", "_") .replaceAll(">", "_"); } private void createGraphDirectory() { File graphDir = new File(getGraphDirectory()); if (!graphDir.exists() && !graphDir.mkdirs()) throw new IllegalStateException("unable to create directory " + getGraphDirectory()); } private void createToPNGScript(String filename) { File dotFile = new File(filename); // dot -Tpng RawCFG11_exe2_III_I.dot > file.png assert (dotFile.exists() && !dotFile.isDirectory()); try { String[] cmd = { "dot", "-Tpng", "-o" + dotFile.getAbsolutePath() + ".png", dotFile.getAbsolutePath() }; Runtime.getRuntime().exec(cmd); } catch (IOException e) { logger.error("Problem while generating a graph for a dotFile", e); } } /** *

getName

* * @return a {@link java.lang.String} object. */ public String getName() { return "EvoSuiteGraph_" + graphId; } /** *

registerVertexAttributeProvider

* * @param vertexAttributeProvider a {@link org.jgrapht.ext.ComponentAttributeProvider} object. */ public void registerVertexAttributeProvider( ComponentAttributeProvider vertexAttributeProvider) { this.vertexAttributeProvider = vertexAttributeProvider; } /** *

registerEdgeAttributeProvider

* * @param edgeAttributeProvider a {@link org.jgrapht.ext.ComponentAttributeProvider} object. */ public void registerEdgeAttributeProvider( ComponentAttributeProvider edgeAttributeProvider) { this.edgeAttributeProvider = edgeAttributeProvider; } private void toDot(String filename) { // TODO check if graphviz/dot is actually available on the current // machine try { FileWriter fstream = new FileWriter(filename); BufferedWriter out = new BufferedWriter(fstream); if (!graph.vertexSet().isEmpty()) { // FrameVertexNameProvider nameprovider = new // FrameVertexNameProvider(mn.instructions); // DOTExporter exporter = new // DOTExporter(); // DOTExporter exporter = new // DOTExporter(new IntegerNameProvider(), // nameprovider, new IntegerEdgeNameProvider()); // DOTExporter exporter = new // DOTExporter(new LineNumberProvider(), // new LineNumberProvider(), new IntegerEdgeNameProvider()); DOTExporter exporter = new DOTExporter( new IntegerNameProvider(), new StringNameProvider(), new StringEdgeNameProvider(), vertexAttributeProvider, edgeAttributeProvider); // new IntegerEdgeNameProvider()); exporter.export(out, graph); logger.info("exportet " + getName()); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy