// $Id: TestDependencies.java 43 2010-04-03 20:28:12Z marcusvnac $
// Copyright (c) 2007-2009 The Regents of the University of California. All
// Rights Reserved. Permission to use, copy, modify, and distribute this
// software and its documentation without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph appear in all copies. This software program and
// documentation are copyrighted by The Regents of the University of
// California. The software program and documentation are supplied "AS
// IS", without any accompanying services from The Regents. The Regents
// does not warrant that the operation of the program will be
// uninterrupted or error-free. The end-user understands that the program
// was developed for research purposes and is advised not to rely
// exclusively on the program for any reason. IN NO EVENT SHALL THE
// UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
// UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
package org.argouml;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.List;
import jdepend.framework.JDepend;
import jdepend.framework.JavaPackage;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Test for Dependency cycles with JDepend. <p>
*
* This test will guarantee that once
* a package is made free of dependency-cycles,
* (and it has been added here,)
* it will stay dependency-cycle free. <p>
*
* It also checks that the top level package(s) are
* (and remain) unused by other packages. <p>
*
* It also checks that low level packages do not
* use other argouml packages.
*
* @author Michiel
*/
public class TestDependencies extends TestCase {
/**
* Tests that a list of packages does not contain
* any package dependency cycles.
*
* @return a list of tests.
*/
public static Test suite() {
JDepend jdepend = new JDepend();
try {
jdepend.addDirectory("build-eclipse");
} catch (IOException e) {
// Ignore if the directory does not exist.
// This error will throw when running from the ant setup.
System.out.println("Assuming running from ant!");
}
try {
jdepend.addDirectory("build/classes");
} catch (IOException e) {
// Ignore if the directory does not exist.
// This error will throw when running from the Eclipse setup.
System.out.println("Assuming running from Eclipse!");
}
jdepend.analyze();
TestSuite suite =
new TestSuite("Tests for dependencies using Jdepend");
String[] clean = {
"org.argouml.application.api",
"org.argouml.application.events",
"org.argouml.application.helpers",
"org.argouml.application.security",
"org.argouml.cognitive.checklist",
//"org.argouml.cognitive.critics",
"org.argouml.configuration",
"org.argouml.i18n",
"org.argouml.gefext",
"org.argouml.language.ui",
"org.argouml.moduleloader",
//"org.argouml.notation.providers",//fails because sub-packages fail (?)
//"org.argouml.notation.providers.java",
//"org.argouml.notation.providers.uml",
//"org.argouml.notation",//fails because sub-packages fail (?)
//"org.argouml.notation.ui",
"org.argouml.swingext",
"org.argouml.taskmgmt",
"org.argouml.uml.diagram.layout",
"org.argouml.uml.generator",
"org.argouml.uml.util.namespace",
"org.argouml.util.logging",
"org.argouml.util.osdep",
"org.argouml.util",
// There was a comment saying that the below has no cycles, but
// Classycle thinks there's a cycle here too, so I believe there
// really is one - tfm 20070702
// "org.argouml.uml.cognitive.critics",
};
suite.addTest(new TimeStamp());
for (int i = 0; i < clean.length; i++) {
suite.addTest(new CheckDependencyCycle(jdepend, clean[i]));
}
String[] top = {
"org.argouml.application",
};
for (int i = 0; i < top.length; i++) {
suite.addTest(new CheckTopLevel(jdepend, top[i]));
}
String[] low = {
"org.argouml.application.security",
"org.argouml.configuration",
"org.argouml.i18n",
"org.argouml.swingext",
"org.argouml.taskmgmt",
"org.argouml.util.logging",
"org.argouml.util.osdep",
};
for (int i = 0; i < low.length; i++) {
suite.addTest(new CheckLowLevel(jdepend, low[i]));
}
String[][] dep = {
// There shall not be a dependency from ... to ...
{"org.argouml.persistence", "org.argouml.ui"},
{"org.argouml.moduleloader", "org.argouml.persistence"},
{"org.argouml.notation", "org.argouml.notation.ui"},
{"org.argouml.ui.targetmanager", "org.argouml.ui"},
{"org.argouml.moduleloader", "org.argouml.ui"},
{"org.argouml.cognitive", "org.argouml.ui"},
{"org.argouml.cognitive.critics", "org.argouml.cognitive.ui"},
{"org.argouml.ui", "org.argouml.cognitive.critics.ui"},
//TODO:{"org.argouml.ui", "org.argouml.cognitive.ui"},//fails due to subpackages
//TODO:{"org.argouml.cognitive", "org.argouml.cognitive.critics"},
{"org.argouml.uml.diagram", "org.argouml.ui"},
{"org.argouml.ui", "org.argouml.notation.ui"},
{"org.argouml.util", "org.argouml.ui.cmd"},
//{"org.argouml.kernel", "org.argouml.uml.diagram.ui"},//why does this fail?
};
for (int i = 0; i < dep.length; i++) {
suite.addTest(new CheckNoDependency(jdepend, dep[i]));
}
return suite;
}
static class DateUtils {
static final String DATE_FORMAT_NOW = "yyyy-MM-dd HH:mm:ss Z";
public static String now() {
Calendar cal = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_NOW);
return sdf.format(cal.getTime());
}
}
static class TimeStamp extends TestCase {
TimeStamp() {
super("Timestamp: " + DateUtils.now());
}
@SuppressWarnings("unchecked")
public void runTest() {
// do nothing by design
}
}
static class CheckDependencyCycle extends TestCase {
private String packageName;
private JDepend jdepend;
CheckDependencyCycle(JDepend jd, String name) {
super("Check dependency cycle in " + name);
jdepend = jd;
packageName = name;
}
@SuppressWarnings("unchecked")
public void runTest() {
JavaPackage p = jdepend.getPackage(packageName);
assertNotNull(p);
if (p.containsCycle()) {
StringBuffer msg = new StringBuffer(
"JDepend indicates a dependency cycle in ");
msg.append(p.getName());
List<JavaPackage> firstCycle = new ArrayList<JavaPackage>();
p.collectCycle(firstCycle);
msg.append("(" + firstCycle.size());
msg.append(" packages in first cycle: ");
for (JavaPackage cp : firstCycle) {
msg.append(cp.getName()).append(" ");
}
msg.append(") -- ");
List<JavaPackage> otherCycles = new ArrayList<JavaPackage>();
p.collectAllCycles(otherCycles);
otherCycles.removeAll(firstCycle);
if (!otherCycles.isEmpty()) {
msg.append("(" + otherCycles.size());
msg.append(" packages in additional cycle(s): ");
for (JavaPackage cp : otherCycles) {
msg.append(cp.getName()).append(" ");
}
msg.append(") -- ");
}
// msg.append("(" + p.getClassCount() + " classes: ");
// Collection<JavaClass> c = p.getClasses();
// for (JavaClass jc : c) {
// msg.append(jc.getName());
// msg.append(" ");
// }
// msg.append(")");
assertTrue(msg.toString(), false);
}
}
}
static class CheckTopLevel extends TestCase {
private String packageName;
private JDepend jdepend;
CheckTopLevel(JDepend jd, String name) {
super("Check top level dependencies for " + name);
jdepend = jd;
packageName = name;
}
@SuppressWarnings("unchecked")
public void runTest() {
JavaPackage p = jdepend.getPackage(packageName);
assertNotNull(p);
Collection<JavaPackage> afferents = p.getAfferents();
if (afferents.size() > 0) {
StringBuffer msg = new StringBuffer("JDepend "
+ "indicates an afferent dependency "
+ "to a top level package: ");
msg.append(p.getName());
msg.append(" is used by ");
msg.append(afferents.size());
msg.append(" packages: ");
for (JavaPackage jp : afferents) {
msg.append(jp.getName());
msg.append(" ");
}
assertTrue(msg.toString(), false);
}
}
}
static class CheckLowLevel extends TestCase {
private String packageName;
private JDepend jdepend;
CheckLowLevel(JDepend jd, String name) {
super("Check low level dependencies for " + name);
jdepend = jd;
packageName = name;
}
@SuppressWarnings("unchecked")
public void runTest() {
JavaPackage p = jdepend.getPackage(packageName);
assertNotNull(p);
Collection<JavaPackage> efferents = p.getEfferents();
Collection<JavaPackage> wrong = new ArrayList<JavaPackage>();
for (JavaPackage jp : efferents) {
if (jp.getName().startsWith("org.argouml")) {
wrong.add(jp);
}
if (!wrong.isEmpty()) {
StringBuffer msg = new StringBuffer("JDepend "
+ "indicates a dependency from "
+ "the low level package ");
msg.append(p.getName());
msg.append(" to the argouml package(s)");
for (JavaPackage jpWrong : wrong) {
msg.append(" ");
msg.append(jpWrong.getName());
}
assertTrue(msg.toString(), false);
}
}
}
}
static class CheckNoDependency extends TestCase {
private String nameFrom;
private String nameTo;
private JDepend jdepend;
CheckNoDependency(JDepend jd, String[] name) {
super("Check for dependency from " + name[0] + " to " + name[1]);
jdepend = jd;
nameFrom = name[0];
nameTo = name[1];
}
@SuppressWarnings("unchecked")
public void runTest() {
JavaPackage packageFrom = jdepend.getPackage(nameFrom);
JavaPackage packageTo = jdepend.getPackage(nameTo);
assertNotNull("Missing package", packageFrom);
Collection<JavaPackage> efferents = packageFrom.getEfferents();
for (JavaPackage jp : efferents) {
if (jp.equals(packageTo)) {
StringBuffer msg = new StringBuffer(
"JDepend indicates a dependency from ");
msg.append(nameFrom);
msg.append(" to ");
msg.append(nameTo);
assertTrue(msg.toString(), false);
}
}
}
}
}