diff --git a/src/gr/spinellis/umlgraph/test/BasicTest.java b/src/gr/spinellis/umlgraph/test/BasicTest.java new file mode 100644 index 0000000..9d9fec1 --- /dev/null +++ b/src/gr/spinellis/umlgraph/test/BasicTest.java @@ -0,0 +1,95 @@ +/* + * UmlGraph class diagram testing framework + * + * Contibuted by Andrea Aime + * (C) Copyright 2005 Diomidis Spinellis + * + * Permission to use, copy, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + * + */ + +package gr.spinellis.umlgraph.test; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; + +public class BasicTest { + + static String testSourceFolder = "./test-data/simple/java"; + + static String testDestFolder = "./test-data/simple/dot-out"; + + static String testRefFolder = "./test-data/simple/dot-ref"; + + static PrintWriter pw = new PrintWriter(System.out); + + public static void main(String[] args) throws IOException { + String[] javaFiles = new File(testSourceFolder).list(new JavaFilter()); +// String[] javaFiles = new String[] {"MyVector.java"}; + + File outFolder = new File(testDestFolder); + if (!outFolder.exists()) + outFolder.mkdirs(); + + boolean equal = true; + for (int i = 0; i < javaFiles.length; i++) { + String javaFileName = javaFiles[i].substring(0, javaFiles[i].length() - 5); + String dotPath = new File(testDestFolder, javaFileName + ".dot").getAbsolutePath(); + String refPath = new File(testRefFolder, javaFileName + ".dot").getAbsolutePath(); + String javaPath = new File(testSourceFolder, javaFiles[i]).getAbsolutePath(); + String[] options = new String[] { "-hide", "Hidden", "-private", "-output", dotPath, + javaPath }; + + com.sun.tools.javadoc.Main.execute("UMLGraph test", pw, pw, pw, "UmlGraph", options); + + System.out.println("Performing diff:\nout:" + dotPath + "\nref:" + refPath); + DotDiff differ = new DotDiff(new File(dotPath), new File(refPath)); + if (differ.graphEquals()) { + System.out.println("File contents are structurally equal"); + } else { + equal = false; + System.out.println("File contents are structurally not equal"); + printList("# Lines in out but not in ref", differ.getExtraLines1()); + printList("# Lines in ref but not in out", differ.getExtraLines2()); + printList("# Nodes in out but not in ref", differ.getNodes1()); + printList("# Nodes in ref but not in out", differ.getNodes2()); + printList("# Arcs in out but not in ref", differ.getArcs1()); + printList("# Arcs in ref but not in out", differ.getArcs2()); + } + System.out.println("\n\n"); + } + + if (!equal) + System.out.println("ERROR, some file are not structurally equal"); + else + System.out.println("GOOD, all files are structurally equal"); + } + + private static void printList(String message, List extraOut) { + if (extraOut.size() > 0) { + System.out.println(message); + for (Object o : extraOut) { + System.out.println(o); + } + } + } + + private static final class JavaFilter implements FilenameFilter { + public boolean accept(File dir, String name) { + return name.endsWith(".java"); + } + } +} diff --git a/src/gr/spinellis/umlgraph/test/DotDiff.java b/src/gr/spinellis/umlgraph/test/DotDiff.java new file mode 100644 index 0000000..d1d01fd --- /dev/null +++ b/src/gr/spinellis/umlgraph/test/DotDiff.java @@ -0,0 +1,308 @@ +/* + * UmlGraph class diagram testing framework + * + * Contibuted by Andrea Aime + * (C) Copyright 2005 Diomidis Spinellis + * + * Permission to use, copy, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + * + */ + +package gr.spinellis.umlgraph.test; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.StreamTokenizer; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class DotDiff { + + private List nodes1 = new ArrayList(); + + private List nodes2 = new ArrayList(); + + private List arcs1 = new ArrayList(); + + private List arcs2 = new ArrayList(); + + private List extraLines1 = new ArrayList(); + + private List extraLines2 = new ArrayList(); + + /** + * Builds a dot differ on the two files + * + * @param dotFirst + * @param dotSecond + * @throws IOException + */ + public DotDiff(File dotFirst, File dotSecond) throws IOException { + // gather the lines + List lines1 = readGraphLines(dotFirst); + List lines2 = readGraphLines(dotSecond); + + // parse the lines + extraLines1 = parseLines(lines1, nodes1, arcs1); + extraLines2 = parseLines(lines2, nodes2, arcs2); + + // diff extra lines + for (Iterator it = extraLines1.iterator(); it.hasNext();) { + if (extraLines2.remove(it.next())) + it.remove(); + } + + // diff nodes + for (Iterator it = nodes1.iterator(); it.hasNext();) { + if (nodes2.remove(it.next())) + it.remove(); + } + + // diff arcs + for (Iterator it = arcs1.iterator(); it.hasNext();) { + if (arcs2.remove(it.next())) + it.remove(); + } + } + + /** + * Returns true if the dot files are structurally equal, that is, if every + * non comment and non header line of the first file appears in the second, + * and otherwise. + * + * @return + */ + public boolean graphEquals() { + return (extraLines1.size() + extraLines2.size() + nodes1.size() + nodes2.size() + + arcs1.size() + arcs2.size()) == 0; + } + + public List getArcs1() { + return arcs1; + } + + public List getArcs2() { + return arcs2; + } + + public List getExtraLines1() { + return extraLines1; + } + + public List getExtraLines2() { + return extraLines2; + } + + public List getNodes1() { + return nodes1; + } + + public List getNodes2() { + return nodes2; + } + + /** + * Reads all relevant lines from the dot file + * + * @param dotFile + * @return + * @throws IOException + */ + private List readGraphLines(File dotFile) throws IOException { + List lines = new ArrayList(); + BufferedReader br = null; + try { + br = new BufferedReader(new FileReader(dotFile)); + + String line; + while ((line = br.readLine()) != null) { + line = line.trim(); + if (graphDefinitionLine(line)) + lines.add(line); + } + } finally { + if (br != null) + br.close(); + } + + return lines; + } + + /** + * Tells if a line is relevant or not (unrelevant lines are headers, + * comments, "digraf G {" and the closing "}" (to simplify matters we assume + * the file is properly structured). + * + * @param line + * @return + */ + private boolean graphDefinitionLine(String line) { + return !(line.startsWith("#") || line.startsWith("//") || line.equals("digraph G {") || line + .equals("}")); + } + + private List parseLines(List lines, List nodeList, List arcs) + throws IOException { + List extraLines = new ArrayList(); + List arcLines = new ArrayList(); + Map nodes = new HashMap(); + for (String line : lines) { + int openBrackedIdx = line.indexOf('['); + int closedBracketIdx = line.lastIndexOf(']'); + int arrowIdx = line.indexOf("->"); + if (openBrackedIdx < 0 && closedBracketIdx < 0 || line.startsWith("edge") + || line.startsWith("node")) + extraLines.add(line); + else if (arrowIdx > 0 && arrowIdx < openBrackedIdx) { // that's an + arcLines.add(line); + } else { // that's a node + String attributes = line.substring(openBrackedIdx + 1, closedBracketIdx); + Map attMap = parseAttributes(attributes); + String name = line.substring(0, openBrackedIdx - 1).trim(); + String label = attMap.get("label"); + DotNode node = new DotNode(name, label, attMap, line); + nodes.put(name, node); + nodeList.add(node); + } + } + for (String line : arcLines) { + int openBrackedIdx = line.indexOf('['); + int closedBracketIdx = line.lastIndexOf(']'); + String attributes = line.substring(openBrackedIdx + 1, closedBracketIdx); + String[] names = line.substring(0, openBrackedIdx).split("->"); + DotNode from = nodes.get(names[0].trim()); + DotNode to = nodes.get(names[1].trim()); + if (from == null) { + from = new DotNode(names[0], "", new HashMap(), ""); + } + if (to == null) { + to = new DotNode(names[1], "", new HashMap(), ""); + } + arcs.add(new DotArc(from, to, parseAttributes(attributes), line)); + } + return extraLines; + } + + private Map parseAttributes(String attributes) throws IOException { + Map map = new HashMap(); + + StreamTokenizer st = new StreamTokenizer(new StringReader(attributes)); + st.wordChars('_', '_'); + st.wordChars('\\', '\\'); + st.wordChars('\'', '\''); + int tokenType; + boolean isValue = false; + String attName = null; + String token = null; + while ((tokenType = st.nextToken()) != StreamTokenizer.TT_EOF) { + switch (tokenType) { + case StreamTokenizer.TT_NUMBER: + token = "" + st.nval; + break; + case StreamTokenizer.TT_WORD: + case '"': + case '\'': + token = st.sval; + break; + } + tokenType = st.nextToken(); + + if (isValue) { + map.put(attName, token); + isValue = false; + } else { + attName = token; + isValue = true; + } + } + + return map; + } + + private static class DotNode { + String name; + + String label; + + Map attributes; + + String line; + + public DotNode(String name, String label, Map attributes, String line) { + this.name = name; + this.label = label.replace("\n", "\\n"); + this.attributes = attributes; + this.line = line.replace("\n", "\\n"); + } + + public boolean equals(Object other) { + if (!(other instanceof DotNode)) + return false; + + DotNode on = (DotNode) other; + if (label == null) // anonymous node + return on.label == null && on.name.equals(name) && on.attributes.equals(attributes); + else + return on.label.equals(label) && on.attributes.equals(attributes); + } + + public int hashCode() { + return name.hashCode() + 17 * attributes.hashCode(); + } + + public String toString() { + return "Node: " + label + "; " + line; + } + + } + + private static class DotArc { + DotNode from; + + DotNode to; + + Map attributes; + + String line; + + public DotArc(DotNode from, DotNode to, Map attributes, String line) { + this.from = from; + this.to = to; + this.attributes = attributes; + this.line = line.replace("\n", "\\n"); + } + + public boolean equals(Object other) { + if (!(other instanceof DotArc)) + return false; + + DotArc oa = (DotArc) other; + return oa.from.equals(from) && oa.to.equals(to) && oa.attributes.equals(attributes); + } + + public int hashCode() { + return from.hashCode() + 17 * (to.hashCode() + 17 * attributes.hashCode()); + } + + public String toString() { + return "Arc: " + from.label + " -> " + to.label + "; " + line; + } + } + +} diff --git a/src/org/umlgraph/test/BasicTest.java b/src/org/umlgraph/test/BasicTest.java new file mode 100644 index 0000000..9d9fec1 --- /dev/null +++ b/src/org/umlgraph/test/BasicTest.java @@ -0,0 +1,95 @@ +/* + * UmlGraph class diagram testing framework + * + * Contibuted by Andrea Aime + * (C) Copyright 2005 Diomidis Spinellis + * + * Permission to use, copy, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + * + */ + +package gr.spinellis.umlgraph.test; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; + +public class BasicTest { + + static String testSourceFolder = "./test-data/simple/java"; + + static String testDestFolder = "./test-data/simple/dot-out"; + + static String testRefFolder = "./test-data/simple/dot-ref"; + + static PrintWriter pw = new PrintWriter(System.out); + + public static void main(String[] args) throws IOException { + String[] javaFiles = new File(testSourceFolder).list(new JavaFilter()); +// String[] javaFiles = new String[] {"MyVector.java"}; + + File outFolder = new File(testDestFolder); + if (!outFolder.exists()) + outFolder.mkdirs(); + + boolean equal = true; + for (int i = 0; i < javaFiles.length; i++) { + String javaFileName = javaFiles[i].substring(0, javaFiles[i].length() - 5); + String dotPath = new File(testDestFolder, javaFileName + ".dot").getAbsolutePath(); + String refPath = new File(testRefFolder, javaFileName + ".dot").getAbsolutePath(); + String javaPath = new File(testSourceFolder, javaFiles[i]).getAbsolutePath(); + String[] options = new String[] { "-hide", "Hidden", "-private", "-output", dotPath, + javaPath }; + + com.sun.tools.javadoc.Main.execute("UMLGraph test", pw, pw, pw, "UmlGraph", options); + + System.out.println("Performing diff:\nout:" + dotPath + "\nref:" + refPath); + DotDiff differ = new DotDiff(new File(dotPath), new File(refPath)); + if (differ.graphEquals()) { + System.out.println("File contents are structurally equal"); + } else { + equal = false; + System.out.println("File contents are structurally not equal"); + printList("# Lines in out but not in ref", differ.getExtraLines1()); + printList("# Lines in ref but not in out", differ.getExtraLines2()); + printList("# Nodes in out but not in ref", differ.getNodes1()); + printList("# Nodes in ref but not in out", differ.getNodes2()); + printList("# Arcs in out but not in ref", differ.getArcs1()); + printList("# Arcs in ref but not in out", differ.getArcs2()); + } + System.out.println("\n\n"); + } + + if (!equal) + System.out.println("ERROR, some file are not structurally equal"); + else + System.out.println("GOOD, all files are structurally equal"); + } + + private static void printList(String message, List extraOut) { + if (extraOut.size() > 0) { + System.out.println(message); + for (Object o : extraOut) { + System.out.println(o); + } + } + } + + private static final class JavaFilter implements FilenameFilter { + public boolean accept(File dir, String name) { + return name.endsWith(".java"); + } + } +} diff --git a/src/org/umlgraph/test/DotDiff.java b/src/org/umlgraph/test/DotDiff.java new file mode 100644 index 0000000..d1d01fd --- /dev/null +++ b/src/org/umlgraph/test/DotDiff.java @@ -0,0 +1,308 @@ +/* + * UmlGraph class diagram testing framework + * + * Contibuted by Andrea Aime + * (C) Copyright 2005 Diomidis Spinellis + * + * Permission to use, copy, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id$ + * + */ + +package gr.spinellis.umlgraph.test; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.StreamTokenizer; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class DotDiff { + + private List nodes1 = new ArrayList(); + + private List nodes2 = new ArrayList(); + + private List arcs1 = new ArrayList(); + + private List arcs2 = new ArrayList(); + + private List extraLines1 = new ArrayList(); + + private List extraLines2 = new ArrayList(); + + /** + * Builds a dot differ on the two files + * + * @param dotFirst + * @param dotSecond + * @throws IOException + */ + public DotDiff(File dotFirst, File dotSecond) throws IOException { + // gather the lines + List lines1 = readGraphLines(dotFirst); + List lines2 = readGraphLines(dotSecond); + + // parse the lines + extraLines1 = parseLines(lines1, nodes1, arcs1); + extraLines2 = parseLines(lines2, nodes2, arcs2); + + // diff extra lines + for (Iterator it = extraLines1.iterator(); it.hasNext();) { + if (extraLines2.remove(it.next())) + it.remove(); + } + + // diff nodes + for (Iterator it = nodes1.iterator(); it.hasNext();) { + if (nodes2.remove(it.next())) + it.remove(); + } + + // diff arcs + for (Iterator it = arcs1.iterator(); it.hasNext();) { + if (arcs2.remove(it.next())) + it.remove(); + } + } + + /** + * Returns true if the dot files are structurally equal, that is, if every + * non comment and non header line of the first file appears in the second, + * and otherwise. + * + * @return + */ + public boolean graphEquals() { + return (extraLines1.size() + extraLines2.size() + nodes1.size() + nodes2.size() + + arcs1.size() + arcs2.size()) == 0; + } + + public List getArcs1() { + return arcs1; + } + + public List getArcs2() { + return arcs2; + } + + public List getExtraLines1() { + return extraLines1; + } + + public List getExtraLines2() { + return extraLines2; + } + + public List getNodes1() { + return nodes1; + } + + public List getNodes2() { + return nodes2; + } + + /** + * Reads all relevant lines from the dot file + * + * @param dotFile + * @return + * @throws IOException + */ + private List readGraphLines(File dotFile) throws IOException { + List lines = new ArrayList(); + BufferedReader br = null; + try { + br = new BufferedReader(new FileReader(dotFile)); + + String line; + while ((line = br.readLine()) != null) { + line = line.trim(); + if (graphDefinitionLine(line)) + lines.add(line); + } + } finally { + if (br != null) + br.close(); + } + + return lines; + } + + /** + * Tells if a line is relevant or not (unrelevant lines are headers, + * comments, "digraf G {" and the closing "}" (to simplify matters we assume + * the file is properly structured). + * + * @param line + * @return + */ + private boolean graphDefinitionLine(String line) { + return !(line.startsWith("#") || line.startsWith("//") || line.equals("digraph G {") || line + .equals("}")); + } + + private List parseLines(List lines, List nodeList, List arcs) + throws IOException { + List extraLines = new ArrayList(); + List arcLines = new ArrayList(); + Map nodes = new HashMap(); + for (String line : lines) { + int openBrackedIdx = line.indexOf('['); + int closedBracketIdx = line.lastIndexOf(']'); + int arrowIdx = line.indexOf("->"); + if (openBrackedIdx < 0 && closedBracketIdx < 0 || line.startsWith("edge") + || line.startsWith("node")) + extraLines.add(line); + else if (arrowIdx > 0 && arrowIdx < openBrackedIdx) { // that's an + arcLines.add(line); + } else { // that's a node + String attributes = line.substring(openBrackedIdx + 1, closedBracketIdx); + Map attMap = parseAttributes(attributes); + String name = line.substring(0, openBrackedIdx - 1).trim(); + String label = attMap.get("label"); + DotNode node = new DotNode(name, label, attMap, line); + nodes.put(name, node); + nodeList.add(node); + } + } + for (String line : arcLines) { + int openBrackedIdx = line.indexOf('['); + int closedBracketIdx = line.lastIndexOf(']'); + String attributes = line.substring(openBrackedIdx + 1, closedBracketIdx); + String[] names = line.substring(0, openBrackedIdx).split("->"); + DotNode from = nodes.get(names[0].trim()); + DotNode to = nodes.get(names[1].trim()); + if (from == null) { + from = new DotNode(names[0], "", new HashMap(), ""); + } + if (to == null) { + to = new DotNode(names[1], "", new HashMap(), ""); + } + arcs.add(new DotArc(from, to, parseAttributes(attributes), line)); + } + return extraLines; + } + + private Map parseAttributes(String attributes) throws IOException { + Map map = new HashMap(); + + StreamTokenizer st = new StreamTokenizer(new StringReader(attributes)); + st.wordChars('_', '_'); + st.wordChars('\\', '\\'); + st.wordChars('\'', '\''); + int tokenType; + boolean isValue = false; + String attName = null; + String token = null; + while ((tokenType = st.nextToken()) != StreamTokenizer.TT_EOF) { + switch (tokenType) { + case StreamTokenizer.TT_NUMBER: + token = "" + st.nval; + break; + case StreamTokenizer.TT_WORD: + case '"': + case '\'': + token = st.sval; + break; + } + tokenType = st.nextToken(); + + if (isValue) { + map.put(attName, token); + isValue = false; + } else { + attName = token; + isValue = true; + } + } + + return map; + } + + private static class DotNode { + String name; + + String label; + + Map attributes; + + String line; + + public DotNode(String name, String label, Map attributes, String line) { + this.name = name; + this.label = label.replace("\n", "\\n"); + this.attributes = attributes; + this.line = line.replace("\n", "\\n"); + } + + public boolean equals(Object other) { + if (!(other instanceof DotNode)) + return false; + + DotNode on = (DotNode) other; + if (label == null) // anonymous node + return on.label == null && on.name.equals(name) && on.attributes.equals(attributes); + else + return on.label.equals(label) && on.attributes.equals(attributes); + } + + public int hashCode() { + return name.hashCode() + 17 * attributes.hashCode(); + } + + public String toString() { + return "Node: " + label + "; " + line; + } + + } + + private static class DotArc { + DotNode from; + + DotNode to; + + Map attributes; + + String line; + + public DotArc(DotNode from, DotNode to, Map attributes, String line) { + this.from = from; + this.to = to; + this.attributes = attributes; + this.line = line.replace("\n", "\\n"); + } + + public boolean equals(Object other) { + if (!(other instanceof DotArc)) + return false; + + DotArc oa = (DotArc) other; + return oa.from.equals(from) && oa.to.equals(to) && oa.attributes.equals(attributes); + } + + public int hashCode() { + return from.hashCode() + 17 * (to.hashCode() + 17 * attributes.hashCode()); + } + + public String toString() { + return "Arc: " + from.label + " -> " + to.label + "; " + line; + } + } + +}