├── .gitignore ├── build.gradle ├── check └── src │ ├── main │ └── scala │ │ └── de │ │ └── schauderhaft │ │ └── degraph │ │ └── check │ │ ├── Check.scala │ │ ├── ConstraintBuilder.scala │ │ ├── JCheck.scala │ │ ├── constraints.scala │ │ └── hamcrest │ │ └── HamcrestWrapper.scala │ └── test │ ├── java │ └── de │ │ └── schauderhaft │ │ └── degraph │ │ └── check │ │ └── JavaCheckApiTest.java │ └── scala │ └── de │ └── schauderhaft │ └── degraph │ └── check │ ├── CheckTest.scala │ ├── ConstraintBuilderTest.scala │ ├── CycleFreeTest.scala │ ├── DependencyTest.scala │ ├── DirectLayeringConstraintTest.scala │ ├── LayeringConstraintTest.scala │ ├── MockSliceSource.scala │ ├── MultiElementLenientLayerConstraintTest.scala │ ├── MultiElementStrictLayerConstraintTest.scala │ ├── NodeTestUtil.scala │ └── hamcrest │ └── HamcrestWrapperTest.scala ├── core └── src │ ├── main │ └── scala │ │ └── de │ │ └── schauderhaft │ │ └── degraph │ │ ├── analysis │ │ ├── AnalyzerLike.scala │ │ ├── IncludeExcludeFilter.scala │ │ ├── NoSelfReference.scala │ │ └── asm │ │ │ ├── Analyzer.scala │ │ │ ├── FileFinder.scala │ │ │ └── GraphBuildingClassVisitor.scala │ │ ├── configuration │ │ ├── Configuration.scala │ │ ├── Constraint.scala │ │ ├── PrintConfiguration.scala │ │ └── Slicer.scala │ │ ├── graph │ │ └── Graph.scala │ │ ├── java │ │ ├── Categorizer.scala │ │ ├── JavaGraph.scala │ │ └── JavaHierarchicGraph.scala │ │ ├── model │ │ └── SimpleNode.scala │ │ ├── slicer │ │ ├── CombinedSlicer.scala │ │ ├── InternalClassCategorizer.scala │ │ ├── MultiCategorizer.scala │ │ ├── NamedPatternMatchingCategorizer.scala │ │ ├── PackageCategorizer.scala │ │ ├── ParallelCategorizer.scala │ │ ├── PatternMatcher.scala │ │ ├── PatternMatchingCategorizer.scala │ │ └── PatternMatchingFilter.scala │ │ └── writer │ │ ├── EdgeStyle.scala │ │ ├── Labeling.scala │ │ ├── PredicateStyler.scala │ │ ├── SlicePredicate.scala │ │ └── Writer.scala │ └── test │ ├── java │ └── de │ │ └── schauderhaft │ │ └── degraph │ │ ├── examples │ │ ├── AnImplementation.java │ │ ├── AnInterface.java │ │ ├── AnnoA.java │ │ ├── AnnoB.java │ │ ├── AnnoC.java │ │ ├── ClassWithAnnotationWithArrayOfClasses.java │ │ ├── DependsOnArray.java │ │ ├── InstanceReferenceToStaticField.java │ │ ├── InstanceReferenceToStaticMethod.java │ │ ├── SomeAnnotation.java │ │ ├── StaticMethod.java │ │ ├── StaticReferenceToStaticField.java │ │ ├── StaticReferenceToStaticMethod.java │ │ └── UsageInMethod.java │ │ └── java │ │ ├── ChessCategory.java │ │ ├── ConstantCategorizer.java │ │ ├── JavaApiTest.java │ │ └── NodeBuilder.java │ └── scala │ └── de │ └── schauderhaft │ └── degraph │ ├── analysis │ ├── IncludeExcludeFilterTest.scala │ ├── NoSelfReferenceTest.scala │ ├── asm │ │ ├── FileFinderTest.scala │ │ └── GraphBuildingClassVisitorTest.scala │ └── test │ │ └── AnalyzerTest.scala │ ├── configuration │ ├── ConfigurationConstraintTest.scala │ ├── ConfigurationTest.scala │ ├── ConstraintViolationTest.scala │ ├── NamedPatternTest.scala │ ├── PrintConfigurationTest.scala │ └── SlicerTest.scala │ ├── examples │ └── SuperClass.scala │ ├── graph │ ├── GraphSliceTest.scala │ ├── GraphTest.scala │ ├── NodeTestUtil.scala │ └── SliceNodeFinderTest.scala │ ├── slicer │ ├── CombinedSlicerTest.scala │ ├── InternalClassCategorizerTest.scala │ ├── ListCategory.scala │ ├── MultiCategorizerTest.scala │ ├── NamedPatternMatchingCategorizerTest.scala │ ├── PackageCategorizerTest.scala │ ├── ParallelCategorizerTest.scala │ ├── PatternMatcherTest.scala │ ├── PatternMatchingCategorizerTest.scala │ └── PatternMatchingFilterTest.scala │ └── writer │ ├── EdgeStyleTest.scala │ ├── LabelingTest.scala │ ├── PredicateStylerTest.scala │ ├── SlicePredicateTest.scala │ └── WriterTest.scala ├── degraph └── src │ ├── demo │ └── resource │ │ └── example1.config │ ├── dist │ └── lib │ │ ├── ASMLicense.txt │ │ ├── ScalaLibraryLicense.txt │ │ └── ScallopLicense.txt │ ├── main │ ├── ghpages │ │ ├── images │ │ │ ├── body-bg.png │ │ │ ├── highlight-bg.jpg │ │ │ ├── hr.png │ │ │ ├── octocat-icon.png │ │ │ ├── tar-gz-icon.png │ │ │ └── zip-icon.png │ │ ├── index.md │ │ ├── indexx.html │ │ ├── javascripts │ │ │ └── main.js │ │ ├── params.json │ │ └── stylesheets │ │ │ ├── print.css │ │ │ ├── pygment_trac.css │ │ │ └── stylesheet.css │ └── scala │ │ └── de │ │ └── schauderhaft │ │ └── degraph │ │ └── app │ │ ├── CommandLineParser.scala │ │ ├── ConfigurationFromCommandLine.scala │ │ ├── ConfigurationParser.scala │ │ └── Degraph.scala │ └── test │ ├── resource │ ├── example.config │ ├── junit.config │ └── manualSelfTest.config │ └── scala │ └── de │ └── schauderhaft │ └── degraph │ └── app │ ├── CommandLineParserTest.scala │ ├── ConfigurationFromCommandLineTest.scala │ └── ConfigurationParserTest.scala ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jdk8tests └── src │ ├── main │ └── java │ │ └── de │ │ └── schauderhaft │ │ └── degraph │ │ └── jdk8tests │ │ ├── ClassWithTypeAnnotations.java │ │ ├── ClassWithTypeParam.java │ │ ├── TypeAnno1.java │ │ ├── TypeAnno2.java │ │ ├── TypeAnno3.java │ │ ├── TypeAnno4.java │ │ ├── TypeAnno5.java │ │ ├── TypeAnno6.java │ │ ├── TypeAnno7.java │ │ └── TypeAnno8.java │ └── test │ └── scala │ └── de │ └── schauderhaft │ └── degraph │ └── jdk8tests │ └── AnalyzerTest.scala ├── license.txt ├── readme.md ├── releaseNotes.md ├── settings.gradle ├── shippable.yml └── todo.md /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | out/ 3 | build/ 4 | *.graphml 5 | .* 6 | caches/ 7 | native/ 8 | wrapper/ 9 | fileFinderTestDir/ 10 | *.iml 11 | *.ipr 12 | *.iws 13 | /*.html 14 | 15 | 16 | /degraph.iprx 17 | -------------------------------------------------------------------------------- /check/src/main/scala/de/schauderhaft/degraph/check/Check.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.check 2 | 3 | import java.awt.Color._ 4 | 5 | import de.schauderhaft.degraph.analysis.asm.Analyzer 6 | import de.schauderhaft.degraph.configuration.{Configuration, ConstraintViolation} 7 | import de.schauderhaft.degraph.model.Node 8 | import de.schauderhaft.degraph.writer._ 9 | import org.scalatest.matchers.{BeMatcher, MatchResult} 10 | 11 | import scala.xml.XML 12 | 13 | /** 14 | * provides access to configurations and scalatest matchers useful when testing for dependencies. 15 | * 16 | * With all members of Check imported a simple use case looks like this: 17 | * 18 | * classpath.forType("module").allow("advertising", "billing", "customer") should be (violationFree) 19 | * 20 | * Such a test checks that 21 | * * all slice types are cycle free. 22 | * * for the slicetype *module* the slices *advertising* may depend on 23 | * *billing* and *customer*, and *billing* may depend on *customer*. 24 | * * Dependencies in other directions are not allowed. 25 | * 26 | * Other slices not mentioned in the constraint definition may depend on each other or on the slices in the constraint. 27 | */ 28 | object Check { 29 | 30 | /** 31 | * a Configuration object containing the current classpath. 32 | * 33 | * Intended as a starting point for analyzing the application that is currently running. 34 | * 35 | * Note in a typical maven like setup it will also include the test classes as well as all libraries, which might not be desirable. 36 | * Manipulate the configuration to only contain the classpath elements required or use include and exclude filters for limiting the analyzed classes. 37 | */ 38 | val classpath = customClasspath(System.getProperty("java.class.path")) 39 | 40 | /** 41 | * a Configuration object containing a custom classpath. 42 | * 43 | * Intended as a starting point for analyzing the application that is currently running. 44 | */ 45 | def customClasspath(path: String): ConstraintBuilder = ConstraintBuilder(new Configuration( 46 | classpath = Option(path), 47 | analyzer = Analyzer), "package") 48 | 49 | /** 50 | * a matcher for Configurations testing if the classes specified in the configuration 51 | * adhere to the dependency constraints configured in the configuration. 52 | */ 53 | val violationFree: BeMatcher[ConstraintBuilder] = new BeMatcher[ConstraintBuilder] { 54 | def apply(constraintBuilder: ConstraintBuilder) = { 55 | val conf = constraintBuilder.configuration 56 | val g = conf.createGraph() 57 | 58 | def maybePrintGraph(vs: Set[ConstraintViolation]) { 59 | if (vs.nonEmpty) { 60 | val styler = PredicateStyler.styler( 61 | new SlicePredicate(conf.slicing, vs. 62 | flatMap(_.dependencies)), 63 | EdgeStyle(RED, 2.0), DefaultEdgeStyle 64 | ) 65 | val xml = new Writer(styler).toXml(g) 66 | conf.output.foreach(XML.save(_, xml, "UTF8", xmlDecl = true, doctype = null)) 67 | } 68 | } 69 | 70 | def checkForViolations(): Set[ConstraintViolation] = { 71 | val vs = for { 72 | c <- conf.constraint 73 | v <- c.violations(g) 74 | } yield v 75 | maybePrintGraph(vs) 76 | vs 77 | } 78 | 79 | val violations = checkForViolations() 80 | 81 | val matches = violations.isEmpty 82 | 83 | val failureMessage = "%s yields the following constraint violations: %s".format(conf, violations.mkString("%n").format()) 84 | val negativeFailureMessage = "%s does not yield any violations of the constraints.".format(conf) 85 | 86 | new MatchResult(matches, failureMessage, negativeFailureMessage) 87 | } 88 | } 89 | 90 | private def sliceNode(edge: (Node, Node)) = { 91 | val (n1, n2) = edge 92 | n1.types == n2.types && n1.types.head != "Class" 93 | } 94 | 95 | private def lowerCaseFirstLetter(s: String) = s.head.toLower + s.tail 96 | } 97 | 98 | -------------------------------------------------------------------------------- /check/src/main/scala/de/schauderhaft/degraph/check/ConstraintBuilder.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.check 2 | 3 | import de.schauderhaft.degraph.configuration._ 4 | 5 | import scala.annotation.varargs 6 | 7 | /** 8 | * the basis for the DSL do define constraints on your dependencies in tests. 9 | */ 10 | case class ConstraintBuilder( 11 | private val config: Configuration = new Configuration(), 12 | sliceType: String = "", 13 | includes: Seq[String] = Seq(), 14 | excludes: Seq[String] = Seq(), 15 | slicings: Map[String, Seq[Pattern]] = Map(), 16 | constraints: Set[Constraint] = Set(), 17 | output: PrintConfiguration = NoPrinting() 18 | ) { 19 | 20 | 21 | /** 22 | * When the test fails the analysis results get written to the given path as a graphml file 23 | * 24 | * @param path the path or just the file name to use for writing out the graphml-file 25 | * @return 26 | */ 27 | def printOnFailure(path: String) = copy(output = Print(path, onConstraintViolationOnly = true)) 28 | 29 | def printTo(path: String) = copy(output = Print(path, onConstraintViolationOnly = false)) 30 | 31 | private def any2Layer(arg: AnyRef): Layer = arg match { 32 | case s: String => LenientLayer(s) 33 | case l: Layer => l 34 | case _ => throw new IllegalArgumentException("Only arguments of type String or Layer are accepted") 35 | } 36 | 37 | private def modifyConfig(slices: IndexedSeq[AnyRef], toConstraint: (String, IndexedSeq[Layer]) => Constraint): ConstraintBuilder = 38 | copy( 39 | constraints = 40 | constraints + 41 | toConstraint(sliceType, slices.map((x: AnyRef) => any2Layer(x))) 42 | ) 43 | 44 | /** 45 | * allows a chain of dependencies. Any slice specified as an argument may depend on any slice mentioned later in the list of arguments. 46 | * Dependencies in the opposite direction are forbidden and lead to a test failure 47 | * 48 | * Arguments are either Strings, denoting a slice or a {Layer} 49 | * 50 | * see also #allowDirect 51 | */ 52 | @varargs def allow(slices: AnyRef*): ConstraintBuilder = 53 | modifyConfig(slices.toIndexedSeq, LayeringConstraint) 54 | 55 | /** 56 | * allows a slice mentioned in the argument list to depend on the directly next element in the argument list. 57 | * 58 | * 59 | * 60 | * see also #allow 61 | */ 62 | @varargs def allowDirect(slices: AnyRef*): ConstraintBuilder = 63 | modifyConfig(slices.toIndexedSeq, DirectLayeringConstraint) 64 | 65 | @varargs def withSlicing(sliceType: String, sls: AnyRef*) = { 66 | copy( 67 | sliceType = sliceType, slicings = slicings + (sliceType -> 68 | sls.map { 69 | case s: String => UnnamedPattern(s) 70 | case (n: String, p: String) => NamedPattern(n, p) 71 | case p: Pattern => p 72 | }) 73 | ) 74 | } 75 | 76 | /** 77 | * defines the packages to be included in the analysis 78 | * @param s an ANT style pattern (using ** and * as wildcards) for packages to be included 79 | * @return this 80 | */ 81 | def including(s: String): ConstraintBuilder = copy(includes = includes :+ s) 82 | 83 | 84 | /** 85 | * defines the packages to be excluded in the analysis 86 | * @param s an ANT style pattern (using ** and * as wildcards) for packages to be included 87 | * @return this 88 | */ 89 | def excluding(s: String): ConstraintBuilder = copy(excludes = excludes :+ s) 90 | 91 | /** 92 | * removes all jar files from the classpath to be analyzed. 93 | * 94 | * Everything ending in .jar is considered a jar file. 95 | */ 96 | def noJars: ConstraintBuilder = 97 | filterClasspath(!_.endsWith(".jar")) 98 | 99 | /** 100 | * reduces the classpath to be analyzed to include only those elements matching the pattern. 101 | * 102 | * The pattern is can contain * as a wildcard. 103 | */ 104 | def filterClasspath(pattern: String): ConstraintBuilder = { 105 | val regexp = pattern.replace("*", ".*").r 106 | val filter: (String) => Boolean = regexp.pattern.matcher(_).matches() 107 | filterClasspath(filter) 108 | } 109 | 110 | /** 111 | * reduces the classpath to be analyzed to include only those elements matching the filter. 112 | * 113 | */ 114 | def filterClasspath(filter: (String) => Boolean): ConstraintBuilder = { 115 | copy(config = config.copy(classpath = config.classpath.map(filterClasspathString(_, filter)))) 116 | } 117 | 118 | def configuration = config.copy( 119 | categories = slicings, 120 | includes = includes, 121 | excludes = excludes, 122 | constraint = config.constraint ++ constraints, 123 | output = output 124 | ) 125 | 126 | private def filterClasspathString(s: String, filter: String => Boolean) = { 127 | val sep = System.getProperty("path.separator") 128 | val elements = s.split(sep) 129 | elements.filter(filter).mkString(sep) 130 | } 131 | 132 | 133 | } -------------------------------------------------------------------------------- /check/src/main/scala/de/schauderhaft/degraph/check/JCheck.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.check 2 | 3 | import de.schauderhaft.degraph.check.hamcrest.HamcrestWrapper 4 | 5 | /** 6 | * This is the Java DSL for writing Degraph dependency tests using Java. 7 | * 8 | * It is equivalent with Check, but optimized for Java usage. 9 | */ 10 | object JCheck { 11 | def classpath = Check.classpath 12 | 13 | def customClasspath(path: String) = Check.customClasspath(path) 14 | 15 | val violationFree = new HamcrestWrapper[ConstraintBuilder](Check.violationFree) 16 | } -------------------------------------------------------------------------------- /check/src/main/scala/de/schauderhaft/degraph/check/constraints.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.check 2 | import de.schauderhaft.degraph.model.Node 3 | import de.schauderhaft.degraph.graph.Graph 4 | import de.schauderhaft.degraph.model.SimpleNode 5 | import de.schauderhaft.degraph.graph.SliceSource 6 | import scalax.collection.edge.LkDiEdge 7 | import scalax.collection.mutable.{ Graph => SGraph } 8 | import scalax.collection.GraphPredef.anyToNode 9 | import scalax.collection.mutable.{ Graph => SGraph } 10 | import de.schauderhaft.degraph.configuration.Configuration 11 | import de.schauderhaft.degraph.configuration.Constraint 12 | import de.schauderhaft.degraph.configuration.ConstraintViolation 13 | import scala.annotation.varargs 14 | 15 | /** 16 | * provides a DSLish method of creating Layer instances 17 | */ 18 | object Layer { 19 | @varargs def anyOf(es: String*) = LenientLayer(es: _*) 20 | @varargs def oneOf(es: String*) = StrictLayer(es: _*) 21 | } 22 | 23 | object JLayer { 24 | @varargs def anyOf(es: String*) = LenientLayer(es: _*) 25 | @varargs def oneOf(es: String*) = StrictLayer(es: _*) 26 | } 27 | 28 | /** 29 | * one or more slices making up an architectural layer, i.e. a group of slices all constraint in the same way. 30 | */ 31 | sealed trait Layer { 32 | def contains(elem: String): Boolean 33 | def denyDependenciesWithinLayer: Boolean 34 | } 35 | 36 | abstract class SimpleLayer(es: String*) extends Layer { 37 | private[this] val eSet = es.toSet 38 | def contains(elem: String): Boolean = eSet.contains(elem) 39 | } 40 | 41 | /** 42 | * a layer where the elements of that layer may depend on each other 43 | */ 44 | case class LenientLayer(es: String*) extends SimpleLayer(es: _*) { 45 | val denyDependenciesWithinLayer = false 46 | } 47 | 48 | /** 49 | * a layer where the elements of that may NOT depend on each other 50 | */ 51 | case class StrictLayer(es: String*) extends SimpleLayer(es: _*) { 52 | val denyDependenciesWithinLayer = true 53 | } 54 | 55 | /** 56 | * a constraint that applies only to a certain sliceType. 57 | * 58 | */ 59 | trait SlicedConstraint extends Constraint { 60 | def sliceType: String 61 | 62 | /** 63 | * a predicate determining if a given node pair violates this constraint. 64 | * 65 | * Will get called with nodes from the slice type of this constraint 66 | */ 67 | def isViolatedBy(n1: Node, n2: Node): Boolean 68 | 69 | protected def slices: IndexedSeq[Layer] 70 | protected def indexOf(n: Node) = n match { 71 | case sn: SimpleNode => slices.indexWhere(_.contains(sn.name)) 72 | case _ => throw new IllegalStateException("Sorry, I thought this would never happen, please report a bug including the callstack") 73 | } 74 | 75 | protected def constraitContainsBothNodes(i: Int, j: Int) = 76 | i >= 0 && j >= 0 77 | 78 | /** 79 | * implemented in such a way that all dependencies of the specified slice type will be iterated and @link isViolatedBy called for each dependency 80 | */ 81 | def violations(ss: SliceSource): Set[ConstraintViolation] = { 82 | val sg = ss.slice(sliceType) 83 | val deps = 84 | (for { 85 | eT <- sg.edges.toSeq 86 | e = eT.value 87 | if (isViolatedBy(e._1, e._2)) 88 | } yield (e._1.value, e._2.value)) 89 | 90 | if (deps.isEmpty) 91 | Set() 92 | else 93 | Set(ConstraintViolation(sliceType, shortString, deps: _*)) 94 | 95 | } 96 | 97 | private def layersToString(ls: Iterable[String], start: String, end: String) = 98 | if (ls.size == 1) ls.head 99 | else ls.mkString(start, ", ", end) 100 | 101 | protected val arrow: String 102 | 103 | /** 104 | * String representations of the constraint 105 | * @return 106 | */ 107 | def shortString = 108 | (for { 109 | l <- slices 110 | s = l match { 111 | case LenientLayer(es @ _*) => layersToString(es, "(", ")") 112 | case StrictLayer(es @ _*) => layersToString(es, "[", "]") 113 | case _ => l.toString() 114 | } 115 | } yield s).mkString(" %s ".format(arrow)) 116 | 117 | } 118 | 119 | /** 120 | * each layer (A) mentioned in this constraint may only depend on a layer (B) when A comes before B 121 | * 122 | * Dependencies from and to slices not represented by a layer aren't constraint by this constraint 123 | */ 124 | case class LayeringConstraint(sliceType: String, slices: IndexedSeq[Layer]) extends SlicedConstraint { 125 | def isViolatedBy(n1: Node, n2: Node) = { 126 | val i1 = indexOf(n1) 127 | val i2 = indexOf(n2) 128 | constraitContainsBothNodes(i1, i2) && 129 | (i1 > i2 || 130 | (n1 != n2 && i1 == i2 && slices(i1).denyDependenciesWithinLayer)) 131 | } 132 | 133 | val arrow = "->" 134 | } 135 | 136 | /** 137 | * each layer (A) mentioned in this constraint may only depend on a layer (B) when A comes directly before B 138 | */ 139 | case class DirectLayeringConstraint(sliceType: String, slices: IndexedSeq[Layer]) extends SlicedConstraint { 140 | def isViolatedBy(n1: Node, n2: Node) = { 141 | val i1 = indexOf(n1) 142 | val i2 = indexOf(n2) 143 | (constraitContainsBothNodes(i1, i2) && 144 | (i1 > i2 || 145 | i2 - i1 > 1 || (n1 != n2 && i1 == i2 && slices(i1).denyDependenciesWithinLayer))) || 146 | (i1 < 0 && i2 > 0) || 147 | (i1 >= 0 && i1 < slices.size - 1 && i2 < 0) 148 | } 149 | 150 | val arrow = "=>" 151 | } 152 | 153 | -------------------------------------------------------------------------------- /check/src/main/scala/de/schauderhaft/degraph/check/hamcrest/HamcrestWrapper.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.check.hamcrest 2 | 3 | import org.scalatest.matchers.{ BeMatcher => SMatcher } 4 | import org.hamcrest.TypeSafeMatcher 5 | import org.hamcrest.Description 6 | 7 | case class HamcrestWrapper[T](sMatcher: SMatcher[T]) extends TypeSafeMatcher[T] { 8 | override def matchesSafely(item: T): Boolean = sMatcher(item).matches 9 | override def describeMismatchSafely(item: T, mismatchDescription: Description): Unit = 10 | mismatchDescription.appendText(sMatcher(item).failureMessage) 11 | override def describeTo(description: Description): Unit = description.appendValue(sMatcher.getClass().getName()) 12 | } -------------------------------------------------------------------------------- /check/src/test/java/de/schauderhaft/degraph/check/JavaCheckApiTest.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.check; 2 | 3 | import de.schauderhaft.degraph.configuration.NamedPattern; 4 | import static de.schauderhaft.degraph.check.JCheck.*; 5 | 6 | import org.junit.Test; 7 | 8 | import static org.hamcrest.core.Is.*; 9 | 10 | import static org.junit.Assert.assertThat; 11 | import static de.schauderhaft.degraph.check.Check.classpath; 12 | 13 | public class JavaCheckApiTest { 14 | 15 | @Test 16 | public void canAccessClasspathConfigurationBuilder() { 17 | classpath(); 18 | } 19 | 20 | @Test 21 | public void canProvideCustomClasspath() { 22 | customClasspath("foo:bar"); 23 | } 24 | 25 | @Test 26 | public void canAccessNoJars() { 27 | classpath().noJars(); 28 | } 29 | 30 | @Test 31 | public void canDefineSlices() { 32 | classpath().withSlicing("blah", "whatever"); 33 | } 34 | 35 | @Test 36 | public void canUseAllow() { 37 | classpath().withSlicing("blah", "whatever").allow("a", "b", "c"); 38 | } 39 | 40 | @Test 41 | public void canUseAllowDirect() { 42 | classpath().withSlicing("blah", "whatever").allowDirect("a", "b", "c"); 43 | } 44 | 45 | @Test 46 | public void canUseAnyOf() { 47 | JLayer.anyOf("b", "c", "d"); 48 | } 49 | 50 | @Test 51 | public void canUseOneOf() { 52 | JLayer.oneOf("b", "c", "d"); 53 | } 54 | 55 | @Test 56 | public void degraphHonoursItsConstraintsJavaStyle() { 57 | assertThat( 58 | classpath() 59 | .including("de.schauderhaft.**") 60 | .withSlicing("part", "de.schauderhaft.*.(*).**") 61 | .withSlicing( 62 | "lib", 63 | "de.schauderhaft.**(Test)", 64 | new NamedPattern("main", "de.schauderhaft.*.**")) 65 | .withSlicing( 66 | "internalExternal", 67 | new NamedPattern("internal", 68 | "de.schauderhaft.**"), 69 | new NamedPattern("external", "**")), 70 | is(violationFree())); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /check/src/test/scala/de/schauderhaft/degraph/check/ConstraintBuilderTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.check 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.junit.JUnitRunner 5 | import org.scalatest.Matchers._ 6 | import org.scalatest.FunSuite 7 | import de.schauderhaft.degraph.configuration.UnnamedPattern 8 | import de.schauderhaft.degraph.configuration.NamedPattern 9 | import de.schauderhaft.degraph.configuration.Configuration 10 | import java.util.Collections 11 | import scala.util.Random 12 | import org.scalatest.prop.Tables.Table 13 | import org.scalatest.prop.TableDrivenPropertyChecks 14 | 15 | @RunWith(classOf[JUnitRunner]) 16 | class ConstraintBuilderTest extends FunSuite with TableDrivenPropertyChecks { 17 | 18 | test("simple Pattern ends up In Configuration") { 19 | val conf = ConstraintBuilder().withSlicing("x", "a.(*).**").configuration 20 | conf.categories should be(Map("x" -> Seq(UnnamedPattern("a.(*).**")))) 21 | } 22 | 23 | test("named Pattern ends up In Configuration") { 24 | val conf = ConstraintBuilder().withSlicing("type", ("name", "pattern")).configuration 25 | conf.categories should be(Map("type" -> Seq(NamedPattern("name", "pattern")))) 26 | } 27 | 28 | test("noJars configuration contains only directory based entries, single Jar gets removed") { 29 | val cb = ConstraintBuilder(config = new Configuration(classpath = Some("x.jar"))) 30 | cb.noJars.configuration.classpath should be(Some("")) 31 | } 32 | 33 | test("noJars configuration contains only directory based entries, single path gets preserved") { 34 | val cb = ConstraintBuilder(config = new Configuration(classpath = Some("x"))) 35 | cb.noJars.configuration.classpath should be(Some("x")) 36 | } 37 | 38 | test("noJars configuration contains only directory based entries, single path gets preserved when path ends in slash") { 39 | val cb = ConstraintBuilder(config = new Configuration(classpath = Some("x/"))) 40 | cb.noJars.configuration.classpath should be(Some("x/")) 41 | } 42 | 43 | test("noJars configuration contains only directory based entries, single path gets preserved when path ends in backslash") { 44 | val cb = ConstraintBuilder(config = new Configuration(classpath = Some("""x\"""))) 45 | cb.noJars.configuration.classpath should be(Some("""x\""")) 46 | } 47 | 48 | test("filterClasspath leaves only matching entries in class path - single entry"){ 49 | val cb = ConstraintBuilder(config = new Configuration(classpath = Some("""xyz\"""))) 50 | cb.filterClasspath("x*").configuration.classpath should be(Some("""xyz\""")) 51 | cb.filterClasspath("a*").configuration.classpath should be(Some("")) 52 | } 53 | 54 | test("filterClasspath leaves only matching entries in class path - multiple entries"){ 55 | val sep = System.getProperty("path.separator") 56 | 57 | val cb = ConstraintBuilder(config = new Configuration(classpath = Some("xyz.jar" + sep + "abc.jar"))) 58 | cb.filterClasspath("x*.jar").configuration.classpath should be(Some("""xyz.jar""")) 59 | } 60 | 61 | test("noJars configuration contains only directory based entries, path entries from multiple entries get preserved") { 62 | 63 | val pathSeperator = System.getProperty("path.separator") 64 | val r = new Random(0) 65 | 66 | val pathElements = Table("alpha", "beta/gamma", """x\y""") 67 | val jarElements = Table("this.jar", "beta/SomeOther.jar") 68 | val path = r.shuffle(pathElements ++ jarElements).mkString(pathSeperator) 69 | 70 | val cb = ConstraintBuilder(config = new Configuration(classpath = Some(path))) 71 | forAll(pathElements) { p => 72 | cb.noJars.configuration.classpath.get should include(p) 73 | } 74 | forAll(jarElements) { p => 75 | cb.noJars.configuration.classpath.get should not include (p) 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /check/src/test/scala/de/schauderhaft/degraph/check/CycleFreeTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.check 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.Matchers._ 5 | import org.scalatest.FunSuite 6 | import org.scalatest.junit.JUnitRunner 7 | import de.schauderhaft.degraph.check.NodeTestUtil.n 8 | import de.schauderhaft.degraph.graph.Graph 9 | import de.schauderhaft.degraph.model.SimpleNode.classNode 10 | import de.schauderhaft.degraph.model.SimpleNode.packageNode 11 | import de.schauderhaft.degraph.slicer.PackageCategorizer 12 | import de.schauderhaft.degraph.slicer.ParallelCategorizer 13 | import de.schauderhaft.degraph.model.SimpleNode 14 | import de.schauderhaft.degraph.graph.Graph 15 | import org.junit.runner.RunWith 16 | import org.scalatest.junit.JUnitRunner 17 | import de.schauderhaft.degraph.configuration.CycleFree 18 | 19 | @RunWith(classOf[JUnitRunner]) 20 | class CycleFreeTest extends FunSuite { 21 | import ConstraintViolationTestUtil._ 22 | 23 | test("an empty graph has no cycles") { 24 | val g = new Graph() 25 | CycleFree.violations(g) should be(Set()) 26 | } 27 | 28 | test("a graph with two cyclic dependent nodes without slices does not report those as cycles") { 29 | val g = new Graph() 30 | g.connect(n("a"), n("b")) 31 | g.connect(n("b"), n("a")) 32 | CycleFree.violations(g) should be(Set()) 33 | } 34 | 35 | test("a graph with a cyclic dependency between two packages returns dependencies between those packages as cyclic") { 36 | val g = new Graph(PackageCategorizer) 37 | g.connect(classNode("de.p1.A1"), classNode("de.p2.B2")) 38 | g.connect(classNode("de.p2.B1"), classNode("de.p3.C2")) 39 | g.connect(classNode("de.p3.C1"), classNode("de.p1.A2")) 40 | 41 | dependenciesIn(CycleFree.violations(g)) should be(Set( 42 | (packageNode("de.p1"), packageNode("de.p2")), 43 | (packageNode("de.p2"), packageNode("de.p3")), 44 | (packageNode("de.p3"), packageNode("de.p1")))) 45 | } 46 | 47 | test("detecting package cycles works with combined slices") { 48 | val g = new Graph(new ParallelCategorizer(PackageCategorizer, _ => SimpleNode("tld", "de"))) 49 | g.connect(classNode("de.p1.A1"), classNode("de.p2.B2")) 50 | g.connect(classNode("de.p2.B1"), classNode("de.p3.C2")) 51 | g.connect(classNode("de.p3.C1"), classNode("de.p1.A2")) 52 | 53 | dependenciesIn(CycleFree.violations(g)) should be(Set( 54 | (packageNode("de.p1"), packageNode("de.p2")), 55 | (packageNode("de.p2"), packageNode("de.p3")), 56 | (packageNode("de.p3"), packageNode("de.p1")))) 57 | } 58 | 59 | test("a graph with a cyclic dependency between two sets of packages returns dependencies between those packages as cyclic") { 60 | val g = new Graph(PackageCategorizer) 61 | g.connect(classNode("de.p1.A1"), classNode("de.p2.B2")) 62 | g.connect(classNode("de.p2.B1"), classNode("de.p3.C2")) 63 | g.connect(classNode("de.p3.C1"), classNode("de.p1.A2")) 64 | g.connect(classNode("com.p1.A1"), classNode("com.p2.B2")) 65 | g.connect(classNode("com.p2.B1"), classNode("com.p1.C2")) 66 | 67 | dependenciesIn(CycleFree.violations(g)) should be(Set( 68 | (packageNode("de.p1"), packageNode("de.p2")), 69 | (packageNode("de.p2"), packageNode("de.p3")), 70 | (packageNode("de.p3"), packageNode("de.p1")), 71 | (packageNode("com.p2"), packageNode("com.p1")), 72 | (packageNode("com.p1"), packageNode("com.p2")))) 73 | } 74 | 75 | } -------------------------------------------------------------------------------- /check/src/test/scala/de/schauderhaft/degraph/check/DependencyTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.check 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.junit.JUnitRunner 5 | import org.scalatest.Matchers._ 6 | import org.scalatest.FunSuite 7 | import Check._ 8 | 9 | @RunWith(classOf[JUnitRunner]) 10 | class DependencyTest extends FunSuite { 11 | 12 | test("Degraph honors its constraints") { 13 | classpath.noJars.including("de.schauderhaft.**"). 14 | withSlicing("part", "de.schauderhaft.*.(*).**"). 15 | withSlicing("lib", "de.schauderhaft.**(Test)", 16 | ("main", "de.schauderhaft.*.**")). 17 | withSlicing("internalExternal", ("internal", "de.schauderhaft.**"), ("external", "**")) should be(violationFree) 18 | } 19 | 20 | test("Check identifies cycles in junit") { 21 | classpath.printOnFailure("junitDependencyFailure.graphml").including("**.junit.**") should not be (violationFree) 22 | } 23 | 24 | test("Degraph has no cycles") { 25 | classpath.noJars.including("de.schauderhaft.**") should be(violationFree) 26 | } 27 | } -------------------------------------------------------------------------------- /check/src/test/scala/de/schauderhaft/degraph/check/DirectLayeringConstraintTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.check 2 | 3 | import org.scalatest.junit.JUnitRunner 4 | import org.scalatest.Matchers._ 5 | import org.junit.runner.RunWith 6 | import org.scalatest.FunSuite 7 | import de.schauderhaft.degraph.model.SimpleNode 8 | import de.schauderhaft.degraph.configuration.ConstraintViolation 9 | import org.junit.runner.RunWith 10 | import org.scalatest.junit.JUnitRunner 11 | 12 | object ConstraintViolationTestUtil { 13 | def dependenciesIn(v: Set[ConstraintViolation]) = v.flatMap(_.dependencies.toSet) 14 | } 15 | 16 | @RunWith(classOf[JUnitRunner]) 17 | class DirectLayeringConstraintTest extends FunSuite { 18 | import ConstraintViolationTestUtil._ 19 | 20 | val c = DirectLayeringConstraint("t", IndexedSeq(LenientLayer("a"), LenientLayer("b"), LenientLayer("c"))) 21 | 22 | test("simple violation free graph returns empty Set of violations") { 23 | c.violations(MockSliceSource("t", "a" -> "b", "b" -> "c")) should be(Set()) 24 | } 25 | 26 | test("it's not ok to skip layers") { 27 | dependenciesIn(c.violations(MockSliceSource("t", "a" -> "c"))) should be( 28 | Set((SimpleNode("t", "a"), SimpleNode("t", "c")))) 29 | } 30 | 31 | test("reverse dependency is reported as a violation") { 32 | dependenciesIn(c.violations(MockSliceSource("t", "b" -> "a"))) should 33 | be(Set((SimpleNode("t", "b"), SimpleNode("t", "a")))) 34 | } 35 | test("dependencies in other layers are ignored") { 36 | c.violations(MockSliceSource("x", "b" -> "a")) should be(Set()) 37 | } 38 | 39 | test("dependency to unknown is ok when from last") { 40 | c.violations(MockSliceSource("t", "c" -> "x")) should be(Set()) 41 | } 42 | 43 | test("dependency from unknown is ok when to first") { 44 | c.violations(MockSliceSource("t", "x" -> "a")) should be(Set()) 45 | } 46 | test("dependency to unknown is not ok when from middle") { 47 | dependenciesIn(c.violations(MockSliceSource("t", "b" -> "x"))) should 48 | be(Set((SimpleNode("t", "b"), SimpleNode("t", "x")))) 49 | } 50 | 51 | test("dependency from unknown is not ok when to middle") { 52 | dependenciesIn(c.violations(MockSliceSource("t", "x" -> "b"))) should 53 | be(Set((SimpleNode("t", "x"), SimpleNode("t", "b")))) 54 | } 55 | 56 | test("shortString with single element layers") { 57 | DirectLayeringConstraint("type", IndexedSeq(StrictLayer("a"), LenientLayer("b"))). 58 | shortString should be("a => b") 59 | } 60 | test("shortString with multi element layers") { 61 | DirectLayeringConstraint("type", IndexedSeq(StrictLayer("a", "x"), LenientLayer("b", "y"))). 62 | shortString should be("[a, x] => (b, y)") 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /check/src/test/scala/de/schauderhaft/degraph/check/LayeringConstraintTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.check 2 | 3 | import org.scalatest.junit.JUnitRunner 4 | import org.scalatest.Matchers._ 5 | import org.junit.runner.RunWith 6 | import org.scalatest.FunSuite 7 | import de.schauderhaft.degraph.model.SimpleNode 8 | import ConstraintViolationTestUtil._ 9 | import org.junit.runner.RunWith 10 | import org.scalatest.junit.JUnitRunner 11 | 12 | @RunWith(classOf[JUnitRunner]) 13 | class LayeringConstraintTest extends FunSuite { 14 | val c = LayeringConstraint("t", IndexedSeq(LenientLayer("a"), LenientLayer("b"), LenientLayer("c"))) 15 | 16 | test("simple violation free graph returns empty Set of violations") { 17 | c.violations(MockSliceSource("t", "a" -> "b", "b" -> "c")) should be(Set()) 18 | } 19 | 20 | test("it's ok to skip layers") { 21 | c.violations(MockSliceSource("t", "a" -> "c")) should be(Set()) 22 | } 23 | 24 | test("reverse dependency is reported as a violation") { 25 | dependenciesIn(c.violations(MockSliceSource("t", "b" -> "a"))) should 26 | be(Set((SimpleNode("t", "b"), SimpleNode("t", "a")))) 27 | } 28 | test("dependencies in other layers are ignored") { 29 | c.violations(MockSliceSource("x", "b" -> "a")) should be(Set()) 30 | } 31 | 32 | test("dependency to unknown is ok when from last") { 33 | c.violations(MockSliceSource("t", "c" -> "x")) should be(Set()) 34 | } 35 | 36 | test("dependency from unknown is ok when to first") { 37 | c.violations(MockSliceSource("t", "x" -> "a")) should be(Set()) 38 | } 39 | test("dependency to unknown is ok when from middle") { 40 | c.violations(MockSliceSource("t", "b" -> "x")) should be(Set()) 41 | } 42 | 43 | test("dependency from unknown is ok when to middle") { 44 | c.violations(MockSliceSource("t", "x" -> "b")) should be(Set()) 45 | } 46 | 47 | test("shortString with single element layers") { 48 | LayeringConstraint("type", IndexedSeq(StrictLayer("a"), LenientLayer("b"))). 49 | shortString should be("a -> b") 50 | } 51 | test("shortString with multi element layers") { 52 | LayeringConstraint("type", IndexedSeq(StrictLayer("a", "x"), LenientLayer("b", "y"))). 53 | shortString should be("[a, x] -> (b, y)") 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /check/src/test/scala/de/schauderhaft/degraph/check/MockSliceSource.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.check 2 | import de.schauderhaft.degraph.graph.SliceSource 3 | import scalax.collection.edge.LkDiEdge 4 | import scalax.collection.mutable.{ Graph => SGraph } 5 | import de.schauderhaft.degraph.model.Node 6 | import de.schauderhaft.degraph.graph.Graph 7 | import de.schauderhaft.degraph.model.SimpleNode 8 | import scalax.collection.mutable.{Graph => SGraph} 9 | import scalax.collection.mutable.Graph.apply$default$3 10 | 11 | case class MockSliceSource(slice: String, deps: (String, String)*) extends SliceSource { 12 | implicit val factory = scalax.collection.edge.LkDiEdge 13 | val graph = SGraph[Node, LkDiEdge]() 14 | for ((a, b) <- deps) 15 | graph.addLEdge(SimpleNode(slice, a), SimpleNode(slice, b))(Graph.references) 16 | def slices = Set(slice) 17 | def slice(name: String) = if (name == slice) graph else SGraph[Node, LkDiEdge]() 18 | } -------------------------------------------------------------------------------- /check/src/test/scala/de/schauderhaft/degraph/check/MultiElementLenientLayerConstraintTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.check 2 | 3 | import org.scalatest.junit.JUnitRunner 4 | import org.scalatest.Matchers._ 5 | import org.junit.runner.RunWith 6 | import org.scalatest.FunSuite 7 | import org.junit.runner.RunWith 8 | import org.scalatest.junit.JUnitRunner 9 | 10 | @RunWith(classOf[JUnitRunner]) 11 | class MultiElementLenientLayerConstraintTest extends FunSuite { 12 | val paramTupel = ("t", IndexedSeq(LenientLayer("a"), LenientLayer("b", "c", "d"), LenientLayer("e"))) 13 | val cons = Seq(LayeringConstraint.tupled(paramTupel), 14 | DirectLayeringConstraint.tupled(paramTupel)) 15 | 16 | for (c <- cons) { 17 | test("dependencies into a multielement layer are ok %s".format(c.getClass())) { 18 | c.violations(MockSliceSource("t", "a" -> "b", "a" -> "d")) should be(Set()) 19 | } 20 | 21 | test("dependencies from a multielement layer are ok %s".format(c.getClass())) { 22 | c.violations(MockSliceSource("t", "b" -> "e", "d" -> "e")) should be(Set()) 23 | } 24 | test("dependencies within a multielement layer are ok %s".format(c.getClass())) { 25 | c.violations(MockSliceSource("t", "b" -> "c", "b" -> "d")) should be(Set()) 26 | } 27 | 28 | test("self dependencies within a multielement layer are ok %s".format(c.getClass())) { 29 | c.violations(MockSliceSource("t", "b" -> "b")) should be(Set()) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /check/src/test/scala/de/schauderhaft/degraph/check/MultiElementStrictLayerConstraintTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.check 2 | 3 | import org.scalatest.junit.JUnitRunner 4 | import org.scalatest.Matchers._ 5 | import org.junit.runner.RunWith 6 | import org.scalatest.FunSuite 7 | import de.schauderhaft.degraph.model.SimpleNode 8 | import ConstraintViolationTestUtil.dependenciesIn 9 | import org.junit.runner.RunWith 10 | import org.scalatest.junit.JUnitRunner 11 | 12 | @RunWith(classOf[JUnitRunner]) 13 | class MultiElementStrictLayerConstraintTest extends FunSuite { 14 | import ConstraintViolationTestUtil._ 15 | 16 | val paramTupel = ("t", IndexedSeq(StrictLayer("a"), StrictLayer("b", "c", "d"), StrictLayer("e"))) 17 | val cons = Seq(LayeringConstraint.tupled(paramTupel), 18 | DirectLayeringConstraint.tupled(paramTupel)) 19 | 20 | for (c <- cons) { 21 | test("dependencies into a multielement layer are ok %s".format(c.getClass())) { 22 | c.violations(MockSliceSource("t", "a" -> "b", "a" -> "d")) should be(Set()) 23 | } 24 | 25 | test("dependencies from a multielement layer are ok %s".format(c.getClass())) { 26 | c.violations(MockSliceSource("t", "b" -> "e", "d" -> "e")) should be(Set()) 27 | } 28 | 29 | test("dependencies within a multielement layer are not ok %s".format(c.getClass())) { 30 | dependenciesIn(c.violations(MockSliceSource("t", "b" -> "c", "b" -> "d"))) should be(Set( 31 | (SimpleNode("t", "b"), SimpleNode("t", "c")), 32 | (SimpleNode("t", "b"), SimpleNode("t", "d")))) 33 | } 34 | 35 | test("inverse dependencies within a multielement layer are not ok %s".format(c.getClass())) { 36 | dependenciesIn(c.violations(MockSliceSource("t", "c" -> "b", "d" -> "b"))) should be(Set( 37 | (SimpleNode("t", "c"), SimpleNode("t", "b")), 38 | (SimpleNode("t", "d"), SimpleNode("t", "b")))) 39 | } 40 | 41 | test("self dependencies within a multielement layer are ok %s".format(c.getClass())) { 42 | c.violations(MockSliceSource("t", "b" -> "b")) should be(Set()) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /check/src/test/scala/de/schauderhaft/degraph/check/NodeTestUtil.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.check 2 | 3 | import de.schauderhaft.degraph.model.SimpleNode 4 | 5 | object NodeTestUtil { 6 | def n(s: String) = SimpleNode(s, s) 7 | } -------------------------------------------------------------------------------- /check/src/test/scala/de/schauderhaft/degraph/check/hamcrest/HamcrestWrapperTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.check.hamcrest 2 | 3 | import org.scalatest.Matchers._ 4 | import org.junit.runner.RunWith 5 | import org.scalatest.FunSuite 6 | import org.scalatest.junit.JUnitRunner 7 | import org.scalatest.matchers.{ BeMatcher => SMatcher } 8 | import org.hamcrest.{ Matcher => HMatcher } 9 | import org.scalatest.matchers.MatchResult 10 | import org.scalatest.prop.PropertyChecks 11 | import org.hamcrest.Description 12 | import org.hamcrest.StringDescription 13 | 14 | @RunWith(classOf[JUnitRunner]) 15 | class HamcrestWrapperTest extends FunSuite { 16 | def sMatcher(result: Boolean) = new SMatcher[String]() { 17 | override def apply(left: String) = MatchResult(result, "failed", "notFailed") 18 | } 19 | test("the wrapper converts a Scalatest Matcher to a Hamcrest Matcher") { 20 | val hMatcher: HMatcher[String] = HamcrestWrapper(sMatcher(true)) // won't compile if the assumption does not hold 21 | } 22 | 23 | for (r <- Set(true, false)) 24 | test("Scalatest Matcher and HamcrestMatcher yield the same return Value (%b)".format(r)) { 25 | HamcrestWrapper(sMatcher(r)).matchesSafely("x") should be(r) 26 | } 27 | 28 | test("HamcrestMatcher yields the failure message of not matching") { 29 | val d = new StringDescription() 30 | HamcrestWrapper(sMatcher(true)).describeMismatchSafely("x", d) 31 | d.toString should be("failed") 32 | } 33 | 34 | test("selfdescription contains the class Name of the matcher ... not smart, but I don't know anything better") { 35 | val d = new StringDescription() 36 | HamcrestWrapper(sMatcher(true)).describeTo(d) 37 | d.toString should include("HamcrestWrapperTest") 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/analysis/AnalyzerLike.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.analysis 2 | 3 | import de.schauderhaft.degraph.graph.Graph 4 | import de.schauderhaft.degraph.model.Node 5 | 6 | /** 7 | * trait abstracting over who or what provides dependencies. 8 | * 9 | * Usage of this trait instead of it single current implementation 10 | * allows the implementation later to be replaced by something else, 11 | * like analyzers providing Spring or database dependencies. 12 | */ 13 | trait AnalyzerLike { 14 | def analyze( 15 | sourceFolder: String, 16 | categorizer: Node => Node, 17 | filter: Node => Boolean): Graph 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/analysis/IncludeExcludeFilter.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.analysis 2 | 3 | import de.schauderhaft.degraph.model.Node 4 | 5 | /** 6 | * returns true if the include set is empty or at least one element returns true, 7 | * and all of the exclude elements returns false. 8 | */ 9 | class IncludeExcludeFilter( 10 | include: Set[Node => Boolean], 11 | exclude: Set[Node => Boolean]) 12 | extends (Node => Boolean) { 13 | 14 | override def apply(x: Node) = { 15 | include_?(x) && !exclude.exists(_(x)) 16 | } 17 | 18 | private def include_?(x: Node) = 19 | if (include.isEmpty) 20 | true 21 | else include.exists(_(x)) 22 | } -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/analysis/NoSelfReference.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.analysis 2 | 3 | import de.schauderhaft.degraph.model.Node 4 | 5 | /** 6 | * a filter that prevents dependencies from a node to itself or to one of its parent 7 | */ 8 | class NoSelfReference(category: Node => Node = x => x) extends (((Node, Node)) => Boolean) { 9 | def apply(t: (Node, Node)): Boolean = { 10 | !( 11 | findInCategory(t._1, t._2) || 12 | findInCategory(t._2, t._1)) 13 | } 14 | 15 | private def findInCategory(a: Node, b: Node): Boolean = { 16 | val next = category(a) 17 | a == b || (next != a && findInCategory(next, b)) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/analysis/asm/Analyzer.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.analysis.asm 2 | 3 | import de.schauderhaft.degraph.analysis.{NoSelfReference, AnalyzerLike} 4 | import de.schauderhaft.degraph.model.Node 5 | import de.schauderhaft.degraph.graph.Graph 6 | import org.objectweb.asm._ 7 | import java.io.{File, BufferedInputStream, FileInputStream} 8 | import java.util.zip.ZipFile 9 | 10 | object Analyzer extends AnalyzerLike { 11 | 12 | def analyze(sourceFolder: String, categorizer: (Node) => Node, filter: (Node) => Boolean): Graph = { 13 | val g = new Graph(categorizer, filter, new NoSelfReference(categorizer)) 14 | 15 | def readStream(reader: ClassReader, name: String): Unit = { 16 | try { 17 | reader.accept(new GraphBuildingClassVisitor(g), 0) 18 | } catch { 19 | case e: Exception => 20 | println("Something went wrong when analyzing " + name) 21 | println("For this class some or all dependencies will be missing.") 22 | println("This is most likely due to a bug in the JDK (no, really) or ASM,") 23 | println("see https://github.com/schauder/degraph/issues/68 for details.") 24 | println("If the stacktrace below does not match what you see in the issue above,") 25 | println("please create a new issue and include the stacktrace.") 26 | e.printStackTrace() 27 | } 28 | } 29 | 30 | def analyze(f: File) = { 31 | if (f.getName.endsWith(".class")) { 32 | val reader = new ClassReader(new BufferedInputStream(new FileInputStream(f))) 33 | readStream(reader, f.getName) 34 | } else { 35 | val zipFile = new ZipFile(f) 36 | val entries = zipFile.entries() 37 | while (entries.hasMoreElements) { 38 | val e = entries.nextElement() 39 | if (e.getName.endsWith(".class")) { 40 | val reader = new ClassReader(zipFile.getInputStream(e)) 41 | readStream(reader, e.getName) 42 | } 43 | } 44 | } 45 | } 46 | 47 | for (folder <- sourceFolder.split(System.getProperty("path.separator"))) { 48 | val fileFinder = new FileFinder(folder) 49 | val files = fileFinder.find() 50 | for {f <- files} { 51 | analyze(f) 52 | } 53 | } 54 | 55 | g 56 | } 57 | 58 | 59 | } 60 | 61 | -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/analysis/asm/FileFinder.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.analysis.asm 2 | 3 | import java.io.{FileFilter, File, FilenameFilter} 4 | 5 | class FileFinder(val rootPath: String) { 6 | private def singleDirFind(root: File): Set[File] = { 7 | if (root.isDirectory()) { 8 | val files = root.list(ClassFileFilter).toSet 9 | files.map(new File(root.getCanonicalPath(), _)) ++ expandDirs(root.listFiles(DirFilter).toSet) 10 | } else 11 | Set(root.getAbsoluteFile).filter( 12 | (f: File) => ClassFileFilter.accept(f.getParentFile, f.getName) 13 | ) 14 | } 15 | 16 | private def expandDirs(files: Set[File]) = 17 | files.flatMap(singleDirFind(_)) 18 | 19 | def find(): Set[File] = 20 | singleDirFind(new File(rootPath)) 21 | } 22 | 23 | object ClassFileFilter extends FilenameFilter { 24 | override def accept(f: File, n: String) = 25 | n.endsWith(".class") || 26 | n.endsWith(".jar") 27 | } 28 | 29 | object DirFilter extends FileFilter { 30 | def accept(pathname: File): Boolean = pathname.isDirectory 31 | } -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/configuration/Configuration.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.configuration 2 | 3 | import de.schauderhaft.degraph.analysis.{AnalyzerLike, IncludeExcludeFilter} 4 | import de.schauderhaft.degraph.configuration.Slicer.toSlicer 5 | import de.schauderhaft.degraph.model.Node 6 | import de.schauderhaft.degraph.slicer.MultiCategorizer.combine 7 | import de.schauderhaft.degraph.slicer.{CombinedSlicer, InternalClassCategorizer, PackageCategorizer, ParallelCategorizer, PatternMatchingFilter} 8 | 9 | 10 | /** 11 | * represents all the information configurable in commandline arguments and configuration files for Degraph. 12 | */ 13 | case class Configuration( 14 | classpath: Option[String] = None, 15 | includes: Seq[String] = Seq(), 16 | excludes: Seq[String] = Seq(), 17 | categories: Map[String, Seq[Pattern]] = Map(), 18 | output: PrintConfiguration = NoPrinting(), 19 | constraint: Set[Constraint] = Set(CycleFree), 20 | analyzer: AnalyzerLike = null 21 | ) { 22 | 23 | lazy val slicing = buildCategorizer(categories) 24 | 25 | def createGraph() = 26 | analyzer.analyze(classpath.get, slicing, buildFilter(includes, excludes)) 27 | 28 | def valid = classpath.isDefined && output != NoPrinting() 29 | 30 | private[this] def buildFilter( 31 | includes: Seq[String], 32 | excludes: Seq[String] 33 | ) = { 34 | new IncludeExcludeFilter( 35 | includes.map((x: String) => new PatternMatchingFilter(x)).toSet, 36 | excludes.map((x: String) => new PatternMatchingFilter(x)).toSet 37 | ) 38 | } 39 | 40 | private[this] def buildCategorizer(categories: Map[String, Seq[Pattern]]): (AnyRef => Node) = { 41 | val slicers = for {(level, patterns) <- categories} 42 | yield buildCategorizer(level, patterns) 43 | val slicersWithPackages = new ParallelCategorizer(PackageCategorizer +: slicers.toSeq: _*) 44 | combine(InternalClassCategorizer, slicersWithPackages) 45 | } 46 | 47 | private[this] def buildCategorizer(slicing: String, groupings: Seq[Pattern]): (AnyRef => Node) = 48 | new CombinedSlicer(groupings.map(toSlicer(slicing, _)): _*) 49 | 50 | override def toString = { 51 | def line(l: String, v: String) = l + " = " + v + "%n".format() 52 | 53 | def lineO(l: String, c: Option[String]) = c match { 54 | case None => "" 55 | case Some(s) => line(l, s) 56 | } 57 | 58 | def lineS(l: String, c: Iterable[String]) = 59 | if (c.isEmpty) "" 60 | else line(l, c.mkString(", ")) 61 | 62 | "Configuration{%s%s%s%s%s%s}".format( 63 | lineO("classpath", classpath), 64 | lineS("includes", includes), 65 | lineS("excludes", excludes), 66 | lineS("categories", categories.map(t => t._1 + " " + t._2.mkString(", "))), 67 | line( 68 | "output", output match { 69 | case NoPrinting() => "" 70 | case p: Print => p.path 71 | } 72 | ), 73 | lineS("constraints", constraint.map(_.shortString)) 74 | ) 75 | } 76 | } 77 | 78 | sealed trait Pattern { 79 | def pattern: String 80 | } 81 | 82 | case class UnnamedPattern(val pattern: String) extends Pattern 83 | 84 | case class NamedPattern(val pattern: String, name: String) extends Pattern { 85 | if (name.contains("*") || name.contains(".")) { 86 | println("You use '" + name + "' as the NAME of a NamedPattern. Please note that the NAME comes last in such a pattern.") 87 | } 88 | } 89 | 90 | -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/configuration/Constraint.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.configuration 2 | 3 | import de.schauderhaft.degraph.graph.SliceSource 4 | import de.schauderhaft.degraph.model.Node 5 | import scalax.collection.mutable.{ Graph => SGraph } 6 | import scalax.collection.edge.LkDiEdge 7 | import de.schauderhaft.degraph.graph.Graph 8 | 9 | /** 10 | * constraints the allowed dependencies of a dependency graph. 11 | */ 12 | trait Constraint { 13 | /** 14 | * returns a set of (Node, Node) tuple representing violations of the constraint. 15 | * The nodes may refer to nodes of a slice, thus representing more than a single dependency in the orginal graph. 16 | */ 17 | def violations(ss: SliceSource): Iterable[ConstraintViolation] 18 | def shortString: String 19 | } 20 | 21 | case class ConstraintViolation(sliceType: String, name: String, dependencies: (Node, Node)*) { 22 | override def toString = { 23 | val formattedDeps = dependencies 24 | .map(t => "%n " + t._1.name + " -> " + t._2.name) 25 | .mkString("").format() 26 | "[%s](%s):%s".format(sliceType, name, formattedDeps) 27 | } 28 | } 29 | 30 | /** 31 | * constraints the graph to be cycle free for all slice types 32 | */ 33 | object CycleFree extends Constraint { 34 | private def cyclicDependencies(sg: SGraph[Node, LkDiEdge]) = { 35 | def iter(sg: SGraph[Node, LkDiEdge], cyclicDependencies: Set[(Node, Node)]): Set[(Node, Node)] = { 36 | val newDeps = for { 37 | s <- sg.findCycle.toList 38 | e <- s.edges 39 | } yield (e.edge._1.value, e.edge._2.value) 40 | 41 | if (newDeps.isEmpty) cyclicDependencies 42 | else iter(sg -- newDeps.map((d: (Node, Node)) => LkDiEdge.newEdge[Node, String](d, Graph.references)), cyclicDependencies ++ newDeps) 43 | } 44 | 45 | iter(sg, Set()) 46 | } 47 | 48 | def violations(ss: SliceSource) = { 49 | val edges = (for { 50 | st <- ss.slices 51 | if (st != "Class") 52 | cycDeps = cyclicDependencies(ss.slice(st)) 53 | if (!cycDeps.isEmpty) 54 | } yield ConstraintViolation(st, shortString, 55 | cycDeps.map(d => (d._1, d._2)).toSeq: _*)).toSet 56 | 57 | edges 58 | } 59 | 60 | def shortString = "no cycles" 61 | } -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/configuration/PrintConfiguration.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.configuration 2 | 3 | /** 4 | * configures what and how to print analysis results. 5 | * 6 | * Printing refers to writing the result to a graphml file 7 | */ 8 | sealed trait PrintConfiguration { 9 | def foreach(f: (String) => Unit) 10 | 11 | /** merges this and a second PrintConfiguration into a new one */ 12 | def merge(other: PrintConfiguration): PrintConfiguration 13 | def isDefined: Boolean 14 | } 15 | 16 | /** 17 | * Nothing will be printed 18 | */ 19 | case class NoPrinting() extends PrintConfiguration { 20 | override def merge(other: PrintConfiguration): PrintConfiguration = other 21 | 22 | override val isDefined = false 23 | 24 | override def foreach(f: (String) => Unit): Unit = () 25 | } 26 | 27 | 28 | /** 29 | * Printing will happen to the given path if onFailureOnly is false or a constraint violation happend 30 | * @param path where to write the result to. 31 | * @param onConstraintViolationOnly if result should be written to graphml only when a constraint was violated 32 | */ 33 | case class Print( 34 | path: String, 35 | onConstraintViolationOnly: Boolean = false 36 | ) extends PrintConfiguration { 37 | 38 | 39 | override def merge(other: PrintConfiguration): PrintConfiguration = { 40 | def isConstraintViolationOnly(other: PrintConfiguration): Boolean = 41 | other.isInstanceOf[Print] && other.asInstanceOf[Print].onConstraintViolationOnly 42 | copy( 43 | onConstraintViolationOnly = onConstraintViolationOnly && isConstraintViolationOnly(other) 44 | ) 45 | } 46 | 47 | override val isDefined = true 48 | 49 | override def foreach(f: (String) => Unit): Unit = f(path) 50 | } -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/configuration/Slicer.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.configuration 2 | 3 | import de.schauderhaft.degraph.slicer.NamedPatternMatchingCategorizer 4 | import de.schauderhaft.degraph.slicer.PatternMatchingCategorizer 5 | 6 | object Slicer { 7 | def toSlicer(s: String, p: Pattern) = p match { 8 | case NamedPattern(p, n) => NamedPatternMatchingCategorizer(s, p, n) 9 | case UnnamedPattern(p) => PatternMatchingCategorizer(s, p) 10 | } 11 | } -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/graph/Graph.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.graph 2 | 3 | import scalax.collection.mutable.{ Graph => SGraph } 4 | import scalax.collection.GraphPredef._ 5 | import scalax.collection.GraphEdge._ 6 | import scalax.collection.edge.Implicits._ 7 | import scalax.collection.edge.LkDiEdge 8 | import de.schauderhaft.degraph.model.SimpleNode 9 | import de.schauderhaft.degraph.model.ParentAwareNode 10 | import de.schauderhaft.degraph.model.Node 11 | 12 | object Graph { 13 | val contains = "contains" 14 | val references = "references" 15 | } 16 | 17 | trait SliceSource { 18 | def slices: Iterable[String] 19 | def slice(name: String): SGraph[Node, LkDiEdge] 20 | } 21 | 22 | trait HierarchicGraph { 23 | def topNodes: Set[Node] 24 | def contentsOf(group: Node): Set[Node] 25 | def connectionsOf(node: Node): Set[Node] 26 | def allNodes: Set[Node] 27 | } 28 | 29 | /** 30 | * a special graph for gathering and organizing dependencies in a hirachical fashion. 31 | * 32 | * Argument is a category which is by default the identity. A category is a function that returns an outer node for any node and the node itself if no out node is available 33 | */ 34 | class Graph(category: Node => Node = (x) => x, 35 | filter: Node => Boolean = _ => true, 36 | edgeFilter: ((Node, Node)) => Boolean = _ => true) extends SliceSource with HierarchicGraph { 37 | 38 | import Graph._ 39 | 40 | private val internalGraph = SGraph[Node, LkDiEdge]() 41 | 42 | def topNodes: Set[Node] = 43 | for { 44 | n <- internalGraph.nodes.toSet 45 | if (n.incoming.forall(_.label != contains)) // only the nodes not contained inside another one 46 | } yield n.value 47 | 48 | private def connectedNodes(node: Node, connectionType: String): Set[Node] = 49 | for { 50 | n <- internalGraph.find(node).toSet[internalGraph.NodeT] 51 | e <- n.outgoing 52 | if (e.label == connectionType) 53 | } yield e._2.value 54 | 55 | def contentsOf(group: Node): Set[Node] = connectedNodes(group, contains) 56 | 57 | def connectionsOf(node: Node): Set[Node] = connectedNodes(node, references) 58 | 59 | def add(node: Node) = if (filter(node)) unfilteredAdd(node) 60 | 61 | private def unfilteredAdd(node: Node) : Unit = { 62 | val cat = category(node) 63 | if (cat == node) { 64 | internalGraph += node 65 | } else { 66 | addNodeToSlice(node, cat) 67 | unfilteredAdd(cat) 68 | } 69 | } 70 | 71 | def connect(a: Node, b: Node) = { 72 | addEdge(a, b) 73 | add(a) 74 | add(b) 75 | } 76 | 77 | def allNodes: Set[Node] = internalGraph.nodes.map(_.value).toSet 78 | 79 | def slice(name: String) : SGraph[Node, LkDiEdge]= { 80 | 81 | def sliceNodes = internalGraph.nodes.map(_.value).collect { case n: SimpleNode if (n.nodeType == name) => n } 82 | 83 | val sliceNodeFinder = new SliceNodeFinder(name, internalGraph) 84 | 85 | val sliceGraph = SGraph[Node, LkDiEdge]() 86 | sliceNodes.foreach(sliceGraph.add(_)) 87 | 88 | //--------------- 89 | implicit val factory = scalax.collection.edge.LkDiEdge 90 | val edges = internalGraph.edges 91 | for { 92 | e <- edges 93 | if (e.label == references) 94 | s1 <- sliceNodeFinder.lift(e._1.value) 95 | s2 <- sliceNodeFinder.lift(e._2.value) 96 | } sliceGraph.addLEdge(s1, s2)(references) 97 | 98 | sliceGraph 99 | } 100 | 101 | private def addEdge(a: Node, b: Node) = { 102 | implicit val factory = scalax.collection.edge.LkDiEdge 103 | if (filter(a) && filter(b) && edgeFilter(a, b)) 104 | internalGraph.addLEdge(a, b)(references) 105 | } 106 | 107 | private def addNodeToSlice(node: Node, cat: Node) = { 108 | implicit val factory = scalax.collection.edge.LkDiEdge 109 | internalGraph.addLEdge(cat, node)(contains) 110 | } 111 | 112 | def slices = internalGraph.nodes.flatMap(_.types) 113 | 114 | private def edgesInCycles: Set[(Node, Node)] = { 115 | val edges = (for { 116 | st <- slices 117 | s <- slice(st).findCycle.toList 118 | e <- s.edges 119 | } yield (e.edge._1.value, e.edge._2.value)).toSet 120 | 121 | edges 122 | } 123 | } 124 | 125 | /** 126 | * for a graph containing the ParentAwareNodes representing the cross product of various slices types, the SliceNodeFinder finds the nodes representing a single slice type 127 | */ 128 | class SliceNodeFinder(slice: String, graph: SGraph[Node, LkDiEdge]) extends PartialFunction[Node, SimpleNode] { 129 | 130 | private def contains(pan: ParentAwareNode): Boolean = 131 | pan.vals.exists { 132 | case n: SimpleNode if n.nodeType == slice => true 133 | case _ => false 134 | } 135 | 136 | private def findIn(pan: ParentAwareNode): SimpleNode = 137 | pan.vals.collectFirst { case n: SimpleNode if n.nodeType == slice => n }.get 138 | 139 | private def container(n: Node) = { 140 | val containers = for { 141 | in <- graph.find(n).toSeq 142 | ie <- in.incoming 143 | if ie.label == Graph.contains 144 | } yield ie._1.value 145 | containers.headOption 146 | } 147 | 148 | def isDefinedAt(n: Node): Boolean = n match { 149 | case node: SimpleNode if node.nodeType == slice => true 150 | case pan: ParentAwareNode if contains(pan) => true 151 | case _ => container(n) match { 152 | case Some(c) => isDefinedAt(c) 153 | case _ => false 154 | } 155 | } 156 | def apply(n: Node): SimpleNode = n match { 157 | case node: SimpleNode if node.nodeType == slice => node 158 | case pan: ParentAwareNode => findIn(pan) 159 | case _ => apply(container(n).get) 160 | } 161 | } -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/java/Categorizer.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.java 2 | 3 | import de.schauderhaft.degraph.model.Node 4 | 5 | /** 6 | * A Categorizer groups nodes in categories. If a node does not belong to any category, the Categorizer should just return the node itself. 7 | * 8 | * Use Categorizer only when you use the library from Java (i.e. JavaGraph). If you use Scala just use a normal Function as parameter to Graph. 9 | * 10 | * See JavaApiTest#writeGraphMlFromGraphExampl for example usage. 11 | */ 12 | trait Categorizer { 13 | def categoryOf(node: Object): Node 14 | } -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/java/JavaGraph.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.java 2 | 3 | import java.util.HashSet 4 | import de.schauderhaft.degraph.graph.Graph 5 | import scala.collection.JavaConverters._ 6 | import scala.xml.XML 7 | import de.schauderhaft.degraph.writer.Writer 8 | import de.schauderhaft.degraph.model.Node 9 | import java.util.{ Set => JSet } 10 | 11 | /** 12 | * a class intendent to use with java, so it skips on all the fancy Scala stuff. 13 | * 14 | * 15 | * See JavaApiTest#writeGraphMlFromGraphExampl for example usage. 16 | */ 17 | class JavaGraph(graph: Graph) extends JavaHierarchicGraph { 18 | def this() = this(new Graph) 19 | 20 | def this(categorizer: Categorizer) = this(new Graph(categorizer.categoryOf _)) 21 | 22 | def allNodes(): JSet[Node] = graph.allNodes.asJava 23 | 24 | def topNodes(): JSet[Node] = graph.topNodes.asJava 25 | 26 | def add(node: Node): Unit = graph.add(node) 27 | 28 | def connectionsOf(node: Node): java.util.Set[Node] = graph.connectionsOf(node).asJava 29 | 30 | def connect(a: Node, b: Node): Unit = graph.connect(a, b) 31 | def save(fileName: String) = { 32 | XML.save(fileName, (new Writer()).toXml(graph), "UTF-8", true, null) 33 | } 34 | def contentsOf(node: Node): java.util.Set[Node] = graph.contentsOf(node).asJava 35 | } 36 | 37 | -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/java/JavaHierarchicGraph.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.java 2 | 3 | import de.schauderhaft.degraph.graph.HierarchicGraph 4 | import de.schauderhaft.degraph.model.Node 5 | import java.util.{ Set => JSet } 6 | 7 | /** 8 | * an interface for easy Java interoperation with the Degraph Graph calss / the HierarchicGraph trait. 9 | * 10 | * This interface should be sufficient when creating some kind of visual representation of a Degraph Graph. 11 | */ 12 | trait JavaHierarchicGraph { 13 | 14 | /** 15 | * the root nodes of the graph, i.e. the nodes visible when all nodes are collapsed 16 | */ 17 | def topNodes: JSet[Node] 18 | 19 | /** 20 | * all the nodes that are inside the node given as a parameter. If the parameter represents an empty node and empty Set is returned 21 | */ 22 | def contentsOf(group: Node): JSet[Node] 23 | 24 | /** 25 | * all the nodes the argument node depends on. Only produces dependencies of the actual dependend node. 26 | * 27 | * If A contains B and B depends on C 28 | * 29 | * * connectionsOf A will be empty 30 | * * connectionsOf B returns Set(C) 31 | */ 32 | def connectionsOf(node: Node): JSet[Node] 33 | 34 | /** 35 | * all the nodes of the graph regardless of their dependency relationship. 36 | */ 37 | def allNodes: JSet[Node] 38 | } -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/model/SimpleNode.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.model 2 | 3 | /** 4 | * companion object providing factory methods for often needed kinds of Node instances. 5 | */ 6 | object SimpleNode { 7 | final val classType = "Class" 8 | final val packageType = "Package" 9 | def classNode(name: String) = SimpleNode(classType, name) 10 | def packageNode(name: String) = SimpleNode(packageType, name) 11 | } 12 | 13 | sealed trait Node { 14 | def contains(n: Node): Boolean 15 | def types: Set[String] 16 | def name: String 17 | } 18 | 19 | /** 20 | * represents a node in the dependency graph. 21 | */ 22 | case class SimpleNode( 23 | nodeType: String, 24 | name: String) extends Node { 25 | 26 | def contains(n: Node) = (this == n) 27 | def types = Set(nodeType) 28 | } 29 | 30 | case class ParentAwareNode(vals: Node*) extends Node { 31 | def prune = if (vals.size == 1) vals.head else this 32 | def next = if (vals.size > 1) new ParentAwareNode(vals.tail: _*) else this 33 | def head = vals.head 34 | 35 | def contains(n: Node) = (vals.exists(_.contains(n))) 36 | def types = vals.flatMap(_.types).toSet 37 | def name = vals.map(_.name).mkString(" x ") 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/slicer/CombinedSlicer.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.slicer 2 | 3 | import de.schauderhaft.degraph.model.Node 4 | 5 | class CombinedSlicer(slicer: (AnyRef => Node)*) extends (AnyRef => Node) { 6 | def apply(n: AnyRef): Node = slicer.foldLeft(n.asInstanceOf[Node])((x, s) => if (x != n) x else s(n)) 7 | } -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/slicer/InternalClassCategorizer.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.slicer 2 | import de.schauderhaft.degraph.model.SimpleNode 3 | import de.schauderhaft.degraph.model.SimpleNode._ 4 | import de.schauderhaft.degraph.model.Node 5 | 6 | /** 7 | * categorizes an Inner class as content of its outer class 8 | */ 9 | object InternalClassCategorizer extends Function1[AnyRef, Node] { 10 | override def apply(any: AnyRef): Node = { 11 | any match { 12 | case cn: SimpleNode if (cn.nodeType == classType && cn.name.contains("$")) => 13 | SimpleNode(classType, cn.name.split("""\$""")(0)) 14 | case _ => any.asInstanceOf[Node] 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/slicer/MultiCategorizer.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.slicer 2 | 3 | import de.schauderhaft.degraph.model.Node 4 | 5 | /** 6 | * combines multiple Categorizers to a single one by applying one after the other until one succeeds to categorize the node. 7 | */ 8 | object MultiCategorizer { 9 | def combine(categorizer: (AnyRef => Node)*): AnyRef => Node = { 10 | x: AnyRef => 11 | next(categorizer, x) 12 | } 13 | 14 | private def next(categorizer: Seq[AnyRef => Node], x: AnyRef): Node = { 15 | if (categorizer.isEmpty) 16 | x.asInstanceOf[Node] 17 | else { 18 | val cat = categorizer.head(x) 19 | if (cat != x) cat 20 | else 21 | next(categorizer.tail, x) 22 | } 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/slicer/NamedPatternMatchingCategorizer.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.slicer 2 | 3 | import de.schauderhaft.degraph.model.SimpleNode 4 | import de.schauderhaft.degraph.model.Node 5 | 6 | /** 7 | * categorizes package nodes by matching them against ant like patterns. 8 | * 9 | * * matches a node with any name not containing dots 10 | * * letters and dots match those letters and dots respectively 11 | * ** matches any combination of letters and dots. 12 | * 13 | * The category of a matched node it that part of the match that is wrapped in parenthesis. 14 | * 15 | * If no parenthesis are give the full name is returned as a category. 16 | * 17 | * examples: 18 | * 19 | * de.**.test matches de..test, de.some.test and de.some.other.test 20 | * 21 | * de.*.test matches from the examples given above only de.some.test 22 | * 23 | * 24 | * de.(*.test) categorizes de.some.test as 'some.test' 25 | * de.(*).test categorizes it as 'some' 26 | */ 27 | case class NamedPatternMatchingCategorizer(targetType: String, pattern: String, name: String) 28 | extends (AnyRef => Node) { 29 | private[this] val matcher = new PatternMatcher(pattern) 30 | 31 | override def apply(x: AnyRef): Node = x match { 32 | case n: SimpleNode => matcher.matches(n.name).map(_ => SimpleNode(targetType, name)).getOrElse(n) 33 | case _ => x.asInstanceOf[Node] 34 | } 35 | } -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/slicer/PackageCategorizer.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.slicer 2 | 3 | import de.schauderhaft.degraph.model.SimpleNode 4 | import de.schauderhaft.degraph.model.SimpleNode._ 5 | import de.schauderhaft.degraph.model.Node 6 | 7 | /** 8 | * categorizes a java node as member of the matching package node 9 | */ 10 | object PackageCategorizer extends Function1[AnyRef, Node] { 11 | def apply(value: AnyRef) = { 12 | value match { 13 | case SimpleNode(t, n) if (t == classType) => packageNode(packagePart(n)) 14 | case _ => value.asInstanceOf[Node] 15 | } 16 | } 17 | 18 | def packagePart(name: String) = { 19 | val lastDotIndex = name.lastIndexOf('.') 20 | if (lastDotIndex >= 0) 21 | name.substring(0, lastDotIndex) 22 | else 23 | name 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/slicer/ParallelCategorizer.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.slicer 2 | 3 | import de.schauderhaft.degraph.model.ParentAwareNode 4 | import de.schauderhaft.degraph.model.Node 5 | 6 | class ParallelCategorizer(cs: (AnyRef => Node)*) extends (AnyRef => Node) { 7 | def apply(x: AnyRef): Node = x match { 8 | case pan: ParentAwareNode => pan.next 9 | case _ => 10 | cs match { 11 | case _ if (cs.isEmpty) => x.asInstanceOf[Node] 12 | case _ => ParentAwareNode(cs.map(_(x)): _*).prune 13 | } 14 | } 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/slicer/PatternMatcher.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.slicer 2 | 3 | class PatternMatcher(pattern: String) { 4 | private[this] val Pattern = ensureParens(escapeStars(escapeDots(pattern))).r 5 | 6 | def matches(name: String) = { 7 | name match { 8 | case Pattern(matchedGroup) => Some(matchedGroup) 9 | case _ => None 10 | } 11 | } 12 | 13 | private[this] def escapeStars(p: String) = { 14 | if (p.contains("***")) throw new IllegalArgumentException("More than two '*'s in a row is not a supported pattern.") 15 | val doubleStarPlaceHolder = getPlaceHolder(p) 16 | val singleStarPlaceHolder = getPlaceHolder(p + doubleStarPlaceHolder) 17 | p. 18 | replace("**", doubleStarPlaceHolder). 19 | replace("*", singleStarPlaceHolder). 20 | replace(doubleStarPlaceHolder, """.*"""). 21 | replace(singleStarPlaceHolder, """[^\.]*""") 22 | } 23 | 24 | private[this] def getPlaceHolder(pattern: String) = 25 | ((1).toChar to (200).toChar).find(!pattern.contains(_)).head.toString 26 | 27 | private[this] def escapeDots(p: String) = p.replace(".", """\.""") 28 | 29 | private[this] def ensureParens(p: String) = 30 | if (""".*\(.*\).*""".r.findFirstIn(p).isDefined) 31 | p 32 | else 33 | "(" + p + ")" 34 | } -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/slicer/PatternMatchingCategorizer.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.slicer 2 | 3 | import de.schauderhaft.degraph.model.SimpleNode 4 | import de.schauderhaft.degraph.model.Node 5 | 6 | /** 7 | * categorizes package nodes by matching them against ant like patterns. 8 | * 9 | * * matches a node with any name not containing dots 10 | * * letters and dots match those letters and dots respectively 11 | * ** matches any combination of letters and dots. 12 | * 13 | * The category of a matched node it that part of the match that is wrapped in parenthesis. 14 | * 15 | * If no parenthesis are give the full name is returned as a category. 16 | * 17 | * examples: 18 | * 19 | * de.**.test matches de..test, de.some.test and de.some.other.test 20 | * 21 | * de.*.test matches from the examples given above only de.some.test 22 | * 23 | * 24 | * de.(*.test) categorizes de.some.test as 'some.test' 25 | * de.(*).test categorizes it as 'some' 26 | */ 27 | case class PatternMatchingCategorizer(targetType: String, pattern: String) 28 | extends (AnyRef => Node) { 29 | private[this] val matcher = new PatternMatcher(pattern) 30 | 31 | override def apply(x: AnyRef): Node = x match { 32 | case n: SimpleNode => matcher.matches(n.name).map(SimpleNode(targetType, _)).getOrElse(n) 33 | case _ => x.asInstanceOf[Node] 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/slicer/PatternMatchingFilter.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.slicer 2 | 3 | import de.schauderhaft.degraph.model.Node 4 | import de.schauderhaft.degraph.model.SimpleNode 5 | 6 | class PatternMatchingFilter(pattern: String) extends (Node => Boolean) { 7 | private val matcher = new PatternMatcher(pattern) 8 | def apply(n: Node) = n match { 9 | case sn: SimpleNode => matcher.matches(sn.name).isDefined 10 | case _ => true 11 | } 12 | } -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/writer/EdgeStyle.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.writer 2 | 3 | import java.awt.Color 4 | import java.util.Locale 5 | 6 | case class EdgeStyle(color: Color, width: Double) { 7 | 8 | private def toHex(i: Int): String = { 9 | val s = i.toHexString 10 | if (s.length == 1) "0" + s 11 | else s 12 | } 13 | 14 | val colorHexString = "#" + ( 15 | toHex(color.getRed()) + 16 | toHex(color.getGreen()) + 17 | toHex(color.getBlue())).toUpperCase() 18 | 19 | val widthString = 20 | if (width < 0.1) 21 | "0.1" 22 | else 23 | "%2.1f".formatLocal(Locale.US, width) 24 | } 25 | 26 | object DefaultEdgeStyle extends EdgeStyle(Color.BLACK, 1.0) -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/writer/Labeling.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.writer 2 | 3 | import de.schauderhaft.degraph.model.SimpleNode 4 | import de.schauderhaft.degraph.model.ParentAwareNode 5 | 6 | /** 7 | * creates a labels from an arbitrary node. It starts with the name of actual Nodes and to String if anything else ends up in the Graph. 8 | * 9 | * It then removes the part of the name that is identically with the label of the parent node if present. 10 | */ 11 | object Labeling { 12 | def apply(node: AnyRef, parent: Option[AnyRef] = None): String = { 13 | val parentLabel = parent match { 14 | case Some(p) => 15 | apply(p) 16 | case _ => 17 | baseLabel(node) 18 | } 19 | 20 | val nLabel = baseLabel(node) 21 | 22 | def removePrefix(label: String, delimiter: String) = { 23 | val prefix = parentLabel + delimiter 24 | if (prefix == label) 25 | label 26 | else if (label.startsWith(prefix)) 27 | label.replace(prefix, "") 28 | else 29 | label 30 | } 31 | 32 | removePrefix( 33 | removePrefix( 34 | removePrefix(nLabel, "$$"), 35 | "$"), 36 | ".") 37 | } 38 | 39 | private def baseLabel(node: AnyRef): String = node match { 40 | case p: ParentAwareNode => baseLabel(p.head) 41 | case n: SimpleNode => n.name 42 | case _ => node.toString 43 | } 44 | } -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/writer/PredicateStyler.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.writer 2 | 3 | import de.schauderhaft.degraph.model.Node 4 | 5 | object PredicateStyler { 6 | def styler(predicate: ((Node, Node)) => Boolean, highlight: EdgeStyle, default: EdgeStyle): (((Node, Node)) => EdgeStyle) = 7 | (t: (Node, Node)) => if (predicate(t)) highlight else default 8 | } -------------------------------------------------------------------------------- /core/src/main/scala/de/schauderhaft/degraph/writer/SlicePredicate.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.writer 2 | 3 | import de.schauderhaft.degraph.model.Node 4 | 5 | class SlicePredicate( 6 | slicing: Node => Node, 7 | edgesInCycles: Set[(Node, Node)]) 8 | extends (((Node, Node)) => Boolean) { 9 | 10 | def apply(e: (Node, Node)): Boolean = { 11 | def sliceChain(n: Node): Set[Node] = sliceChainIter(n, Set()) 12 | def sliceChainIter(n: Node, chain: Set[Node]): Set[Node] = { 13 | val s = slicing(n) 14 | if (chain.contains(s)) 15 | chain 16 | else 17 | sliceChainIter(s, chain + s) 18 | } 19 | 20 | val edgeCandidates = for { 21 | n1 <- sliceChain(e._1) 22 | n2 <- sliceChain(e._2) 23 | } yield (n1, n2) 24 | 25 | edgeCandidates.exists(e => edgesInCycles.exists(eic => e._1.contains(eic._1) && e._2.contains(eic._2))) 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /core/src/test/java/de/schauderhaft/degraph/examples/AnImplementation.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.examples; 2 | 3 | /** 4 | * Created by jeschaud on 20.05.2014. 5 | */ 6 | public class AnImplementation implements AnInterface { 7 | } 8 | -------------------------------------------------------------------------------- /core/src/test/java/de/schauderhaft/degraph/examples/AnInterface.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.examples; 2 | 3 | public interface AnInterface { 4 | } 5 | -------------------------------------------------------------------------------- /core/src/test/java/de/schauderhaft/degraph/examples/AnnoA.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.examples; 2 | 3 | public @interface AnnoA { 4 | public AnnoB value(); 5 | } 6 | -------------------------------------------------------------------------------- /core/src/test/java/de/schauderhaft/degraph/examples/AnnoB.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.examples; 2 | 3 | public @interface AnnoB { 4 | public AnnoC value(); 5 | } 6 | -------------------------------------------------------------------------------- /core/src/test/java/de/schauderhaft/degraph/examples/AnnoC.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.examples; 2 | 3 | public @interface AnnoC { 4 | } 5 | -------------------------------------------------------------------------------- /core/src/test/java/de/schauderhaft/degraph/examples/ClassWithAnnotationWithArrayOfClasses.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.examples; 2 | 3 | @SomeAnnotation({Number.class, Double.class, Float.class}) 4 | @AnnoA(@AnnoB(@AnnoC)) 5 | public class ClassWithAnnotationWithArrayOfClasses { 6 | } 7 | -------------------------------------------------------------------------------- /core/src/test/java/de/schauderhaft/degraph/examples/DependsOnArray.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.examples; 2 | 3 | public class DependsOnArray { 4 | String[] strings = null; 5 | } 6 | -------------------------------------------------------------------------------- /core/src/test/java/de/schauderhaft/degraph/examples/InstanceReferenceToStaticField.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.examples; 2 | 3 | import scala.annotation.meta.field; 4 | 5 | @SuppressWarnings("ALL") 6 | public class InstanceReferenceToStaticField { 7 | public String field; 8 | public void doSomething(){ 9 | field = StaticMethod.field; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /core/src/test/java/de/schauderhaft/degraph/examples/InstanceReferenceToStaticMethod.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.examples; 2 | 3 | @SuppressWarnings("ALL") 4 | public class InstanceReferenceToStaticMethod { 5 | public void doSomething(){ 6 | StaticMethod.method(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /core/src/test/java/de/schauderhaft/degraph/examples/SomeAnnotation.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.examples; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | @Retention(RetentionPolicy.CLASS) 7 | public @interface SomeAnnotation { 8 | public Class[] value(); 9 | } 10 | -------------------------------------------------------------------------------- /core/src/test/java/de/schauderhaft/degraph/examples/StaticMethod.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.examples; 2 | 3 | 4 | public class StaticMethod { 5 | public static String field = ""; 6 | public static void method(){ 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /core/src/test/java/de/schauderhaft/degraph/examples/StaticReferenceToStaticField.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.examples; 2 | 3 | 4 | @SuppressWarnings("ALL") 5 | public class StaticReferenceToStaticField { 6 | public static String field ; 7 | public static void doSomething(){ 8 | field = StaticMethod.field; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /core/src/test/java/de/schauderhaft/degraph/examples/StaticReferenceToStaticMethod.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.examples; 2 | 3 | @SuppressWarnings("ALL") 4 | public class StaticReferenceToStaticMethod { 5 | public static void doSomething(){ 6 | StaticMethod.method(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /core/src/test/java/de/schauderhaft/degraph/examples/UsageInMethod.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.examples; 2 | 3 | public class UsageInMethod { 4 | 5 | public void usage() { 6 | System.out.println("Hello World"); // this is a dependency to String 7 | // and System 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /core/src/test/java/de/schauderhaft/degraph/java/ChessCategory.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.java; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import de.schauderhaft.degraph.model.Node; 7 | import de.schauderhaft.degraph.model.SimpleNode; 8 | 9 | public class ChessCategory implements Categorizer { 10 | public SimpleNode asNode(String s) { 11 | return new SimpleNode("chesspiece", s); 12 | } 13 | 14 | private final Map categories = new HashMap<>(); 15 | 16 | { 17 | categories.put(asNode("Queen"), asNode("Heavy")); 18 | categories.put(asNode("Rook"), asNode("Heavy")); 19 | categories.put(asNode("Bishop"), asNode("Light")); 20 | categories.put(asNode("Knight"), asNode("Light")); 21 | categories.put(asNode("Light"), asNode("Figure")); 22 | categories.put(asNode("Heavy"), asNode("Figure")); 23 | } 24 | 25 | @Override 26 | public Node categoryOf(Object node) { 27 | Node result = categories.get(node); 28 | if (result == null) 29 | return (Node) node; 30 | else 31 | return result; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /core/src/test/java/de/schauderhaft/degraph/java/ConstantCategorizer.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.java; 2 | 3 | import de.schauderhaft.degraph.model.Node; 4 | 5 | class ConstantCategorizer implements Categorizer { 6 | 7 | private final Node fixedValue; 8 | 9 | public ConstantCategorizer(Node fixedValue) { 10 | this.fixedValue = fixedValue; 11 | } 12 | 13 | @Override 14 | public Node categoryOf(Object node) { 15 | return fixedValue; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/src/test/java/de/schauderhaft/degraph/java/JavaApiTest.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.java; 2 | 3 | import static java.util.Arrays.asList; 4 | import static org.junit.Assert.assertEquals; 5 | import static org.junit.Assert.assertTrue; 6 | 7 | import java.util.HashSet; 8 | 9 | import org.junit.Test; 10 | 11 | import de.schauderhaft.degraph.model.SimpleNode; 12 | 13 | public class JavaApiTest { 14 | private SimpleNode node(String s) { 15 | return new SimpleNode(s, s); 16 | } 17 | 18 | @Test 19 | public void aNewGraphContainsNoTopNodes() throws Exception { 20 | JavaGraph graph = new JavaGraph(); 21 | assertEquals(new HashSet(), graph.topNodes()); 22 | } 23 | 24 | @Test 25 | public void aGraphContainsTheNodesThatGetAddedToTheGraph() { 26 | JavaGraph graph = new JavaGraph(); 27 | SimpleNode node = new SimpleNode("", ""); 28 | graph.add(node); 29 | assertTrue(graph.topNodes().contains(node)); 30 | } 31 | 32 | @Test 33 | public void simpleNodesDontHaveConnections() { 34 | JavaGraph graph = new JavaGraph(); 35 | SimpleNode node = new SimpleNode("", ""); 36 | graph.add(node); 37 | 38 | assertEquals(new HashSet<>(), graph.connectionsOf(node)); 39 | 40 | } 41 | 42 | @Test 43 | public void connectionsOfANodeAreTheNodesItHasEdgesWith() { 44 | JavaGraph graph = new JavaGraph(); 45 | SimpleNode a = node("a"); 46 | SimpleNode b = node("b"); 47 | graph.connect(a, b); 48 | 49 | assertEquals(new HashSet<>(asList(b)), graph.connectionsOf(a)); 50 | } 51 | 52 | @Test 53 | public void writeGraphMlFromGraphExample() { 54 | JavaGraph graph = new JavaGraph(new ChessCategory()); 55 | graph.connect(node("King"), node("Queen")); 56 | graph.connect(node("Queen"), node("Rook")); 57 | graph.connect(node("Rook"), node("Bishop")); 58 | graph.connect(node("Rook"), node("Knight")); 59 | graph.connect(node("Knight"), node("Pawn")); 60 | graph.connect(node("Bishop"), node("Pawn")); 61 | 62 | graph.save("chess.graphml"); 63 | } 64 | 65 | @Test 66 | public void aSimpleNodeHasNoContenten() { 67 | JavaGraph graph = new JavaGraph(); 68 | SimpleNode node = new SimpleNode("", ""); 69 | graph.add(node); 70 | assertEquals(new HashSet<>(), graph.contentsOf(node)); 71 | } 72 | 73 | @Test 74 | public void categorizerGetsApplied() { 75 | JavaGraph graph = new JavaGraph(new ConstantCategorizer(new SimpleNode( 76 | "x", "x"))); 77 | SimpleNode node = new SimpleNode("", ""); 78 | graph.add(node); 79 | assertEquals(new HashSet<>(asList(node)), 80 | graph.contentsOf(new SimpleNode("x", "x"))); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /core/src/test/java/de/schauderhaft/degraph/java/NodeBuilder.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.java; 2 | 3 | import de.schauderhaft.degraph.model.Node; 4 | import de.schauderhaft.degraph.model.SimpleNode; 5 | 6 | public class NodeBuilder { 7 | 8 | private String name; 9 | private String typ; 10 | 11 | private NodeBuilder() { 12 | 13 | } 14 | 15 | public static NodeBuilder create() { 16 | return new NodeBuilder(); 17 | } 18 | 19 | public NodeBuilder name(String name) { 20 | this.name = name; 21 | return this; 22 | } 23 | 24 | public NodeBuilder typ(String typ) { 25 | this.typ = typ; 26 | return this; 27 | } 28 | 29 | public Node createSimpleNode() { 30 | Node simpleNode = new SimpleNode(typ, name); 31 | return simpleNode; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/analysis/IncludeExcludeFilterTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.analysis 2 | 3 | import org.scalatest.FunSuite 4 | import org.junit.runner.RunWith 5 | import org.scalatest.junit.JUnitRunner 6 | import de.schauderhaft.degraph.graph.NodeTestUtil.n 7 | 8 | @RunWith(classOf[JUnitRunner]) 9 | class IncludeExcludeFilterTest extends FunSuite { 10 | import org.scalatest.Matchers._ 11 | 12 | val alwaysTrue = (_: AnyRef) => true 13 | val alwaysFalse = (_: AnyRef) => false 14 | 15 | test("true whithout filter") { 16 | new IncludeExcludeFilter( 17 | Set(), Set())(n("x")) should be(true) 18 | } 19 | 20 | test("true when any include filter returns true") { 21 | new IncludeExcludeFilter( 22 | Set(alwaysFalse, alwaysTrue, alwaysFalse), Set())(n("x")) should be(true) 23 | } 24 | 25 | test("false when all include filters returns false") { 26 | new IncludeExcludeFilter( 27 | Set(alwaysFalse, alwaysFalse, alwaysFalse), Set())(n("x")) should be(false) 28 | } 29 | 30 | test("false when any exclude filter returns true") { 31 | new IncludeExcludeFilter( 32 | Set(), 33 | Set(alwaysFalse, alwaysTrue, alwaysFalse))(n("x")) should be(false) 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/analysis/NoSelfReferenceTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.analysis 2 | 3 | import org.scalatest.FunSuite 4 | import de.schauderhaft.degraph.slicer.MultiCategorizer._ 5 | import de.schauderhaft.degraph.slicer.ListCategory 6 | import de.schauderhaft.degraph.model.SimpleNode 7 | import org.junit.runner.RunWith 8 | import org.scalatest.junit.JUnitRunner 9 | import org.scalatest.Matchers._ 10 | 11 | @RunWith(classOf[JUnitRunner]) 12 | class NoSelfReferenceTest extends FunSuite { 13 | 14 | def n(s: String) = SimpleNode(s, s) 15 | 16 | test("returns true for unrelated objects") { 17 | new NoSelfReference()(n("a"), n("b")) should be(true) 18 | } 19 | 20 | test("returns false for identical objects") { 21 | new NoSelfReference()(n("a"), n("a")) should be(false) 22 | } 23 | 24 | test("returns false if second object is contained in Categories of first instance") { 25 | new NoSelfReference(ListCategory(n("a"), n("b"), n("c"), n("d")))(n("a"), n("c")) should be(false) 26 | } 27 | test("returns false if first object is con9tained in Categories of second instance") { 28 | new NoSelfReference(ListCategory(n("a"), n("b"), n("c"), n("d")))(n("c"), n("a")) should be(false) 29 | } 30 | 31 | test("returns true for unrelated objects with categories") { 32 | new NoSelfReference(ListCategory(n("a"), n("b"), n("c"), n("d")))(n("a"), n("x")) should be(true) 33 | } 34 | 35 | test("returns true for unrelated objects with common categorY") { 36 | new NoSelfReference(combine(ListCategory(n("a"), n("b")), ListCategory(n("x"), n("b"))))(n("a"), n("x")) should be(true) 37 | } 38 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/analysis/asm/FileFinderTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.analysis.asm 2 | 3 | import org.scalatest.FunSuite 4 | import org.junit.runner.RunWith 5 | import org.scalatest.junit.JUnitRunner 6 | import org.scalatest.Matchers._ 7 | import java.io.File 8 | import org.scalatest.BeforeAndAfterAll 9 | 10 | @RunWith(classOf[JUnitRunner]) 11 | class FileFinderTest extends FunSuite 12 | with BeforeAndAfterAll { 13 | 14 | val userDir = new File(System.getProperty("user.dir")) 15 | val tempDir = new File(userDir, "fileFinderTestDir") 16 | val subDir = new File(tempDir, "subdir") 17 | 18 | val filesForTest = Set("A.class", "J.jar", "T.txt") 19 | 20 | override def beforeAll() = { 21 | tempDir.mkdir() 22 | // create some Files to be used in the test 23 | for (f <- filesForTest) { 24 | new File(tempDir, f).createNewFile() 25 | } 26 | 27 | subDir.mkdir() 28 | for (f <- filesForTest) { 29 | new File(subDir, f).createNewFile() 30 | } 31 | } 32 | 33 | override def afterAll() = { 34 | for (f <- filesForTest) { 35 | new File(tempDir, f).delete() 36 | new File(subDir, f).delete() 37 | } 38 | subDir.delete() 39 | } 40 | 41 | test("FileFinder returns the file when the file is a class file") { 42 | val testFile = new File(tempDir, "A.class") 43 | new FileFinder(testFile.getAbsolutePath).find() should be(Set(testFile)) 44 | } 45 | 46 | test("FileFinder returns an empty Set if it finds nothing") { 47 | new FileFinder(new File(tempDir, "/thisPathShouldNotExist/").getAbsolutePath).find() should be(Set[File]()) 48 | } 49 | 50 | test("FileFinder should find File in user dir when searching for file in current directory") { 51 | new FileFinder(tempDir.getAbsolutePath).find() should contain(new File(tempDir, "A.class")) 52 | } 53 | 54 | test("FileFinder should only find class or jar files") { 55 | new FileFinder(tempDir.getAbsolutePath).find().foreach( 56 | f => f.getName() should (endWith(".class") or endWith(".jar"))) 57 | } 58 | 59 | test("FileFinder should find the class file") { 60 | new FileFinder(tempDir.getAbsolutePath).find() 61 | .filter(_.getName().endsWith(".class")) should not be ('empty) 62 | } 63 | 64 | test("FileFinder should find the jar file") { 65 | new FileFinder(tempDir.getAbsolutePath).find() 66 | .filter(_.getName().endsWith(".jar")) should not be ('empty) 67 | } 68 | 69 | test("FileFinder should find File in subdir dir when searching for file in current directory") { 70 | new FileFinder(tempDir.getAbsolutePath).find() should contain(new File(new File(tempDir, "subdir"), "A.class")) 71 | } 72 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/analysis/asm/GraphBuildingClassVisitorTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.analysis.asm 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.junit.JUnitRunner 5 | import org.scalatest.FunSuite 6 | import org.scalatest.Matchers._ 7 | import GraphBuildingClassVisitor._ 8 | import org.objectweb.asm.Type 9 | 10 | @RunWith(classOf[JUnitRunner]) 11 | class GraphBuildingClassVisitorTest extends FunSuite { 12 | 13 | 14 | test("no node for null") { 15 | val result = classNodeFromDescriptor(null) 16 | result.isEmpty should be(true) 17 | } 18 | 19 | test("no node for empty String") { 20 | val result = classNodeFromDescriptor("") 21 | result.isEmpty should be(true) 22 | result should be ('empty) 23 | } 24 | 25 | 26 | test("create a simple class node") { 27 | val result = GraphBuildingClassVisitor.classNodeFromSingleType("java/lang/System") 28 | result should be(classNode("java.lang.System")) 29 | } 30 | 31 | test("create a simple class node from byte code syntax") { 32 | val result = GraphBuildingClassVisitor.classNodeFromSingleType("[Ljava/lang/Object;") 33 | result should be(classNode("java.lang.Object")) 34 | } 35 | 36 | test("create a simple class node from byte code syntax of multidimensional array") { 37 | val result = GraphBuildingClassVisitor.classNodeFromSingleType("[[Ljava/lang/Object;") 38 | result should be(classNode("java.lang.Object")) 39 | } 40 | 41 | test("identifies simple class as Array") { 42 | val result = GraphBuildingClassVisitor.classNodeFromDescriptor("[Ljava/lang/String;") 43 | result should contain(classNode("java.lang.String")) 44 | } 45 | 46 | test("finds method with argument and return value") { 47 | val result = GraphBuildingClassVisitor.classNodeFromDescriptor("(ILjava/lang/String;)[Ljava/blah/Blubb;") 48 | result should contain(classNode("java.lang.String")) 49 | result should contain(classNode("java.blah.Blubb")) 50 | } 51 | test("complex example") { 52 | 53 | val result = GraphBuildingClassVisitor.classNodeFromDescriptor("Lscala/runtime/AbstractFunction1;Ljava/lang/Object;>;Lscala/Serializable;") 54 | result should contain(classNode("scala/runtime/AbstractFunction1")) 55 | result should contain(classNode("scala/Tuple2")) 56 | result should contain(classNode("java/lang/String")) 57 | result should contain(classNode("org/rogach/scallop/Scallop")) 58 | result should contain(classNode("java/lang/Object")) 59 | result should contain(classNode("scala/Serializable")) 60 | } 61 | 62 | 63 | test("inner classes") { 64 | 65 | val result = GraphBuildingClassVisitor.classNodeFromDescriptor( 66 | "(TT;)Lorg/scalautils/TripleEqualsSupport$LegacyCheckingEqualizer;") 67 | result should contain(classNode("java/lang/Object")) 68 | result should contain(classNode("org/scalautils/TripleEqualsSupport$LegacyCheckingEqualizer")) 69 | result should not contain (classNode("")) 70 | result should not contain (classNode("org/scalautils/TripleEqualsSupport$egacyCheckingEqualizer")) 71 | } 72 | 73 | 74 | test("empty names") { 75 | val result = GraphBuildingClassVisitor.classNodeFromDescriptor( 76 | "(Lorg/scalatest/prop/TableFor19;Lscala/Function19;)V") 77 | result should contain(classNode("java/lang/Object")) 78 | result should contain(classNode("org/scalatest/prop/TableFor19")) 79 | result should contain(classNode("scala/runtime/BoxedUnit")) 80 | result should not contain (classNode("")) 81 | } 82 | 83 | test("primitive types get ignored"){ 84 | val result = GraphBuildingClassVisitor.classNodeFromDescriptor("(DD)I") 85 | 86 | result should be (empty) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/analysis/test/AnalyzerTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.analysis.test 2 | 3 | import org.scalatest.FunSuite 4 | import org.scalatest.matchers.MatchResult 5 | import org.scalatest.matchers.Matcher 6 | import org.scalatest.Matchers._ 7 | import de.schauderhaft.degraph.graph.Graph 8 | import de.schauderhaft.degraph.model.SimpleNode 9 | import de.schauderhaft.degraph.model.SimpleNode._ 10 | import org.junit.runner.RunWith 11 | import org.scalatest.junit.JUnitRunner 12 | import de.schauderhaft.degraph.analysis._ 13 | 14 | @RunWith(classOf[JUnitRunner]) 15 | class AnalyzerTest extends FunSuite { 16 | private val testClassFolder = System.getProperty("java.class.path") 17 | private val graphs = Map( 18 | "asm" -> asm.Analyzer.analyze(testClassFolder, x => x, _ => true)) 19 | 20 | 21 | for ((label, graph) <- graphs) { 22 | def stringNodes = graph.topNodes.map(_.toString) 23 | 24 | def nodeByString(name: String) = graph.topNodes.find { 25 | case n: SimpleNode => n.name == name 26 | case _ => false 27 | } 28 | 29 | def test(name: String)(testFun: => Unit) = super.test("%s (%s)".format(name, label))(testFun) 30 | 31 | test("Selftest: nodeByString works") { 32 | nodeByString("java.lang.String") should be(Some(classNode("java.lang.String"))) 33 | } 34 | 35 | test("Test classes get found") { 36 | nodeByString("de.schauderhaft.degraph.examples.OtherUser") should be (Some(classNode("de.schauderhaft.degraph.examples.OtherUser"))) 37 | } 38 | 39 | 40 | test("Selftest: example classes got analyzed") { 41 | nodeByString("de.schauderhaft.degraph.examples.SubClass") should not be (None) 42 | } 43 | 44 | test("Dependency from sub to superclass is found") { 45 | graph should connect("de.schauderhaft.degraph.examples.SubClass" -> "de.schauderhaft.degraph.examples.SuperClass") 46 | } 47 | 48 | test("Dependency from class to usage in constructor is found") { 49 | graph should connect("de.schauderhaft.degraph.examples.User" -> "de.schauderhaft.degraph.examples.Token") 50 | } 51 | 52 | 53 | test("Dependency from class to interface is found") { 54 | graph should connect("de.schauderhaft.degraph.examples.AnImplementation" -> "de.schauderhaft.degraph.examples.AnInterface") 55 | } 56 | 57 | test("Dependency from class to member type is found") { 58 | graph should connect("de.schauderhaft.degraph.examples.OtherUser" -> "de.schauderhaft.degraph.examples.Token") 59 | } 60 | 61 | test("Dependency from class to annotation") { 62 | graph should connect("de.schauderhaft.degraph.examples.UsesAnnotation" -> "org.junit.runner.RunWith") 63 | } 64 | 65 | test("Dependency from class to class used in annotation") { 66 | graph should connect("de.schauderhaft.degraph.examples.UsesAnnotation" -> "de.schauderhaft.degraph.examples.MyRunner") 67 | } 68 | 69 | test("Dependency from class to class used as an array") { 70 | graph should connect("de.schauderhaft.degraph.examples.DependsOnArray" -> "java.lang.String") 71 | } 72 | 73 | test("Dependency from class to class used only inside a method") { 74 | graph should connect("de.schauderhaft.degraph.examples.UsageInMethod" -> "java.lang.String") 75 | graph should connect("de.schauderhaft.degraph.examples.UsageInMethod" -> "java.lang.System") 76 | } 77 | 78 | 79 | test("Dependency from class to type parameter of superclass") { 80 | if (label == "depFinder") pending 81 | graph should connect("de.schauderhaft.degraph.examples.MyArrayList" -> "java.lang.String") 82 | } 83 | 84 | test("Dependency from class to type parameter of member") { 85 | if (label == "depFinder") pending 86 | graph should connect("de.schauderhaft.degraph.examples.ListUser" -> "java.lang.String") 87 | } 88 | 89 | test("Dependency from class to type parameter of abstract member") { 90 | if (label == "depFinder") pending 91 | graph should connect("de.schauderhaft.degraph.examples.ListUser" -> "java.lang.String") 92 | } 93 | 94 | test("Dependency from class to enum parameter in annotation") { 95 | if (label == "depFinder") pending 96 | graph should connect ("de.schauderhaft.degraph.examples.EnumInAnnotationUser" -> "java.lang.annotation.RetentionPolicy") 97 | } 98 | 99 | test("Dependency from instance to static method") { 100 | graph should connect ("de.schauderhaft.degraph.examples.InstanceReferenceToStaticMethod" -> "de.schauderhaft.degraph.examples.StaticMethod") 101 | } 102 | 103 | test("Dependency from class to static method") { 104 | graph should connect ("de.schauderhaft.degraph.examples.StaticReferenceToStaticMethod" -> "de.schauderhaft.degraph.examples.StaticMethod") 105 | } 106 | 107 | test("Dependency from instance to static field") { 108 | graph should connect ("de.schauderhaft.degraph.examples.InstanceReferenceToStaticField" -> "de.schauderhaft.degraph.examples.StaticMethod") 109 | } 110 | 111 | test("Dependency from class to static field") { 112 | graph should connect ("de.schauderhaft.degraph.examples.StaticReferenceToStaticField" -> "de.schauderhaft.degraph.examples.StaticMethod") 113 | } 114 | 115 | test("No self references") { 116 | for ( 117 | n <- graph.topNodes; 118 | n2 <- graph.connectionsOf(n) 119 | ) n should not be n2 120 | } 121 | 122 | test("class with annotation with array of classes"){ 123 | graph should connect ("de.schauderhaft.degraph.examples.ClassWithAnnotationWithArrayOfClasses" -> "java.lang.Number") 124 | graph should connect ("de.schauderhaft.degraph.examples.ClassWithAnnotationWithArrayOfClasses" -> "java.lang.Double") 125 | graph should connect ("de.schauderhaft.degraph.examples.ClassWithAnnotationWithArrayOfClasses" -> "java.lang.Float") 126 | } 127 | 128 | test("class with annotation with annotation with annotation"){ 129 | graph should connect ("de.schauderhaft.degraph.examples.ClassWithAnnotationWithArrayOfClasses" -> "de.schauderhaft.degraph.examples.AnnoA") 130 | graph should connect ("de.schauderhaft.degraph.examples.ClassWithAnnotationWithArrayOfClasses" -> "de.schauderhaft.degraph.examples.AnnoB") 131 | graph should connect ("de.schauderhaft.degraph.examples.ClassWithAnnotationWithArrayOfClasses" -> "de.schauderhaft.degraph.examples.AnnoC") 132 | } 133 | 134 | 135 | def connect(connection: (String, String)) = { 136 | val (from, to) = connection 137 | new Matcher[Graph] { 138 | override def apply(graph: Graph) = { 139 | var messages = List[String]() 140 | val toNode = nodeByString(to) 141 | if (toNode.isEmpty) 142 | messages = "there is no node %s in the graph %s".format(to, graph) :: messages 143 | val fromNode = nodeByString(from) 144 | if (fromNode.isEmpty) 145 | messages = "there is no node %s in the graph %s".format(from, graph) :: messages 146 | else { 147 | val connections = graph.connectionsOf(fromNode.get) 148 | if (messages.isEmpty && !connections.contains(toNode.get)) 149 | messages = "there is no connection from %s to %s in %s. The only connections are %s".format(from, to, graph, connections) :: messages 150 | } 151 | new MatchResult( 152 | toNode.nonEmpty 153 | && fromNode.nonEmpty 154 | && graph.connectionsOf(fromNode.get).contains(toNode.get), 155 | messages.mkString(","), 156 | "There is a connection from %s to %s in %s".format(from, to, graph)) 157 | } 158 | } 159 | } 160 | } 161 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/configuration/ConfigurationConstraintTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.configuration 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.Matchers._ 5 | import org.scalatest.FunSuite 6 | import org.scalatest.junit.JUnitRunner 7 | 8 | @RunWith(classOf[JUnitRunner]) 9 | class ConfigurationConstraintTest extends FunSuite { 10 | 11 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/configuration/ConfigurationTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.configuration 2 | 3 | import org.scalatest.Matchers._ 4 | import org.scalatest.FunSuite 5 | import de.schauderhaft.degraph.graph.Graph 6 | import de.schauderhaft.degraph.analysis.AnalyzerLike 7 | import org.junit.runner.RunWith 8 | import org.scalatest.junit.JUnitRunner 9 | import de.schauderhaft.degraph.model.Node 10 | 11 | @RunWith(classOf[JUnitRunner]) 12 | class ConfigurationTest extends FunSuite { 13 | 14 | 15 | 16 | test("a configuration without classpath to analyze is not valid") { 17 | Configuration( 18 | None, 19 | Seq(), 20 | Seq(), 21 | Map(), 22 | Print("output")) should not be ('valid) 23 | } 24 | 25 | test("a configuration without output to analyze is not valid") { 26 | Configuration( 27 | Some("."), 28 | Seq(), 29 | Seq(), 30 | Map(), 31 | NoPrinting()) should not be ('valid) 32 | } 33 | 34 | test("a complete configuration is valid") { 35 | Configuration( 36 | Some("."), 37 | Seq(), 38 | Seq(), 39 | Map(), 40 | Print("output")) should be('valid) 41 | } 42 | 43 | private def makeRegex(s: String) = "(?s)" + s. 44 | replace("{", "\\{"). 45 | replace("}", "\\}"). 46 | replace("*", ".*") 47 | 48 | test("toString is nice and readabled for empty config") { 49 | Configuration(constraint = Set()).toString should fullyMatch regex makeRegex("Configuration{*}") 50 | } 51 | 52 | test("toString is nice and readabled for full config") { 53 | 54 | val expected = makeRegex( 55 | """Configuration{*classpath = ccc*includes = iii*excludes = eee*categories = ca*nnn*ppp*output = ooo*constraints = no cycles*}""") 56 | 57 | Configuration( 58 | classpath = Some("ccc"), 59 | includes = Seq("iii"), 60 | excludes = Seq("eee"), 61 | categories = Map("ca" -> Seq(NamedPattern("nnn", "ppp"))), 62 | output = Print("ooo"), 63 | constraint = Set(CycleFree)). 64 | toString should fullyMatch regex (expected) 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/configuration/ConstraintViolationTest.scala: -------------------------------------------------------------------------------- 1 | 2 | 3 | package de.schauderhaft.degraph.configuration 4 | 5 | import org.junit.runner.RunWith 6 | import org.scalatest.Matchers._ 7 | import org.scalatest.FunSuite 8 | import org.scalatest.junit.JUnitRunner 9 | import de.schauderhaft.degraph.graph.NodeTestUtil._ 10 | import org.junit.runner.RunWith 11 | import org.scalatest.junit.JUnitRunner 12 | import org.junit.runner.RunWith 13 | import org.scalatest.junit.JUnitRunner 14 | 15 | @RunWith(classOf[JUnitRunner]) 16 | class ConstraintViolationTest extends FunSuite { 17 | test("toString without violations") { 18 | val cv = ConstraintViolation("aSliceType", "constraintName") 19 | cv.toString should be("[aSliceType](constraintName):") 20 | } 21 | 22 | test("toString with violations") { 23 | val cv = ConstraintViolation("aSliceType", "constraintName", (n("a"), n("b")), (n("x"), n("y"))) 24 | cv.toString should be("""[aSliceType](constraintName): 25 | a -> b 26 | x -> y""") 27 | } 28 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/configuration/NamedPatternTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.configuration 2 | 3 | import java.io.{ByteArrayOutputStream, PrintStream} 4 | 5 | import org.junit.runner.RunWith 6 | import org.scalatest.junit.JUnitRunner 7 | import org.scalatest.{BeforeAndAfterEach, FunSuite, Matchers} 8 | 9 | @RunWith(classOf[JUnitRunner]) 10 | class NamedPatternTest extends FunSuite 11 | with BeforeAndAfterEach 12 | with Matchers { 13 | 14 | def fixture = { 15 | val baos = new ByteArrayOutputStream() 16 | val ps: PrintStream = new PrintStream(baos) 17 | System.setOut(ps) 18 | (ps, baos) 19 | } 20 | 21 | var originalOutputStream: PrintStream = null 22 | 23 | override def beforeEach() { 24 | originalOutputStream = System.out 25 | } 26 | 27 | override def afterEach() { 28 | System.out.flush() 29 | System.setOut(originalOutputStream) 30 | } 31 | 32 | test("warns not on correct / plausible usage") { 33 | val (ps, baos) = fixture 34 | Console.withOut(ps) { 35 | NamedPattern("pattern.with*stuff", "name") 36 | System.out.flush() 37 | baos.toString().trim should be("") 38 | } 39 | } 40 | 41 | test("warns on possible mixup of parameters (dots)") { 42 | val (ps, baos) = fixture 43 | Console.withOut(ps) { 44 | NamedPattern("name on wrong position", "pattern.with.dots") 45 | System.out.flush() 46 | baos.toString() should include("'pattern.with.dots'") 47 | } 48 | } 49 | 50 | 51 | test("warns on possible mixup of parameters (stars)") { 52 | val (ps, baos) = fixture 53 | Console.withOut(ps) { 54 | NamedPattern("name on wrong position", "pattern*with*stars") 55 | System.out.flush() 56 | baos.toString() should include("'pattern*with*stars'") 57 | } 58 | } 59 | 60 | 61 | } 62 | 63 | 64 | -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/configuration/PrintConfigurationTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.configuration 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.FunSuite 5 | import org.scalatest.Matchers._ 6 | import org.scalatest.junit.JUnitRunner 7 | 8 | 9 | @RunWith(classOf[JUnitRunner]) 10 | class PrintConfigurationTest extends FunSuite { 11 | 12 | test("merging NoPrinting with another configuration yields the other configuration") { 13 | NoPrinting().merge(Print("somewhere")) should be(Print("somewhere")) 14 | } 15 | 16 | 17 | test("merging Printing on failure only and always printing should yield always printing") { 18 | Print("somewhere", onConstraintViolationOnly = false) 19 | .merge(Print("somewhere", onConstraintViolationOnly = true)) should not be('onConstraintViolationOnly) 20 | Print("somewhere", onConstraintViolationOnly = true) 21 | .merge(Print("somewhere", onConstraintViolationOnly = false)) should not be('onConstraintViolationOnly) 22 | } 23 | 24 | test("merging two Print instnces results in the path of the first instance") { 25 | Print("a").merge(Print("b")) should have('path ("a")) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/configuration/SlicerTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.configuration 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.FunSuite 5 | import org.scalatest.junit.JUnitRunner 6 | import org.scalatest.Matchers._ 7 | import de.schauderhaft.degraph.slicer.NamedPatternMatchingCategorizer 8 | import de.schauderhaft.degraph.slicer.PatternMatchingCategorizer 9 | 10 | @RunWith(classOf[JUnitRunner]) 11 | class SlicerTest extends FunSuite { 12 | test("NamedPattern to NamedPatternCategorizer") { 13 | Slicer.toSlicer("t", NamedPattern("p", "n")) should be(new NamedPatternMatchingCategorizer("t", "p", "n")) 14 | } 15 | test("UnnamedPattern to PatternMatchingCategorizer") { 16 | Slicer.toSlicer("t", NamedPattern("p", "n")) should be(new NamedPatternMatchingCategorizer("t", "p", "n")) 17 | } 18 | test("NnamedPattern to PatternMatchingCategorizer") { 19 | Slicer.toSlicer("t", UnnamedPattern("p")) should be(new PatternMatchingCategorizer("t", "p")) 20 | } 21 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/examples/SuperClass.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.examples 2 | import org.junit.runner.RunWith 3 | import org.junit.runner.Runner 4 | import java.util.ArrayList 5 | import java.lang.annotation.{RetentionPolicy, Retention} 6 | 7 | import scala.annotation.ClassfileAnnotation 8 | 9 | /** 10 | * just a file with different dependencies for use in tests 11 | */ 12 | 13 | class Token 14 | 15 | class SuperClass 16 | class SubClass extends SuperClass 17 | 18 | class User { 19 | new Token 20 | } 21 | 22 | class OtherUser { 23 | val value = new Token 24 | } 25 | 26 | class MyArrayList extends ArrayList[String] 27 | 28 | abstract class ListUser { 29 | val listOfStrings = Vector[String]() 30 | def seqOfStrings: Seq[String] 31 | } 32 | 33 | @RunWith(classOf[MyRunner]) 34 | abstract class UsesAnnotation 35 | 36 | abstract class MyRunner extends Runner 37 | 38 | @Retention(RetentionPolicy.RUNTIME) 39 | abstract class EnumInAnnotationUser 40 | 41 | -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/graph/GraphSliceTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.graph 2 | 3 | import org.scalatest.FunSuite 4 | import org.scalatest.Matchers._ 5 | import scalax.collection.{ Graph => SGraph } 6 | import de.schauderhaft.degraph.slicer.PackageCategorizer 7 | import de.schauderhaft.degraph.model.SimpleNode._ 8 | import scalax.collection.edge.Implicits._ 9 | import de.schauderhaft.degraph.slicer.MultiCategorizer 10 | import de.schauderhaft.degraph.slicer.InternalClassCategorizer 11 | import de.schauderhaft.degraph.model.{Node, SimpleNode} 12 | import org.junit.runner.RunWith 13 | import org.scalatest.junit.JUnitRunner 14 | import scalax.collection.{ Graph => SGraph } 15 | import scalax.collection.edge.LkDiEdge 16 | 17 | @RunWith(classOf[JUnitRunner]) 18 | class GraphSliceTest extends FunSuite { 19 | 20 | test("the package slice of a graph without package is empty") { 21 | val g = new Graph() 22 | g.add(SimpleNode("x", "x")) 23 | 24 | g.slice(packageType) should be(SGraph[Node, LkDiEdge]()) 25 | } 26 | 27 | test("the package slice of a graph with some nodes in a single package is that package") { 28 | val g = new Graph(category = PackageCategorizer) 29 | g.add(classNode("p.C")) 30 | 31 | g.slice(packageType) should be(SGraph[Node, LkDiEdge](packageNode("p"))) 32 | } 33 | 34 | test("the package slice of a graph with two connected nodes in two packages will be that two packages connected") { 35 | val g = new Graph(category = PackageCategorizer) 36 | g.connect(classNode("p.one.Class"), classNode("p.two.Class")) 37 | 38 | g.slice(packageType) should be(SGraph((packageNode("p.one") ~+#> packageNode("p.two"))(Graph.references))) 39 | } 40 | 41 | test("test non existing slice") { 42 | val g = new Graph(category = PackageCategorizer) 43 | g.connect(classNode("p.one.Class"), classNode("p.two.Class")) 44 | 45 | g.slice("no such type") should be(SGraph[Node, LkDiEdge]()) 46 | } 47 | 48 | test("the package slice of an inner class is its package") { 49 | // since the slice node will appear anyway we use an edge between to inner classes, to test that they get projected on the correct slice 50 | val g = new Graph(category = MultiCategorizer.combine(InternalClassCategorizer, PackageCategorizer)) 51 | 52 | g.connect(classNode("p.one.Class$Inner"), classNode("p.two.Class$Inner")) 53 | 54 | g.slice(packageType) should be(SGraph((packageNode("p.one") ~+#> packageNode("p.two"))(Graph.references))) 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/graph/GraphTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.graph 2 | 3 | import org.junit.runner.RunWith 4 | import NodeTestUtil.n 5 | import org.scalatest.FunSuite 6 | import org.scalatest.junit.JUnitRunner 7 | import org.scalatest.Matchers._ 8 | import de.schauderhaft.degraph.slicer.MultiCategorizer._ 9 | import de.schauderhaft.degraph.slicer.ListCategory 10 | import de.schauderhaft.degraph.model.SimpleNode 11 | @RunWith(classOf[JUnitRunner]) 12 | class GraphTest extends FunSuite { 13 | 14 | test("a new graph contains no top nodes") { 15 | val g = new Graph() 16 | g.topNodes should be('empty) 17 | } 18 | 19 | test("a graph contains the nodes that get added to the graph") { 20 | val g = new Graph() 21 | val node = n("a") 22 | g.add(node) 23 | g.topNodes should contain(node.asInstanceOf[AnyRef]) 24 | } 25 | 26 | test("a simple node has no content") { 27 | val g = new Graph() 28 | val node = n("a") 29 | g.add(node) 30 | g.contentsOf(node) should be('empty) 31 | } 32 | 33 | test("if an added node is contained in a category, that category gets added") { 34 | val category = n("cat") 35 | val node = n("a") 36 | val g = new Graph(_ => category) 37 | g.add(node) 38 | g.topNodes should contain(category.asInstanceOf[AnyRef]) 39 | } 40 | 41 | test("elements of a category are contained in that category") { 42 | val g = new Graph(_ => n("x"), _ => true) 43 | val node = n("a") 44 | g.add(node) 45 | g.contentsOf(n("x")) should contain(node.asInstanceOf[AnyRef]) 46 | } 47 | 48 | test("elements of a not existing category are the empty set") { 49 | new Graph().contentsOf(n("x")) should be('empty) 50 | } 51 | 52 | test("categories that are part of other categories contain each other") { 53 | val topCategory = n("top") 54 | val subCategory = n("sub") 55 | val node = n("a") 56 | val g = new Graph(Map[AnyRef, SimpleNode](node -> subCategory).withDefaultValue(topCategory), _ => true) 57 | g.add(node) 58 | 59 | g.topNodes should equal(Set(topCategory)) 60 | g.contentsOf(topCategory) should equal(Set(subCategory)) 61 | g.contentsOf(subCategory) should equal(Set(node)) 62 | } 63 | 64 | test("edges can get added for not existing nodes") { 65 | val g = new Graph() 66 | val a = n("a") 67 | val b = n("b") 68 | g.connect(a, b) 69 | g.connectionsOf(a) should be(Set(b)) 70 | g.topNodes should be(Set(a, b)) 71 | } 72 | 73 | test("connectionsOf returns all connected nodes") { 74 | val g = new Graph() 75 | val a = n("a") 76 | val b = n("b") 77 | val c = n("c") 78 | g.connect(a, b) 79 | g.connect(a, c) 80 | g.connectionsOf(a) should be(Set(b, c)) 81 | g.topNodes should be(Set(a, b, c)) 82 | } 83 | 84 | test("simple nodes don't have connections") { 85 | val g = new Graph() 86 | val a = n("a") 87 | g.add(a) 88 | 89 | g.connectionsOf(a) should be(Set()) 90 | } 91 | 92 | test("allNodes of an empty graph is the empty Set") { 93 | val g = new Graph() 94 | g.allNodes should be(Set()) 95 | } 96 | 97 | test("allNodes of a graph without categories are the topNodes") { 98 | val g = new Graph() 99 | g.add(n("a")) 100 | g.add(n("23")) 101 | g.allNodes should be(g.topNodes) 102 | g.allNodes should be(Set(n("a"), n("23"))) 103 | } 104 | 105 | test("allNodes of a graph with categories contains the topNodes and all categories") { 106 | val g = new Graph(combine(ListCategory(n("a"), n("b"), n("c")), ListCategory(n("23"), n("42"), n("c")))) 107 | g.add(n("a")) 108 | g.add(n("23")) 109 | g.allNodes should be(Set(n("a"), n("b"), n("c"), n("23"), n("42"))) 110 | } 111 | 112 | test("categories don't get filtert") { 113 | val g = new Graph(ListCategory(n("a"), n("b")), _ == n("a")) 114 | g.add(n("a")) 115 | g.topNodes should be(Set(n("b"))) 116 | } 117 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/graph/NodeTestUtil.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.graph 2 | 3 | import de.schauderhaft.degraph.model.SimpleNode 4 | 5 | object NodeTestUtil { 6 | def n(s: String) = SimpleNode(s, s) 7 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/graph/SliceNodeFinderTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.graph 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.Matchers._ 5 | import org.scalatest.FunSuite 6 | import org.scalatest.junit.JUnitRunner 7 | import org.junit.runner.RunWith 8 | import org.scalatest.junit.JUnitRunner 9 | import scalax.collection.mutable.{ Graph => SGraph } 10 | import scalax.collection.edge.LkDiEdge 11 | import de.schauderhaft.degraph.model.SimpleNode._ 12 | import de.schauderhaft.degraph.model.ParentAwareNode 13 | import de.schauderhaft.degraph.model.SimpleNode 14 | import Graph.contains 15 | import de.schauderhaft.degraph.model.Node 16 | 17 | @RunWith(classOf[JUnitRunner]) 18 | class SliceNodeFinderTest extends FunSuite { 19 | 20 | def n(s: String) = SimpleNode(s, s) 21 | 22 | test("is not defined for an empty Graph") { 23 | val finder = new SliceNodeFinder("x", SGraph[Node, LkDiEdge]()) 24 | finder.isDefinedAt(n("z")) should be(false) 25 | } 26 | 27 | test("returns the node for a slice node") { 28 | val p = packageNode("p") 29 | val g = SGraph[Node, LkDiEdge](p) 30 | val finder = new SliceNodeFinder(packageType, g) 31 | finder.isDefinedAt(p) should be(true) 32 | finder(p) should be(p) 33 | } 34 | 35 | test("is not defined if node is of different slice") { 36 | val p = packageNode("p") 37 | val g = SGraph[Node, LkDiEdge](p) 38 | val finder = new SliceNodeFinder("does not exist", g) 39 | finder.isDefinedAt(p) should be(false) 40 | } 41 | 42 | test("returns the content of a ParentAwareNode ") { 43 | val p = packageNode("p") 44 | val n = new ParentAwareNode(p) 45 | val g = SGraph[Node, LkDiEdge](n) 46 | val finder = new SliceNodeFinder(packageType, g) 47 | finder.isDefinedAt(n) should be(true) 48 | finder(n) should be(p) 49 | } 50 | 51 | test("is not defind if ParentAwareNode does not contain correct slice") { 52 | val p = packageNode("p") 53 | val n = new ParentAwareNode(p) 54 | val g = SGraph[Node, LkDiEdge](n) 55 | val finder = new SliceNodeFinder("does not exist", g) 56 | finder.isDefinedAt(n) should be(false) 57 | } 58 | 59 | test("returns the matching slice from content of a ParentAwareNode ") { 60 | val p = packageNode("p") 61 | val n = new ParentAwareNode(SimpleNode("x", "x"), p, SimpleNode("y", "y")) 62 | val g = SGraph[Node, LkDiEdge](n) 63 | val finder = new SliceNodeFinder(packageType, g) 64 | finder.isDefinedAt(n) should be(true) 65 | finder(n) should be(p) 66 | } 67 | 68 | test("traverses contains relationship") { 69 | implicit val factory = scalax.collection.edge.LkDiEdge 70 | val p = packageNode("p") 71 | val g = SGraph[Node, LkDiEdge]() 72 | g.addLEdge(p, n("x"))(contains) 73 | val finder = new SliceNodeFinder(packageType, g) 74 | finder.isDefinedAt(n("x")) should be(true) 75 | finder(n("x")) should be(p) 76 | } 77 | 78 | test("returns correct element from slices relationship") { 79 | implicit val factory = scalax.collection.edge.LkDiEdge 80 | val p = packageNode("p") 81 | val c = classNode("p.c") 82 | val g = SGraph[Node, LkDiEdge]() 83 | g.addLEdge(p, c)(contains) 84 | val finder = new SliceNodeFinder(packageType, g) 85 | finder.isDefinedAt(c) should be(true) 86 | finder(c) should be(p) 87 | } 88 | 89 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/slicer/CombinedSlicerTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.slicer 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.FunSuite 5 | import org.scalatest.junit.JUnitRunner 6 | import org.scalatest.Matchers._ 7 | import de.schauderhaft.degraph.model.SimpleNode 8 | 9 | @RunWith(classOf[JUnitRunner]) 10 | class CombinedSlicerTest extends FunSuite { 11 | def n(s: String) = SimpleNode(s, s) 12 | 13 | test("the combined slicer of a single slicer is equivalent to the single slicer") { 14 | val slicer = new CombinedSlicer(ListCategory(n("a"), n("b"))) 15 | slicer(n("a")) should be(n("b")) 16 | slicer(n("c")) should be(n("c")) 17 | } 18 | 19 | test("when the first slicer is applicable the other slicers get ignored") { 20 | val slicer = new CombinedSlicer(ListCategory(n("a"), n("b")), ListCategory(n("c"), n("d"))) 21 | slicer(n("a")) should be(n("b")) 22 | } 23 | 24 | test("when the first slicer isn't applicable the other slicers get tried") { 25 | val slicer = new CombinedSlicer(ListCategory(n("a"), n("b")), ListCategory(n("c"), n("d"))) 26 | slicer(n("c")) should be(n("d")) 27 | } 28 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/slicer/InternalClassCategorizerTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.slicer 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.FunSuite 5 | import org.scalatest.junit.JUnitRunner 6 | import org.scalatest.Matchers._ 7 | 8 | import de.schauderhaft.degraph.model.SimpleNode.classNode 9 | 10 | @RunWith(classOf[JUnitRunner]) 11 | class InternalClassCategorizerTest extends FunSuite { 12 | val cat = InternalClassCategorizer 13 | test("categorizes a simple class as it self") { 14 | val clazzNode = classNode("de.schauderhaft.SomeClass") 15 | cat(clazzNode) should be(clazzNode) 16 | } 17 | 18 | test("categorizes a inner class as the outer class") { 19 | val innerClassNode = classNode("de.schauderhaft.SomeClass$SomeInnerClass") 20 | val outerClassNode = classNode("de.schauderhaft.SomeClass") 21 | cat(innerClassNode) should be(outerClassNode) 22 | } 23 | test("categorizes a nested inner class as the outer most class") { 24 | val innerClassNode = classNode("de.schauderhaft.SomeClass$SomeInnerClass$Of$Another$InnerClass") 25 | val outerClassNode = classNode("de.schauderhaft.SomeClass") 26 | cat(innerClassNode) should be(outerClassNode) 27 | } 28 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/slicer/ListCategory.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.slicer 2 | 3 | import de.schauderhaft.degraph.model.Node 4 | 5 | class ListCategory(list: List[Node]) extends (AnyRef => Node) { 6 | def apply(v: AnyRef) = { 7 | val i = list.indexOf(v) 8 | if (i >= 0 && i < list.size - 1) 9 | list(i + 1) 10 | else v match { 11 | case n: Node => n 12 | } 13 | } 14 | } 15 | 16 | object ListCategory { 17 | def apply(args: Node*) = new ListCategory(args.toList) 18 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/slicer/MultiCategorizerTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.slicer 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.FunSuite 5 | import org.scalatest.junit.JUnitRunner 6 | import org.scalatest.Matchers._ 7 | import MultiCategorizer.combine 8 | import de.schauderhaft.degraph.slicer.MultiCategorizer.combine 9 | import de.schauderhaft.degraph.model.SimpleNode 10 | import de.schauderhaft.degraph.model.Node 11 | 12 | @RunWith(classOf[JUnitRunner]) 13 | class MultiCategorizerTest extends FunSuite { 14 | 15 | private def n(s: String) = SimpleNode(s, s) 16 | private val id: AnyRef => Node = x => x.asInstanceOf[Node] 17 | 18 | test("combine of a single identity function returns the argument") { 19 | combine(id)(n("x")) should be(n("x")) 20 | } 21 | test("combine of a single function returns function value of the argument") { 22 | combine(ListCategory(n("a"), n("b"), n("c")))(n("b")) should be(n("c")) 23 | } 24 | 25 | test("combine of two functions returns the result of the first function if it is not equal to the argument") { 26 | combine(ListCategory(n("a"), n("b")), ListCategory(n("b"), n("c")))(n("b")) should be(n("c")) 27 | } 28 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/slicer/NamedPatternMatchingCategorizerTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.slicer 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.FunSuite 5 | import org.scalatest.junit.JUnitRunner 6 | import org.scalatest.Matchers._ 7 | 8 | import de.schauderhaft.degraph.model.SimpleNode 9 | import de.schauderhaft.degraph.model.SimpleNode.classNode 10 | 11 | @RunWith(classOf[JUnitRunner]) 12 | class NamedPatternMatchingCategorizerTest extends FunSuite { 13 | 14 | test("returns Node of type with name of match") { 15 | val categorizer = new NamedPatternMatchingCategorizer("type", "(some.package.Class)", "name") 16 | categorizer(classNode("some.package.Class")) should be(SimpleNode("type", "name")) 17 | } 18 | test("returns input when not matched") { 19 | val categorizer = new NamedPatternMatchingCategorizer("type", "(y)", "name") 20 | categorizer(classNode("x")) should be(classNode("x")) 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/slicer/PackageCategorizerTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.slicer 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.FunSuite 5 | import org.scalatest.junit.JUnitRunner 6 | import de.schauderhaft.degraph.model.SimpleNode 7 | import de.schauderhaft.degraph.model.SimpleNode._ 8 | 9 | @RunWith(classOf[JUnitRunner]) 10 | class PackageCategorizerTest extends FunSuite { 11 | import org.scalatest.Matchers._ 12 | 13 | def n(s: String) = SimpleNode(s, s) 14 | 15 | test("an arbitrary value gets categorized as an arbitrary value") { 16 | PackageCategorizer(n("alfred")) should be(n("alfred")) 17 | } 18 | 19 | test("an arbitrary Node gets categorized as an arbitrary Node") { 20 | PackageCategorizer(SimpleNode("x", "alfred")) should be(SimpleNode("x", "alfred")) 21 | } 22 | 23 | test("the category of a class node is its package node") { 24 | PackageCategorizer(classNode("some.package.Class")) should be(packageNode("some.package")) 25 | } 26 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/slicer/ParallelCategorizerTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.slicer 2 | 3 | import org.scalatest.FunSuite 4 | import org.scalatest.Matchers._ 5 | import org.junit.runner.RunWith 6 | import org.scalatest.junit.JUnitRunner 7 | import de.schauderhaft.degraph.model.ParentAwareNode 8 | import de.schauderhaft.degraph.model.SimpleNode 9 | import de.schauderhaft.degraph.model.Node 10 | 11 | @RunWith(classOf[JUnitRunner]) 12 | class ParallelCategorizerTest extends FunSuite { 13 | 14 | def n(s: String) = SimpleNode(s, s) 15 | val id = (n: AnyRef) => n.asInstanceOf[Node] 16 | 17 | test("parallel combination of zero functions returns the identity") { 18 | new ParallelCategorizer()(n("x")) should be(n("x")) 19 | } 20 | test("parallel combination of a single identity function returns the argument") { 21 | new ParallelCategorizer(id)(n("x")) should be(n("x")) 22 | } 23 | 24 | test("parallel combination of a single function returns function value of the argument") { 25 | new ParallelCategorizer(ListCategory(n("a"), n("b"), n("c")))(n("b")) should be(n("c")) 26 | } 27 | 28 | test("parallel combination of two functions returns the results of the all functions (with identity)") { 29 | new ParallelCategorizer(ListCategory(n("a"), n("b")), ListCategory(n("b"), n("c")))(n("a")) should be(ParentAwareNode(n("b"), n("a"))) 30 | } 31 | 32 | test("parallel combination of two functions returns the results of the all functions") { 33 | new ParallelCategorizer(ListCategory(n("a"), n("b")), ListCategory(n("a"), n("c")))(n("a")) should be(ParentAwareNode(n("b"), n("c"))) 34 | } 35 | 36 | test("parallel combination of two functions returns the results of the all functions, with 3 functions") { 37 | new ParallelCategorizer(ListCategory(n("a"), n("b")), ListCategory(n("a"), n("c")), ListCategory(n("a"), n("d")))(n("a")) should be(ParentAwareNode(n("b"), n("c"), n("d"))) 38 | } 39 | 40 | test("steps through elements of a PackageAwareNode") { 41 | new ParallelCategorizer()(ParentAwareNode(n("a"), n("b"), n("c"))) should be(ParentAwareNode(n("b"), n("c"))) 42 | } 43 | 44 | test("steps through elements of a PackageAwareNode (single element)") { 45 | new ParallelCategorizer()(ParentAwareNode(n("a"))) should be(ParentAwareNode(n("a"))) 46 | } 47 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/slicer/PatternMatcherTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.slicer 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.FunSuite 5 | import org.scalatest.junit.JUnitRunner 6 | import org.scalatest.Matchers._ 7 | 8 | @RunWith(classOf[JUnitRunner]) 9 | class PatternMatcherTest extends FunSuite { 10 | 11 | test("doesn't match arbitrary object") { 12 | val matcher = new PatternMatcher("(some.package.Class)") 13 | matcher.matches("x") should be(None) 14 | } 15 | 16 | test("matches fix pattern") { 17 | val matcher = new PatternMatcher("(some.package.Class)") 18 | matcher.matches("some.package.Class") should be(Some("some.package.Class")) 19 | } 20 | 21 | test("if no parens are give parens about the whole pattern are assumed") { 22 | val matcher = new PatternMatcher("some.package.Class") 23 | matcher.matches("some.package.Class") should be(Some("some.package.Class")) 24 | } 25 | 26 | test("fix pattern does not match other class") { 27 | val matcher = new PatternMatcher("(some.package.Class)") 28 | matcher.matches("some.other.package.Class") should be(None) 29 | } 30 | 31 | test("returns only matched group") { 32 | val matcher = new PatternMatcher("some.(package).Class") 33 | matcher.matches("some.package.Class") should be(Some("package")) 34 | } 35 | 36 | test("* matches arbitrary Letters ") { 37 | val matcher = new PatternMatcher("some.(*).Class") 38 | matcher.matches("some.package.Class") should be(Some("package")) 39 | matcher.matches("some.mph.Class") should be(Some("mph")) 40 | } 41 | 42 | test("* does not match dots ") { 43 | val matcher = new PatternMatcher("some.(*).Class") 44 | matcher.matches("some.pack.age.Class") should be(None) 45 | } 46 | 47 | test("dots match dots") { 48 | val matcher = new PatternMatcher("some.pack.age.Class") 49 | matcher.matches("some.pack.age.Class") should be(Some("some.pack.age.Class")) 50 | } 51 | 52 | test("dots don't match anything but dots") { 53 | val matcher = new PatternMatcher("some.pack.age.Class") 54 | matcher.matches("some.packxage.Class") should be(None) 55 | } 56 | 57 | test("** matches single package level") { 58 | val matcher = new PatternMatcher("some.(**).Class") 59 | matcher.matches("some.package.Class") should be(Some("package")) 60 | } 61 | 62 | test("** matches multiple package levels") { 63 | val matcher = new PatternMatcher("some.(**).Class") 64 | matcher.matches("some.pack.age.Class") should be(Some("pack.age")) 65 | } 66 | 67 | test("*** throws an exception") { 68 | val caught = the[IllegalArgumentException] thrownBy { 69 | val matcher = new PatternMatcher("invalid***pattern") 70 | matcher.matches("blah") 71 | } 72 | 73 | caught.getMessage() should be("More than two '*'s in a row is not a supported pattern.") 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/slicer/PatternMatchingCategorizerTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.slicer 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.FunSuite 5 | import org.scalatest.junit.JUnitRunner 6 | import org.scalatest.Matchers._ 7 | 8 | import de.schauderhaft.degraph.model.SimpleNode 9 | import de.schauderhaft.degraph.model.SimpleNode.classNode 10 | 11 | @RunWith(classOf[JUnitRunner]) 12 | class PatternMatchingCategorizerTest extends FunSuite { 13 | def n(s: String) = SimpleNode(s, s) 14 | test("doesn't match arbitrary object") { 15 | val categorizer = new PatternMatchingCategorizer("type", "(some.package.Class)") 16 | categorizer(n("x")) should be(n("x")) 17 | } 18 | 19 | test("returns only matched group") { 20 | val categorizer = new PatternMatchingCategorizer("type", "some.(package).Class") 21 | categorizer(classNode("some.package.Class")) should be(SimpleNode("type", "package")) 22 | } 23 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/slicer/PatternMatchingFilterTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.slicer 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.Matchers._ 5 | import org.scalatest.FunSuite 6 | import org.scalatest.junit.JUnitRunner 7 | import de.schauderhaft.degraph.model.SimpleNode 8 | import de.schauderhaft.degraph.model.ParentAwareNode 9 | 10 | @RunWith(classOf[JUnitRunner]) 11 | class PatternMatchingFilterTest extends FunSuite { 12 | 13 | for (st <- Set("Package", "Class", "other")) 14 | test("matches the name of Node of type %s".format(st)) { 15 | val matcher = new PatternMatchingFilter("x.*y.abc.**") 16 | 17 | matcher(SimpleNode(st, "x.y.abc.x")) should be(true) 18 | matcher(SimpleNode(st, "x.blay.abc.x.yz.D")) should be(true) 19 | matcher(SimpleNode(st, "x.x.y.abc.x")) should be(false) 20 | } 21 | 22 | test("matches ParentAwareNode") { 23 | val matcher = new PatternMatchingFilter("x.*y.abc.**") 24 | matcher(new ParentAwareNode(new SimpleNode("x", "y"), new SimpleNode("c", "b"))) should be(true) 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/writer/EdgeStyleTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.writer 2 | 3 | import org.scalatest.FunSuite 4 | import org.scalatest.Matchers._ 5 | import org.junit.runner.RunWith 6 | import org.scalatest.junit.JUnitRunner 7 | import java.awt.Color 8 | import java.awt.Color.RED 9 | 10 | @RunWith(classOf[JUnitRunner]) 11 | class EdgeStyleTest extends FunSuite { 12 | test("colorHexString represents color") { 13 | EdgeStyle(new Color(255, 255, 255), 1.0).colorHexString should be("#FFFFFF") 14 | EdgeStyle(new Color(238, 9, 1), 1.0).colorHexString should be("#EE0901") 15 | EdgeStyle(new Color(0, 0, 0), 1.0).colorHexString should be("#000000") 16 | } 17 | test("widthString represents width") { 18 | EdgeStyle(RED, 1.0).widthString should be("1.0") 19 | EdgeStyle(RED, 3.5).widthString should be("3.5") 20 | EdgeStyle(RED, 0.0).widthString should be("0.1") // not to let lines completly disapear 21 | } 22 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/writer/LabelingTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.writer 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.FunSuite 5 | import org.scalatest.junit.JUnitRunner 6 | import org.scalatest.Matchers._ 7 | import de.schauderhaft.degraph.model.SimpleNode 8 | import de.schauderhaft.degraph.model.SimpleNode._ 9 | import de.schauderhaft.degraph.model.ParentAwareNode 10 | 11 | @RunWith(classOf[JUnitRunner]) 12 | class LabelingTest extends FunSuite { 13 | 14 | test("the label of a String with parent None should be the String") { 15 | Labeling("test", None) should be("test") 16 | } 17 | 18 | test("the label of an object with parent None should be the toString of that object") { 19 | Labeling(new DummyObject("test"), None) should be("test") 20 | } 21 | 22 | test("when the parent + . is the prefix of the node it gets exluded from the label") { 23 | Labeling("prefix.suffix", Some("prefix")) should be("suffix") 24 | } 25 | 26 | test("when the parent + $ is the prefix of the node it gets exluded from the label") { 27 | Labeling("prefix$suffix", Some("prefix")) should be("suffix") 28 | } 29 | 30 | test("when the parent + $ is the node the node labe does not get change") { 31 | Labeling("prefix$", Some("prefix")) should be("prefix$") 32 | } 33 | 34 | test("when the parent + $$ is the prefix of the node it gets exluded from the label") { 35 | Labeling("prefix$$suffix", Some("prefix")) should be("suffix") 36 | } 37 | 38 | test("the label of a Node is the name of the node") { 39 | Labeling(classNode("some.Class")) should be("some.Class") 40 | } 41 | 42 | test("the label of a ParentAwareNode is the name of the first contained node") { 43 | Labeling(new ParentAwareNode(packageNode("the package"), classNode("Some Java class"))) should be("the package") 44 | } 45 | 46 | class DummyObject(override val toString: String) 47 | } 48 | 49 | -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/writer/PredicateStylerTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.writer 2 | 3 | import org.scalatest.FunSuite 4 | import org.scalatest.Matchers._ 5 | import org.junit.runner.RunWith 6 | import org.scalatest.junit.JUnitRunner 7 | import java.awt.Color 8 | import java.awt.Color.RED 9 | import de.schauderhaft.degraph.model.SimpleNode 10 | 11 | @RunWith(classOf[JUnitRunner]) 12 | class PredicateStylerTest extends FunSuite { 13 | 14 | def n(s: String) = SimpleNode(s, s) 15 | 16 | test("returns default node when predicate is false") { 17 | val style = PredicateStyler.styler( 18 | _ => false, 19 | EdgeStyle(Color.RED, 2.0), 20 | DefaultEdgeStyle) 21 | 22 | style((n("x"), n("y"))) should be(DefaultEdgeStyle) 23 | } 24 | 25 | test("returns highlight node when predicate is true") { 26 | val style = PredicateStyler.styler( 27 | _ => true, 28 | EdgeStyle(Color.RED, 2.0), 29 | DefaultEdgeStyle) 30 | 31 | style((n("x"), n("y"))) should be(EdgeStyle(Color.RED, 2.0)) 32 | } 33 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/writer/SlicePredicateTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.writer 2 | 3 | import org.junit.runner.RunWith 4 | import org.junit.runner.RunWith 5 | import org.scalatest.FunSuite 6 | import org.scalatest.junit.JUnitRunner 7 | import org.scalatest.junit.JUnitRunner 8 | import org.scalatest.Matchers._ 9 | 10 | import de.schauderhaft.degraph.graph.NodeTestUtil.n 11 | import de.schauderhaft.degraph.model.Node 12 | import de.schauderhaft.degraph.model.ParentAwareNode 13 | import de.schauderhaft.degraph.model.SimpleNode.classNode 14 | 15 | @RunWith(classOf[JUnitRunner]) 16 | class SlicePredicateTest extends FunSuite { 17 | 18 | test("returns true for edge contained in set of edges in cycles") { 19 | val predicate = new SlicePredicate(n => n, Set((classNode("a"), classNode("b")))) 20 | predicate(classNode("x"), classNode("y")) should be(false) 21 | predicate(classNode("a"), classNode("b")) should be(true) 22 | predicate(classNode("b"), classNode("a")) should be(false) 23 | } 24 | 25 | test("returns true for edge with the slice projection contained in set of edges in cycles") { 26 | val predicate = new SlicePredicate( 27 | Map(n("x") -> n("a"), 28 | n("y") -> n("b"), 29 | n("a") -> n("x"), 30 | n("b") -> n("y")), 31 | Set((n("x"), n("y")))) 32 | predicate(n("a"), n("b")) should be(true) 33 | predicate(n("b"), n("a")) should be(false) 34 | } 35 | 36 | test("returns true for edge in set but with the slice projection NOT contained in set of edges in cycles") { 37 | val predicate = new SlicePredicate( 38 | Map(n("x") -> n("a"), 39 | n("y") -> n("b"), 40 | n("a") -> n("x"), 41 | n("b") -> n("y")), 42 | Set((n("x"), n("y")))) 43 | predicate(n("x"), n("y")) should be(true) 44 | predicate(n("a"), n("y")) should be(true) 45 | predicate(n("x"), n("b")) should be(true) 46 | predicate(n("a"), n("b")) should be(true) 47 | } 48 | 49 | test("returns true when slice projection is parentAwareNode containing an element of edges in cycles") { 50 | 51 | val predicate = new SlicePredicate( 52 | Map[Node, Node](n("a") -> ParentAwareNode(n("x"), n("y"), n("z")), 53 | n("b") -> ParentAwareNode(n("u"), n("v"), n("w"))).withDefault(x => x), 54 | Set((n("z"), n("w")))) 55 | predicate(n("a"), n("b")) should be(true) 56 | predicate(n("a"), n("w")) should be(true) 57 | predicate(n("z"), n("b")) should be(true) 58 | predicate(n("z"), n("w")) should be(true) 59 | 60 | } 61 | 62 | test("returns true when the slice of a slice of a slice (or something) results in an element of edges in cycles") { 63 | val predicate = new SlicePredicate( 64 | Map[Node, Node]( 65 | n("subsub") -> n("sub"), 66 | n("sub") -> n("SomeClass")).withDefault(x => x), 67 | Set((n("a"), n("SomeClass")))) 68 | 69 | predicate(n("a"), n("subsub")) should be(true) 70 | 71 | } 72 | } -------------------------------------------------------------------------------- /core/src/test/scala/de/schauderhaft/degraph/writer/WriterTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.writer 2 | 3 | import scala.xml.Elem 4 | import scala.xml.Node 5 | import scala.xml.NodeSeq 6 | import org.junit.runner.RunWith 7 | import org.scalatest.FunSuite 8 | import org.scalatest.junit.JUnitRunner 9 | import org.scalatest.matchers.Matcher 10 | import org.scalatest.Matchers._ 11 | import de.schauderhaft.degraph.graph.Graph 12 | import java.awt.Color 13 | import de.schauderhaft.degraph.model.SimpleNode 14 | 15 | @RunWith(classOf[JUnitRunner]) 16 | class WriterTest extends FunSuite { 17 | 18 | def n(s: String) = SimpleNode(s, s) 19 | 20 | test("SelfTest one") { 21 | val someXml = text 22 | (someXml \ "subeins").text should be("text") 23 | } 24 | test("SelfTest two") { 25 | val someXml = text 26 | (someXml \\ "subzwei").head should be(text) 27 | } 28 | 29 | test("writing an empty graph should have a graph node") { 30 | val xml = new Writer().toXml(new Graph()) 31 | (xml \ "graph").size should be(1) 32 | } 33 | 34 | test("writing an empty graph should have key entries") { 35 | val xml = new Writer().toXml(new Graph()) 36 | val keys = (xml \ "key").map((_ \ "@id")).map(_.text) 37 | val keySet = keys.map(_.toString).toSet 38 | val expectedSet = (0 to 10).map("d" + _).toSet 39 | 40 | keySet should be(expectedSet) 41 | } 42 | 43 | test("writing a simple node creates elements for that node") { 44 | val g = new Graph() 45 | val probe = n("probe") 46 | g.add(probe) 47 | val writer = new Writer((x: AnyRef, _) => { x }, new EdgeWriter()) 48 | val xml = writer.toXml(g) 49 | (xml \ "graph" \ "nodeElement").text should be(probe.toString) 50 | } 51 | 52 | test("writing two connected nodes creates elements for the nodes plus an edge") { 53 | val g = new Graph() 54 | g.connect(n("probe1"), n("probe2")) 55 | val writer = new Writer( 56 | (x: AnyRef, _) => { x }, 57 | (x: AnyRef, y: AnyRef) => ) 58 | val xml: Elem = writer.toXml(g) 59 | val nodeText = (xml \ "graph" \ "nodeElement").text 60 | nodeText should include("probe1") 61 | nodeText should include("probe2") 62 | 63 | val edge: NodeSeq = xml \ "graph" \ "edgeElement" 64 | val edgeText = edge.toString 65 | edgeText should include("edgeElement") 66 | } 67 | 68 | test("the color and width of the EdgeStyler end up in the edge") { 69 | val edgeNode = new EdgeWriter(_ => EdgeStyle(Color.RED, 2.0))(n("x"), n("y")) 70 | val styleNode = edgeNode \ "data" \ "PolyLineEdge" \ "LineStyle" 71 | styleNode.toString should include("""color="#FF0000"""") 72 | styleNode.toString should include("""width="2.0"""") 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /degraph/src/demo/resource/example1.config: -------------------------------------------------------------------------------- 1 | output = example1.graphml 2 | classpath = ../lib/degraph-0.1.4.jar 3 | exclude = java*.** 4 | exclude = scala.** 5 | exclude = org.scalatest.** 6 | part = { 7 | de.schauderhaft.*.(*).** 8 | } 9 | lib = { 10 | de.schauderhaft.(*).** 11 | *.(*).** 12 | } 13 | internalExternal = { 14 | internal de.schauderhaft.** 15 | external ** 16 | } 17 | -------------------------------------------------------------------------------- /degraph/src/dist/lib/ASMLicense.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2000-2011 INRIA, France Telecom 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holders nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 23 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 | THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /degraph/src/dist/lib/ScalaLibraryLicense.txt: -------------------------------------------------------------------------------- 1 | SCALA LICENSE 2 | 3 | Copyright (c) 2002-2012 EPFL, Lausanne, unless otherwise specified. 4 | All rights reserved. 5 | 6 | This software was developed by the Programming Methods Laboratory of the 7 | Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. 8 | 9 | Permission to use, copy, modify, and distribute this software in source 10 | or binary form for any purpose with or without fee is hereby granted, 11 | provided that the following conditions are met: 12 | 13 | 1. Redistributions of source code must retain the above copyright 14 | notice, this list of conditions and the following disclaimer. 15 | 16 | 2. Redistributions in binary form must reproduce the above copyright 17 | notice, this list of conditions and the following disclaimer in the 18 | documentation and/or other materials provided with the distribution. 19 | 20 | 3. Neither the name of the EPFL nor the names of its contributors 21 | may be used to endorse or promote products derived from this 22 | software without specific prior written permission. 23 | 24 | 25 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 31 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 | SUCH DAMAGE. -------------------------------------------------------------------------------- /degraph/src/dist/lib/ScallopLicense.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2012 Platon Pronko 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | UTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 17 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /degraph/src/main/ghpages/images/body-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riy/degraph/2e61bb87b73399aa6f00590ef74ba2c869f1861e/degraph/src/main/ghpages/images/body-bg.png -------------------------------------------------------------------------------- /degraph/src/main/ghpages/images/highlight-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riy/degraph/2e61bb87b73399aa6f00590ef74ba2c869f1861e/degraph/src/main/ghpages/images/highlight-bg.jpg -------------------------------------------------------------------------------- /degraph/src/main/ghpages/images/hr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riy/degraph/2e61bb87b73399aa6f00590ef74ba2c869f1861e/degraph/src/main/ghpages/images/hr.png -------------------------------------------------------------------------------- /degraph/src/main/ghpages/images/octocat-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riy/degraph/2e61bb87b73399aa6f00590ef74ba2c869f1861e/degraph/src/main/ghpages/images/octocat-icon.png -------------------------------------------------------------------------------- /degraph/src/main/ghpages/images/tar-gz-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riy/degraph/2e61bb87b73399aa6f00590ef74ba2c869f1861e/degraph/src/main/ghpages/images/tar-gz-icon.png -------------------------------------------------------------------------------- /degraph/src/main/ghpages/images/zip-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riy/degraph/2e61bb87b73399aa6f00590ef74ba2c869f1861e/degraph/src/main/ghpages/images/zip-icon.png -------------------------------------------------------------------------------- /degraph/src/main/ghpages/index.md: -------------------------------------------------------------------------------- 1 | #Welcome to Degraph, the tool for analyzing, visualizing and (soon to come) controlling dependencies inside your Java Code# 2 | 3 | This page is not yet ready for prime time. Head over to the (github repository)[http://https://github.com/schauder/degraph] until this is done. 4 | 5 | -------------------------------------------------------------------------------- /degraph/src/main/ghpages/indexx.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | Degraph by schauder 14 | 15 | 16 | 17 |
18 |
19 | 20 |
21 |

Degraph

22 |

Visualize Dependencies of classes and packages in JVM Byte Code (think Scala and Java)

23 |
24 | 25 |
26 | Download .zip 27 | Download .tar.gz 28 | View on GitHub 29 |
30 | 31 |
32 | 33 |
34 |

What can I do here?

35 | 36 |

DeGraph is a library for creating graph out of dependency data. The unique feature of it is that it can create hierarchic graphs. This means you can represent information like Class A is part of package X which is part of Layer Z in Module Y

37 |
38 | updated from gradle 39 | 43 | 44 | 45 |
46 |
47 | 48 | -------------------------------------------------------------------------------- /degraph/src/main/ghpages/javascripts/main.js: -------------------------------------------------------------------------------- 1 | console.log('This would be the main JS file.'); 2 | -------------------------------------------------------------------------------- /degraph/src/main/ghpages/params.json: -------------------------------------------------------------------------------- 1 | {"name":"Degraph","tagline":"Visualize Dependencies of classes and packages in JVM Byte Code (think Scala and Java)","body":"### What can I do here?\r\nDeGraph is a library for creating graph out of dependency data. The unique feature of it is that it can create hierarchic graphs. This means you can represent information like `Class A is part of package X which is part of Layer Z in Module Y`","google":"","note":"Don't delete this file! It's used internally to help with page regeneration."} -------------------------------------------------------------------------------- /degraph/src/main/ghpages/stylesheets/print.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, applet, object, iframe, 2 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 3 | a, abbr, acronym, address, big, cite, code, 4 | del, dfn, em, img, ins, kbd, q, s, samp, 5 | small, strike, strong, sub, sup, tt, var, 6 | b, u, i, center, 7 | dl, dt, dd, ol, ul, li, 8 | fieldset, form, label, legend, 9 | table, caption, tbody, tfoot, thead, tr, th, td, 10 | article, aside, canvas, details, embed, 11 | figure, figcaption, footer, header, hgroup, 12 | menu, nav, output, ruby, section, summary, 13 | time, mark, audio, video { 14 | margin: 0; 15 | padding: 0; 16 | border: 0; 17 | font-size: 100%; 18 | font: inherit; 19 | vertical-align: baseline; 20 | } 21 | /* HTML5 display-role reset for older browsers */ 22 | article, aside, details, figcaption, figure, 23 | footer, header, hgroup, menu, nav, section { 24 | display: block; 25 | } 26 | body { 27 | line-height: 1; 28 | } 29 | ol, ul { 30 | list-style: none; 31 | } 32 | blockquote, q { 33 | quotes: none; 34 | } 35 | blockquote:before, blockquote:after, 36 | q:before, q:after { 37 | content: ''; 38 | content: none; 39 | } 40 | table { 41 | border-collapse: collapse; 42 | border-spacing: 0; 43 | } 44 | body { 45 | font-size: 13px; 46 | line-height: 1.5; 47 | font-family: 'Helvetica Neue', Helvetica, Arial, serif; 48 | color: #000; 49 | } 50 | 51 | a { 52 | color: #d5000d; 53 | font-weight: bold; 54 | } 55 | 56 | header { 57 | padding-top: 35px; 58 | padding-bottom: 10px; 59 | } 60 | 61 | header h1 { 62 | font-weight: bold; 63 | letter-spacing: -1px; 64 | font-size: 48px; 65 | color: #303030; 66 | line-height: 1.2; 67 | } 68 | 69 | header h2 { 70 | letter-spacing: -1px; 71 | font-size: 24px; 72 | color: #aaa; 73 | font-weight: normal; 74 | line-height: 1.3; 75 | } 76 | #downloads { 77 | display: none; 78 | } 79 | #main_content { 80 | padding-top: 20px; 81 | } 82 | 83 | code, pre { 84 | font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal; 85 | color: #222; 86 | margin-bottom: 30px; 87 | font-size: 12px; 88 | } 89 | 90 | code { 91 | padding: 0 3px; 92 | } 93 | 94 | pre { 95 | border: solid 1px #ddd; 96 | padding: 20px; 97 | overflow: auto; 98 | } 99 | pre code { 100 | padding: 0; 101 | } 102 | 103 | ul, ol, dl { 104 | margin-bottom: 20px; 105 | } 106 | 107 | 108 | /* COMMON STYLES */ 109 | 110 | table { 111 | width: 100%; 112 | border: 1px solid #ebebeb; 113 | } 114 | 115 | th { 116 | font-weight: 500; 117 | } 118 | 119 | td { 120 | border: 1px solid #ebebeb; 121 | text-align: center; 122 | font-weight: 300; 123 | } 124 | 125 | form { 126 | background: #f2f2f2; 127 | padding: 20px; 128 | 129 | } 130 | 131 | 132 | /* GENERAL ELEMENT TYPE STYLES */ 133 | 134 | h1 { 135 | font-size: 2.8em; 136 | } 137 | 138 | h2 { 139 | font-size: 22px; 140 | font-weight: bold; 141 | color: #303030; 142 | margin-bottom: 8px; 143 | } 144 | 145 | h3 { 146 | color: #d5000d; 147 | font-size: 18px; 148 | font-weight: bold; 149 | margin-bottom: 8px; 150 | } 151 | 152 | h4 { 153 | font-size: 16px; 154 | color: #303030; 155 | font-weight: bold; 156 | } 157 | 158 | h5 { 159 | font-size: 1em; 160 | color: #303030; 161 | } 162 | 163 | h6 { 164 | font-size: .8em; 165 | color: #303030; 166 | } 167 | 168 | p { 169 | font-weight: 300; 170 | margin-bottom: 20px; 171 | } 172 | 173 | a { 174 | text-decoration: none; 175 | } 176 | 177 | p a { 178 | font-weight: 400; 179 | } 180 | 181 | blockquote { 182 | font-size: 1.6em; 183 | border-left: 10px solid #e9e9e9; 184 | margin-bottom: 20px; 185 | padding: 0 0 0 30px; 186 | } 187 | 188 | ul li { 189 | list-style: disc inside; 190 | padding-left: 20px; 191 | } 192 | 193 | ol li { 194 | list-style: decimal inside; 195 | padding-left: 3px; 196 | } 197 | 198 | dl dd { 199 | font-style: italic; 200 | font-weight: 100; 201 | } 202 | 203 | footer { 204 | margin-top: 40px; 205 | padding-top: 20px; 206 | padding-bottom: 30px; 207 | font-size: 13px; 208 | color: #aaa; 209 | } 210 | 211 | footer a { 212 | color: #666; 213 | } 214 | 215 | /* MISC */ 216 | .clearfix:after { 217 | clear: both; 218 | content: '.'; 219 | display: block; 220 | visibility: hidden; 221 | height: 0; 222 | } 223 | 224 | .clearfix {display: inline-block;} 225 | * html .clearfix {height: 1%;} 226 | .clearfix {display: block;} -------------------------------------------------------------------------------- /degraph/src/main/ghpages/stylesheets/pygment_trac.css: -------------------------------------------------------------------------------- 1 | .highlight { background: #ffffff; } 2 | .highlight .c { color: #999988; font-style: italic } /* Comment */ 3 | .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ 4 | .highlight .k { font-weight: bold } /* Keyword */ 5 | .highlight .o { font-weight: bold } /* Operator */ 6 | .highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ 7 | .highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ 8 | .highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ 9 | .highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ 10 | .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ 11 | .highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */ 12 | .highlight .ge { font-style: italic } /* Generic.Emph */ 13 | .highlight .gr { color: #aa0000 } /* Generic.Error */ 14 | .highlight .gh { color: #999999 } /* Generic.Heading */ 15 | .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ 16 | .highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */ 17 | .highlight .go { color: #888888 } /* Generic.Output */ 18 | .highlight .gp { color: #555555 } /* Generic.Prompt */ 19 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 20 | .highlight .gu { color: #800080; font-weight: bold; } /* Generic.Subheading */ 21 | .highlight .gt { color: #aa0000 } /* Generic.Traceback */ 22 | .highlight .kc { font-weight: bold } /* Keyword.Constant */ 23 | .highlight .kd { font-weight: bold } /* Keyword.Declaration */ 24 | .highlight .kn { font-weight: bold } /* Keyword.Namespace */ 25 | .highlight .kp { font-weight: bold } /* Keyword.Pseudo */ 26 | .highlight .kr { font-weight: bold } /* Keyword.Reserved */ 27 | .highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ 28 | .highlight .m { color: #009999 } /* Literal.Number */ 29 | .highlight .s { color: #d14 } /* Literal.String */ 30 | .highlight .na { color: #008080 } /* Name.Attribute */ 31 | .highlight .nb { color: #0086B3 } /* Name.Builtin */ 32 | .highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ 33 | .highlight .no { color: #008080 } /* Name.Constant */ 34 | .highlight .ni { color: #800080 } /* Name.Entity */ 35 | .highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ 36 | .highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ 37 | .highlight .nn { color: #555555 } /* Name.Namespace */ 38 | .highlight .nt { color: #000080 } /* Name.Tag */ 39 | .highlight .nv { color: #008080 } /* Name.Variable */ 40 | .highlight .ow { font-weight: bold } /* Operator.Word */ 41 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 42 | .highlight .mf { color: #009999 } /* Literal.Number.Float */ 43 | .highlight .mh { color: #009999 } /* Literal.Number.Hex */ 44 | .highlight .mi { color: #009999 } /* Literal.Number.Integer */ 45 | .highlight .mo { color: #009999 } /* Literal.Number.Oct */ 46 | .highlight .sb { color: #d14 } /* Literal.String.Backtick */ 47 | .highlight .sc { color: #d14 } /* Literal.String.Char */ 48 | .highlight .sd { color: #d14 } /* Literal.String.Doc */ 49 | .highlight .s2 { color: #d14 } /* Literal.String.Double */ 50 | .highlight .se { color: #d14 } /* Literal.String.Escape */ 51 | .highlight .sh { color: #d14 } /* Literal.String.Heredoc */ 52 | .highlight .si { color: #d14 } /* Literal.String.Interpol */ 53 | .highlight .sx { color: #d14 } /* Literal.String.Other */ 54 | .highlight .sr { color: #009926 } /* Literal.String.Regex */ 55 | .highlight .s1 { color: #d14 } /* Literal.String.Single */ 56 | .highlight .ss { color: #990073 } /* Literal.String.Symbol */ 57 | .highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ 58 | .highlight .vc { color: #008080 } /* Name.Variable.Class */ 59 | .highlight .vg { color: #008080 } /* Name.Variable.Global */ 60 | .highlight .vi { color: #008080 } /* Name.Variable.Instance */ 61 | .highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ 62 | 63 | .type-csharp .highlight .k { color: #0000FF } 64 | .type-csharp .highlight .kt { color: #0000FF } 65 | .type-csharp .highlight .nf { color: #000000; font-weight: normal } 66 | .type-csharp .highlight .nc { color: #2B91AF } 67 | .type-csharp .highlight .nn { color: #000000 } 68 | .type-csharp .highlight .s { color: #A31515 } 69 | .type-csharp .highlight .sc { color: #A31515 } 70 | -------------------------------------------------------------------------------- /degraph/src/main/scala/de/schauderhaft/degraph/app/CommandLineParser.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.app 2 | 3 | import org.rogach.scallop._ 4 | 5 | /** 6 | * parses the command line arguments for Degraph 7 | */ 8 | object CommandLineParser { 9 | 10 | def parse(args: Seq[String]) = { 11 | new LazyScallopConf(args) { 12 | 13 | version("Degraph Version 0.1.4") 14 | banner("""Degraph analyses class and jar files and creates graphml documents out of it 15 | for visualizing dependencies.""") 16 | footer("""See https://github.com/schauder/degraph for more information""") 17 | 18 | val output = opt[String]("output", 19 | default = Some("output.graphml"), 20 | descr = "the file name of the output file") 21 | val classpath = opt[String]("classpath", 22 | default = Some("."), 23 | descr = "the classpath where Degraph looks for classfiles to analyze") 24 | val excludeFilter = opt[List[String]]("excludeFilter", 25 | default = Some(List()), 26 | descr = "if this argument is given those nodes that are matched by the given regular expression will get excluded from the graph.") 27 | val includeFilter = opt[List[String]]("includeFilter", 28 | default = Some(List()), 29 | descr = "if this argument is given, only those nodes get included in the resulting graph, that match the given regular expression.") 30 | val file = opt[String]("file", default = None) 31 | val display = toggle("display", default = Some(false)) 32 | 33 | } 34 | 35 | } 36 | } -------------------------------------------------------------------------------- /degraph/src/main/scala/de/schauderhaft/degraph/app/ConfigurationFromCommandLine.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.app 2 | 3 | import org.rogach.scallop.exceptions.{Help, ScallopException} 4 | import de.schauderhaft.degraph.configuration.{Print, Configuration} 5 | import scala.io.Source 6 | 7 | object ConfigurationFromCommandLine { 8 | 9 | /** 10 | * just pass the commandline arguments to this method to get an Either representing the 11 | * result of attempting to create a configuration out of the arguments. 12 | * 13 | * If something went wrong a Left will get returned containing an error message, including 14 | * some usage advice suitable for presenting it to the user. 15 | * 16 | * Otherwise a Right instance containing the complete configuration is returned. 17 | */ 18 | def apply(args: Array[String]): Either[String, Configuration] = { 19 | val eitherConfig = fromCommandLine(args) 20 | 21 | eitherConfig 22 | } 23 | def fromCommandLine(args: Array[String]): Either[String, Configuration] = { 24 | import scala.language.reflectiveCalls 25 | var errorMessage: Option[String] = None 26 | val commandLine = CommandLineParser.parse(args) 27 | commandLine.initialize { 28 | case ScallopException(m) => errorMessage = Some(m + "\nUsage:\n" + commandLine.builder.help) 29 | case Help(_) => errorMessage = Some("\nUsage:\n" + commandLine.builder.help) 30 | } 31 | errorMessage match { 32 | case Some(m) => Left(m) 33 | case _ if commandLine.file.isEmpty => Right( 34 | Configuration( 35 | classpath = Some(commandLine.classpath()), 36 | includes = commandLine.includeFilter(), 37 | excludes = commandLine.excludeFilter(), 38 | output = Print(commandLine.output()), 39 | categories = Map() 40 | ) 41 | ) 42 | case _ => Right(new ConfigurationParser().parse(Source.fromFile(commandLine.file()).mkString)) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /degraph/src/main/scala/de/schauderhaft/degraph/app/ConfigurationParser.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.app 2 | 3 | import scala.util.parsing.combinator.RegexParsers 4 | import de.schauderhaft.degraph.configuration._ 5 | 6 | /** 7 | * parses a configuration file 8 | */ 9 | class ConfigurationParser extends RegexParsers { 10 | def parse(input: String): Configuration = { 11 | parseAll(defs, input + "\n") match { 12 | case s: Success[_] => s.get 13 | case f => throw new RuntimeException("Failed to parse" + f) 14 | } 15 | } 16 | 17 | def defs: Parser[Configuration] = opt(eol) ~> definition.* ^^ mayReduce 18 | 19 | def definition: Parser[Configuration] = comment | output | include | exclude | classpath | slice 20 | 21 | override val whiteSpace = """[ \t]*""".r 22 | def eol: Parser[Any] = """(\r?\n)""".r.+ 23 | 24 | private def line(key: String): Parser[String] = (key ~ "=") ~> word <~ eol 25 | protected def comment: Parser[Configuration] = "#.*".r <~ eol ^^ ((x: String) => Configuration()) 26 | protected def output: Parser[Configuration] = line("output") ^^ ((x: String) => Configuration(None, Seq(), Seq(), Map(), Print(x))) 27 | protected def include: Parser[Configuration] = line("include") ^^ ((x: String) => Configuration(None, Seq(x), Seq(), Map(), NoPrinting())) 28 | protected def exclude: Parser[Configuration] = line("exclude") ^^ ((x: String) => Configuration(None, Seq(), Seq(x), Map(), NoPrinting())) 29 | protected def classpath: Parser[Configuration] = line("classpath") ^^ ((x: String) => Configuration(Some(x), Seq(), Seq(), Map(), NoPrinting())) 30 | protected def emptyLine: Parser[Any] = eol 31 | protected def slice: Parser[Configuration] = (word <~ "=") ~ patternBlock <~ eol ^^ (x => Configuration(categories = Map((x._1, x._2)))) 32 | protected def patternBlock: Parser[List[Pattern]] = "{" ~> eol ~> patterns <~ "}" 33 | protected def patterns: Parser[List[Pattern]] = rep(pattern) 34 | protected def pattern: Parser[Pattern] = namedPattern | unnamedPattern 35 | protected def namedPattern: Parser[NamedPattern] = word ~ word <~ eol ^^ (x => NamedPattern(x._2, x._1)) 36 | protected def unnamedPattern: Parser[UnnamedPattern] = word <~ eol ^^ (x => UnnamedPattern(x)) 37 | protected def word: Parser[String] = "[^\\s{}]+".r 38 | 39 | protected def merge(c1: Configuration, c2: Configuration): Configuration = 40 | Configuration( 41 | c1.classpath.orElse(c2.classpath), 42 | c1.includes ++ c2.includes, 43 | c1.excludes ++ c2.excludes, 44 | c1.categories ++ c2.categories, 45 | c1.output.merge(c2.output)) 46 | 47 | private def combine(s: Seq[Configuration]): Configuration = 48 | s.fold(Configuration(None, Seq(), Seq(), Map(), NoPrinting()))(merge) 49 | 50 | private val mayReduce = new PartialFunction[Seq[Configuration], Configuration] { 51 | def isDefinedAt(s: Seq[Configuration]) = 52 | s.filter(_.output.isDefined).size <= 1 && 53 | s.filter(_.classpath.isDefined).size <= 1 54 | def apply(s: Seq[Configuration]) = combine(s) 55 | } 56 | 57 | private def mayReduce(opt: Option[Seq[Configuration]]): Configuration = opt match { 58 | case Some(list: Seq[_]) => mayReduce(list) 59 | case None => Configuration(None, Seq(), Seq(), Map(), NoPrinting()) 60 | } 61 | } -------------------------------------------------------------------------------- /degraph/src/main/scala/de/schauderhaft/degraph/app/Degraph.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.app 2 | 3 | import _root_.java.awt.Color.RED 4 | 5 | import de.schauderhaft.degraph.analysis.asm.Analyzer 6 | import de.schauderhaft.degraph.configuration.Print 7 | import de.schauderhaft.degraph.writer.{DefaultEdgeStyle, EdgeStyle, PredicateStyler, SlicePredicate, Writer} 8 | 9 | import scala.xml.XML 10 | 11 | /** 12 | * The main class of Degraph, plugging everything together, 13 | * starting the analysis process and writing the result to an XML file 14 | */ 15 | object Degraph { 16 | 17 | def main(args: Array[String]): Unit = { 18 | ConfigurationFromCommandLine(args) match { 19 | case Left(m) => println(m) 20 | case Right(c) => 21 | val g = c.copy(analyzer = Analyzer).createGraph() 22 | val violations = c.constraint. 23 | flatMap(_.violations(g)). 24 | flatMap(_.dependencies) 25 | val styler = PredicateStyler.styler( 26 | new SlicePredicate(c.slicing, violations), 27 | EdgeStyle(RED, 2.0), 28 | DefaultEdgeStyle 29 | ) 30 | val xml = new Writer(styler).toXml(g) 31 | XML.save(c.output.asInstanceOf[Print].path, xml, "UTF8", xmlDecl = true, null) 32 | println("Found %d nodes, with %d slice edges in violation of dependency constraints.".format(g.allNodes.size, violations.size)) 33 | } 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /degraph/src/test/resource/example.config: -------------------------------------------------------------------------------- 1 | output = o.graphml 2 | classpath = src/test/scala 3 | exclude = java** 4 | part = { 5 | de.schauderhaft.*.(*).** 6 | } 7 | lib = { 8 | de.schauderhaft.(*).** 9 | *.(*).** 10 | } 11 | internalExternal = { 12 | internal de.schauderhaft.** 13 | external ** 14 | } 15 | -------------------------------------------------------------------------------- /degraph/src/test/resource/junit.config: -------------------------------------------------------------------------------- 1 | output = junit.graphml 2 | classpath = C:\Users\jeschaud\.m2\repository\junit\junit\4.10\junit-4.10.jar 3 | include = **.junit.** 4 | newOld = { 5 | old junit.** 6 | new org.junit.** 7 | } -------------------------------------------------------------------------------- /degraph/src/test/resource/manualSelfTest.config: -------------------------------------------------------------------------------- 1 | output = selfTest.graphml 2 | classpath = ./build/classes/ 3 | exclude = java*.** 4 | exclude = scala*.** 5 | exclude = org.scalatest.** 6 | part = { 7 | de.schauderhaft.*.(*).** 8 | } 9 | mainVsTest = { 10 | Test de.schauderhaft.*.example.** 11 | de.schauderhaft.**(Test) 12 | main ** 13 | } 14 | lib = { 15 | check de.schauderhaft.degraph.(check).** 16 | check de.schauderhaft.degraph.(hamcrest).** 17 | app de.schauderhaft.degraph.app.** 18 | core de.schauderhaft.degraph.** 19 | *.(*).** 20 | } 21 | internalExternal = { 22 | internal de.schauderhaft.** 23 | external ** 24 | } 25 | -------------------------------------------------------------------------------- /degraph/src/test/scala/de/schauderhaft/degraph/app/CommandLineParserTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.app 2 | 3 | import org.scalatest.FunSuite 4 | import org.junit.runner.RunWith 5 | import org.scalatest.junit.JUnitRunner 6 | 7 | @RunWith(classOf[JUnitRunner]) 8 | class CommandLineParserTest extends FunSuite { 9 | import org.scalatest.Matchers._ 10 | 11 | test("default outputfile is 'output.graphml'") { 12 | val config = CommandLineParser.parse(Array[String]()) 13 | config.initialize { case e => } 14 | config.output() should be("output.graphml") 15 | } 16 | 17 | test("the string after -o is considered the output file name") { 18 | val config = CommandLineParser.parse(Array("-o", "ExampleFile")) 19 | config.initialize { case e => } 20 | config.output() should be("ExampleFile") 21 | } 22 | 23 | test("default input is empty") { 24 | val config = CommandLineParser.parse(Array[String]()) 25 | config.initialize { case e => } 26 | config.classpath() should be(".") 27 | } 28 | 29 | test("the string after -c is considered the input classpath") { 30 | val config = CommandLineParser.parse(Array("-c", "input;blah.jar")) 31 | config.initialize { case e => } 32 | config.classpath() should be("input;blah.jar") 33 | } 34 | 35 | test("default exclude filter is empty") { 36 | val config = CommandLineParser.parse(Array[String]()) 37 | config.initialize { case e => } 38 | config.excludeFilter() should be(List()) 39 | } 40 | test("the strings after -e s are considered the exclude filter") { 41 | val config = CommandLineParser.parse(Array("-e", "filter")) 42 | config.initialize { case e => } 43 | config.excludeFilter() should be(List("filter")) 44 | } 45 | 46 | test("default include filter is empty") { 47 | val config = CommandLineParser.parse(Array[String]()) 48 | config.initialize { case e => } 49 | config.includeFilter() should be(List()) 50 | } 51 | test("the strings after -i s are considered the exclude filter") { 52 | val config = CommandLineParser.parse(Array[String]("-i", "filter")) 53 | config.initialize { case e => } 54 | config.includeFilter() should be(List("filter")) 55 | } 56 | 57 | test("the strings after -f are considered the configurationFile") { 58 | val config = CommandLineParser.parse(Array[String]("-f", "file")) 59 | config.initialize { case e => } 60 | config.file.get should be(Some("file")) 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /degraph/src/test/scala/de/schauderhaft/degraph/app/ConfigurationFromCommandLineTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.app 2 | 3 | import org.scalatest.FunSuite 4 | import org.scalatest.Matchers._ 5 | import de.schauderhaft.degraph.model.SimpleNode._ 6 | import scala.Some 7 | import de.schauderhaft.degraph.model.{Node, SimpleNode, ParentAwareNode} 8 | import de.schauderhaft.degraph.analysis.AnalyzerLike 9 | import de.schauderhaft.degraph.graph.Graph 10 | 11 | class ConfigurationFromCommandLineTest extends FunSuite { 12 | test("valid configuration returns a Righ[Configuration]") { 13 | ConfigurationFromCommandLine(Array[String]()).isRight should be(true) 14 | } 15 | 16 | test("classpath gets passed to analyzer") { 17 | val config = ConfigurationFromCommandLine(Array("-c", "clp")).right.get 18 | val spy = new SpyAnalyze() 19 | config.copy(analyzer = spy).createGraph() 20 | spy.classPath should be("clp") 21 | } 22 | 23 | test("include / exclude gets passed to analyzer") { 24 | val config = ConfigurationFromCommandLine(Array("-i", "input", "-e", "inputty")).right.get 25 | val spy = new SpyAnalyze() 26 | config.copy(analyzer = spy).createGraph() 27 | val filter = spy.filter 28 | filter(classNode("input")) should be(true) 29 | filter(classNode("inputty")) should be(false) 30 | filter(classNode("whats that")) should be(false) 31 | } 32 | 33 | test("the string after -f is considered a configuration file and gets loaded") { 34 | val config = ConfigurationFromCommandLine(Array("-f", "src/test/resource/example.config")).right.get 35 | 36 | config.output should be(Some("o.graphml")) 37 | 38 | val spy = new SpyAnalyze() 39 | config.copy(analyzer = spy).createGraph() 40 | 41 | spy.classPath should be("""src/test/scala""") 42 | 43 | spy.filter(classNode("javax.swing.JPanel")) should be(false) 44 | spy.filter(classNode("de.schauderhaft")) should be(true) 45 | 46 | val categorizer = spy.categorizer 47 | categorizer(classNode("de.schauderhaft.degraph.parser.Jens")) should be( 48 | ParentAwareNode(packageNode("de.schauderhaft.degraph.parser"), SimpleNode("part", "parser"), SimpleNode("lib", "degraph"), SimpleNode("internalExternal", "internal"))) 49 | } 50 | 51 | test("invalid configuration returns a Left(message)") { 52 | val message = ConfigurationFromCommandLine(Array("garbage")).left.get 53 | message should include("garbage") 54 | message should include("Degraph") 55 | } 56 | 57 | 58 | class SpyAnalyze() extends AnalyzerLike { 59 | var classPath: String = "" 60 | var categorizer: Node => Node = (x) => x.asInstanceOf[Node] 61 | var filter: Node => Boolean = (_) => true 62 | 63 | def analyze(aClassPath: String, 64 | aCategorizer: Node => Node, 65 | aFilter: Node => Boolean) = { 66 | classPath = aClassPath 67 | categorizer = aCategorizer 68 | filter = aFilter 69 | new Graph() 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /degraph/src/test/scala/de/schauderhaft/degraph/app/ConfigurationParserTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.app 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.junit.JUnitRunner 5 | import org.scalatest.FunSuiteLike 6 | import de.schauderhaft.degraph.configuration._ 7 | import org.scalatest.Matchers._ 8 | 9 | @RunWith(classOf[JUnitRunner]) 10 | class ConfigurationParserTest extends ConfigurationParser with FunSuiteLike { 11 | test("empty file creates empty configuration") { 12 | parse("") should be(Configuration(None, Seq(), Seq(), Map(), NoPrinting())) 13 | } 14 | test("file with just comments creates empty configuration") { 15 | parse("# comment") should be(Configuration(None, Seq(), Seq(), Map(), NoPrinting())) 16 | } 17 | 18 | test("output configures outputfile configuration") { 19 | parse("output=example.file") should be(Configuration(None, Seq(), Seq(), Map(), Print("example.file"))) 20 | } 21 | test("output configures outputfile configuration, leading newlines") { 22 | parse(""" 23 | output=example.file""") should be(Configuration(None, Seq(), Seq(), Map(), Print("example.file"))) 24 | } 25 | 26 | test("output configures outputfile configuration with trailing eol") { 27 | parse("""output=example.file 28 | """) should be(Configuration(None, Seq(), Seq(), Map(), Print("example.file"))) 29 | } 30 | 31 | test("include configures include configuration") { 32 | parse("include=pattern") should be(Configuration(None, Seq("pattern"), Seq(), Map(), NoPrinting())) 33 | } 34 | 35 | test("leading whitespace gets ignored") { 36 | parse(" include=pattern") should be(Configuration(None, Seq("pattern"), Seq(), Map(), NoPrinting())) 37 | } 38 | 39 | test("trailing whitespace gets ignored") { 40 | parse("include=pattern ") should be(Configuration(None, Seq("pattern"), Seq(), Map(), NoPrinting())) 41 | } 42 | 43 | test("trailing new line gets ignored") { 44 | parse("""include=pattern 45 | """) should be(Configuration(None, Seq("pattern"), Seq(), Map(), NoPrinting())) 46 | } 47 | 48 | test("trailing new lines gets ignored") { 49 | parse("""include=pattern 50 | 51 | 52 | """) should be(Configuration(None, Seq("pattern"), Seq(), Map(), NoPrinting())) 53 | } 54 | 55 | test("two part configuration example") { 56 | parse("""output=example.file 57 | include=pattern""") should be(Configuration(None, Seq("pattern"), Seq(), Map(), Print("example.file"))) 58 | } 59 | 60 | test("two part configuration example with whitespace") { 61 | parse(""" 62 | output=example.file 63 | include=pattern 64 | """) should be(Configuration(None, Seq("pattern"), Seq(), Map(), Print("example.file"))) 65 | } 66 | test("two part configuration example with whitespace around =") { 67 | parse(""" 68 | output = example.file 69 | include = pattern 70 | """) should be(Configuration(None, Seq("pattern"), Seq(), Map(), Print("example.file"))) 71 | } 72 | 73 | test("full configuration example with whitespace") { 74 | parse(""" 75 | classpath=ap;x.jar 76 | output=example.file 77 | include=pattern 78 | exclude=expattern 79 | """) should be(Configuration(Some("ap;x.jar"), Seq("pattern"), Seq("expattern"), Map(), Print("example.file"))) 80 | } 81 | test("full configuration example with whitespace & comments") { 82 | parse(""" 83 | classpath=ap;x.jar 84 | # some comment 85 | output=example.file 86 | # more comments 87 | include=pattern 88 | exclude=expattern 89 | # trailing comments 90 | """) should be(Configuration(Some("ap;x.jar"), Seq("pattern"), Seq("expattern"), Map(), Print("example.file"))) 91 | } 92 | 93 | test("Named Pattern") { 94 | parseAll(namedPattern, """DependencyFinder *.jeant*.** 95 | """).get should be(NamedPattern("""*.jeant*.**""", "DependencyFinder")) 96 | } 97 | 98 | test("word") { 99 | parseAll(word, """ß439qu8rej""").get should be("""ß439qu8rej""") 100 | } 101 | 102 | test("curly braces aren't a word : {") { 103 | parseAll(word, """{""").successful should be(false) 104 | } 105 | 106 | test("curly braces aren't a word : }") { 107 | parseAll(word, """{""").successful should be(false) 108 | } 109 | 110 | test("slice definition") { 111 | parse(""" 112 | libs = { 113 | DependencyFinder *.jeant*.** 114 | *.(*).** 115 | } 116 | """) should be(Configuration( 117 | categories = Map("libs" -> List(NamedPattern("""*.jeant*.**""", "DependencyFinder"), UnnamedPattern("""*.(*).**"""))))) 118 | } 119 | 120 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | version=0.1.5 2 | 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riy/degraph/2e61bb87b73399aa6f00590ef74ba2c869f1861e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Nov 15 11:04:18 CET 2019 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-all.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=$((i+1)) 158 | done 159 | case $i in 160 | (0) set -- ;; 161 | (1) set -- "$args0" ;; 162 | (2) set -- "$args0" "$args1" ;; 163 | (3) set -- "$args0" "$args1" "$args2" ;; 164 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=$(save "$@") 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 184 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 185 | cd "$(dirname "$0")" 186 | fi 187 | 188 | exec "$JAVACMD" "$@" 189 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /jdk8tests/src/main/java/de/schauderhaft/degraph/jdk8tests/ClassWithTypeAnnotations.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.jdk8tests; 2 | 3 | import java.sql.SQLException; 4 | 5 | public class ClassWithTypeAnnotations // implements clause: 6 | implements @TypeAnno1 ClassWithTypeParam<@TypeAnno2 T> { 7 | 8 | 9 | // Class instance creation expression: 10 | private Object x = new @TypeAnno3 Object(); 11 | 12 | private @TypeAnno6 Object str = "Hallo"; 13 | 14 | // Type cast: 15 | public String myString = (@TypeAnno4 String) str; 16 | 17 | // Thrown exception declaration: 18 | public void throwsAnnotatedException() throws @TypeAnno5 SQLException { 19 | } 20 | 21 | @Override 22 | public @TypeAnno2 T doSomething() { 23 | try{ 24 | @TypeAnno8 String local = new String(); 25 | 26 | } catch (@TypeAnno7 RuntimeException re){ 27 | 28 | } 29 | 30 | return null; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /jdk8tests/src/main/java/de/schauderhaft/degraph/jdk8tests/ClassWithTypeParam.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.jdk8tests; 2 | 3 | public interface ClassWithTypeParam { 4 | T doSomething(); 5 | } 6 | -------------------------------------------------------------------------------- /jdk8tests/src/main/java/de/schauderhaft/degraph/jdk8tests/TypeAnno1.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.jdk8tests; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.CLASS) 9 | @Target(ElementType.TYPE_USE) 10 | public @interface TypeAnno1 { 11 | } 12 | -------------------------------------------------------------------------------- /jdk8tests/src/main/java/de/schauderhaft/degraph/jdk8tests/TypeAnno2.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.jdk8tests; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.CLASS) 9 | @Target(ElementType.TYPE_USE) 10 | public @interface TypeAnno2 { 11 | } 12 | -------------------------------------------------------------------------------- /jdk8tests/src/main/java/de/schauderhaft/degraph/jdk8tests/TypeAnno3.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.jdk8tests; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.CLASS) 9 | @Target(ElementType.TYPE_USE) 10 | public @interface TypeAnno3 { 11 | } 12 | -------------------------------------------------------------------------------- /jdk8tests/src/main/java/de/schauderhaft/degraph/jdk8tests/TypeAnno4.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.jdk8tests; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.CLASS) 9 | @Target(ElementType.TYPE_USE) 10 | public @interface TypeAnno4 { 11 | } 12 | -------------------------------------------------------------------------------- /jdk8tests/src/main/java/de/schauderhaft/degraph/jdk8tests/TypeAnno5.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.jdk8tests; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.CLASS) 9 | @Target(ElementType.TYPE_USE) 10 | public @interface TypeAnno5 { 11 | } 12 | -------------------------------------------------------------------------------- /jdk8tests/src/main/java/de/schauderhaft/degraph/jdk8tests/TypeAnno6.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.jdk8tests; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.CLASS) 9 | @Target(ElementType.TYPE_USE) 10 | public @interface TypeAnno6 { 11 | } 12 | -------------------------------------------------------------------------------- /jdk8tests/src/main/java/de/schauderhaft/degraph/jdk8tests/TypeAnno7.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.jdk8tests; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.CLASS) 9 | @Target(ElementType.TYPE_USE) 10 | public @interface TypeAnno7 { 11 | } 12 | -------------------------------------------------------------------------------- /jdk8tests/src/main/java/de/schauderhaft/degraph/jdk8tests/TypeAnno8.java: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.jdk8tests; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.CLASS) 9 | @Target(ElementType.TYPE_USE) 10 | public @interface TypeAnno8 { 11 | } 12 | -------------------------------------------------------------------------------- /jdk8tests/src/test/scala/de/schauderhaft/degraph/jdk8tests/AnalyzerTest.scala: -------------------------------------------------------------------------------- 1 | package de.schauderhaft.degraph.jdk8tests 2 | 3 | import de.schauderhaft.degraph.analysis._ 4 | import de.schauderhaft.degraph.graph.Graph 5 | import de.schauderhaft.degraph.model.SimpleNode 6 | import SimpleNode._ 7 | import org.junit.runner.RunWith 8 | import org.scalatest.FunSuite 9 | import org.scalatest.junit.JUnitRunner 10 | import org.scalatest.matchers.{MatchResult, Matcher} 11 | import org.scalatest.Matchers._ 12 | 13 | 14 | @RunWith(classOf[JUnitRunner]) 15 | class AnalyzerTest extends FunSuite { 16 | 17 | private val testClassFolder = System.getProperty("java.class.path") 18 | 19 | private def isJavaVersionWithBrokenTypeAnnotations: Boolean = { 20 | val javaVersionString = System.getProperty("java.version") 21 | javaVersionString > "1.8.0_31" 22 | } 23 | 24 | private val graphs = 25 | if (isJavaVersionWithBrokenTypeAnnotations) 26 | Map() 27 | else 28 | Map( 29 | "asm" -> asm.Analyzer.analyze(testClassFolder, x => x, _ => true)) 30 | 31 | 32 | for ((label, graph) <- graphs) { 33 | def stringNodes = graph.topNodes.map(_.toString) 34 | 35 | def nodeByString(name: String) = graph.topNodes.find { 36 | case n: SimpleNode => n.name == name 37 | case _ => false 38 | } 39 | 40 | def test(name: String)(testFun: => Unit) = super.test("%s (%s)".format(name, label))(testFun) 41 | 42 | test("Selftest: nodeByString works") { 43 | nodeByString("java.lang.String") should be(Some(classNode("java.lang.String"))) 44 | } 45 | 46 | 47 | test("Dependency from class to type annotation on implements is found") { 48 | graph should connect("de.schauderhaft.degraph.jdk8tests.ClassWithTypeAnnotations" -> "de.schauderhaft.degraph.jdk8tests.TypeAnno1") 49 | } 50 | test("Dependency from class to type annotation on type parameter") { 51 | graph should connect("de.schauderhaft.degraph.jdk8tests.ClassWithTypeAnnotations" -> "de.schauderhaft.degraph.jdk8tests.TypeAnno2") 52 | } 53 | test("Dependency from class to type annotation on constructor is found") { 54 | graph should connect("de.schauderhaft.degraph.jdk8tests.ClassWithTypeAnnotations" -> "de.schauderhaft.degraph.jdk8tests.TypeAnno3") 55 | } 56 | test("Dependency from class to type annotation on cast is found") { 57 | graph should connect("de.schauderhaft.degraph.jdk8tests.ClassWithTypeAnnotations" -> "de.schauderhaft.degraph.jdk8tests.TypeAnno4") 58 | } 59 | test("Dependency from class to type annotation on exception is found") { 60 | graph should connect("de.schauderhaft.degraph.jdk8tests.ClassWithTypeAnnotations" -> "de.schauderhaft.degraph.jdk8tests.TypeAnno5") 61 | } 62 | test("Dependency from class to type annotation on field is found") { 63 | graph should connect("de.schauderhaft.degraph.jdk8tests.ClassWithTypeAnnotations" -> "de.schauderhaft.degraph.jdk8tests.TypeAnno6") 64 | } 65 | // test("Dependency from class to type annotation on type in catch clause is found") { 66 | // graph should connect("de.schauderhaft.degraph.jdk8tests.ClassWithTypeAnnotations" -> "de.schauderhaft.degraph.jdk8tests.TypeAnno7") 67 | // } 68 | // test("Dependency from class to type annotation on local variable is found") { 69 | // graph should connect("de.schauderhaft.degraph.jdk8tests.ClassWithTypeAnnotations" -> "de.schauderhaft.degraph.jdk8tests.TypeAnno8") 70 | // } 71 | 72 | 73 | def connect(connection: (String, String)) = { 74 | val (from, to) = connection 75 | new Matcher[Graph] { 76 | override def apply(graph: Graph) = { 77 | var messages = List[String]() 78 | val toNode = nodeByString(to) 79 | if (toNode.isEmpty) 80 | messages = "there is no node %s in the graph %s".format(to, graph) :: messages 81 | val fromNode = nodeByString(from) 82 | if (fromNode.isEmpty) 83 | messages = "there is no node %s in the graph %s".format(from, graph) :: messages 84 | else { 85 | val connections = graph.connectionsOf(fromNode.get) 86 | if (messages.isEmpty && !connections.contains(toNode.get)) 87 | messages = "there is no connection from %s to %s in %s. The only connections are %s".format(from, to, graph, connections) :: messages 88 | } 89 | new MatchResult( 90 | toNode.nonEmpty 91 | && fromNode.nonEmpty 92 | && graph.connectionsOf(fromNode.get).contains(toNode.get), 93 | messages.mkString(","), 94 | "There is a connection from %s to %s in %s".format(from, to, graph)) 95 | } 96 | } 97 | } 98 | } 99 | 100 | // if annotations are broken, at least verify that broken stuff doesn't completely kill the analysis 101 | if (isJavaVersionWithBrokenTypeAnnotations) { 102 | test("Analysis does not explode") { 103 | asm.Analyzer.analyze(testClassFolder, x => x, _ => true) 104 | } 105 | } 106 | 107 | } -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright 2012 Jens Schauder 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this software except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | 16 | The distributed form of this application contains various software libraries which come with their own licence terms: 17 | 18 | Licensed under Apache 2.0 (see above) 19 | scalatest 20 | 21 | New BSD License (http://opensource.org/licenses/BSD-3-Clause) 22 | Hamcrest 23 | 24 | Licensed under it own license available in the lib folder of the distribution: 25 | scala-library 26 | scallop 27 | asm 28 | 29 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | **Degraph** is a tool for visualizing and testing class and package dependencies in JVM applications. 2 | 3 | You can [download](http://riy.github.io/degraph/download.html) it, or read the [documentation](http://riy.github.io/degraph/documentation.html). 4 | 5 | ## How to build ## 6 | 7 | I'm highly interested in contributions, so if you are interested, drop me a message and until I respond have a look at the source code. 8 | 9 | Degraph uses gradle as a build tool. Since gradle is awesome you actually don't have to install gradle, you just need a working JDK installation. 10 | 11 | Just get the sourcecode and run one of the following commands: 12 | 13 | To execute all the tests: 14 | 15 | gradlew test 16 | 17 | To create a directory that looks like you just installed Degraph: 18 | 19 | gradlew installApp 20 | 21 | ## Roadmap ## 22 | 23 | Degraph allows to defined dependency constraints using tests written in Scala with ScalaTest or Java with JUnit. 24 | This feature will need quite some 25 | tweaking in the future: 26 | 27 | * Support for ignoring dependencies 28 | * Separating the decision to ignore dependencies not mentioned in a constraint and if one can skip steps in the chain of dependencies (i.e. splitting allow/allowDirect into multiple DSL artifacts 29 | * Creating a graph on test failure that visualizes the constraint violations. 30 | 31 | Also while yed is a cool Graph editor it does way more then Degraph actually needs and misses some other features. So a standaloe (or IDE Plugin based) GUI would be nice. I'm experimenting with an HTML5 based solution. 32 | 33 | If you have more ideas what Degraph should be able to do, just open an [issue](https://github.com/riy/degraph/issues). 34 | 35 | 36 | ## Feedback ## 37 | 38 | Please use [the github issue feature](https://github.com/riy/degraph/issues) for questions, bug reports or improvement requests. 39 | 40 | If you like Degraph just say so on twitter, facebook or wherever you like. 41 | 42 | ## Build Info ## 43 | 44 | [![Build Status](https://api.shippable.com/projects/55a4b660edd7f2c0526bb24c/badge?branch=master)](https://app.shippable.com/projects/55a4b660edd7f2c0526bb24c) 45 | 46 | ## Original Author ## 47 | 48 | [Jens Schauder](https://github.com/schauder) 49 | -------------------------------------------------------------------------------- /releaseNotes.md: -------------------------------------------------------------------------------- 1 | # Release Notes 2 | 3 | ## 0.1.5 4 | 5 | - Upgraded Gradle from `3.0` to `6.0` in order to make Degraph build with Java 11. 6 | - Specified Java `1.8` as `sourceCompatibility` and `targetCompatibility`. 7 | - Building on the latest Shippable image (v7.2.4) using the new `shippable.yml` format 8 | 9 | ## 0.1.4 10 | 11 | ### New Features 12 | 13 | - NamedPattern warns, when the name looks like a pattern. Multiple users experienced confusing 14 | results, due to providing the parameters in wrong order. 15 | 16 | - `printTo` in the ConstraintBuilder now always prints the analysis results. If you want the old behaviour back, use 17 | `printOnFailure` 18 | 19 | - `Check` and `JCheck` got a new method `customClasspath`. You can use it in order to provide a classpath explicitly 20 | instead of using the classpath used by the current JVM. 21 | 22 | - ConstraintBuilder got a new method `filterClasspath(pattern: String): ConstraintBuilder` 23 | for limiting the classpath to elements matching the pattern. 24 | This should be useful, if you want to analyze some, but not all jar-files. 25 | 26 | - ConstraintBuilder got a new method `filterClasspath(filter: (String) => Boolean): ConstraintBuilder` 27 | for limiting the classpath to elements matching the pattern. 28 | This should be useful, if you want to analyze some, but not all jar-files. 29 | 30 | - minor performance improvements 31 | 32 | ### Known Issues 33 | 34 | - Due to a JDK or ASM bug analysis of class files which contain Type-Annotations might fail. Dependencies of such classes 35 | will be incomplete. A message saying so will be displayed. See 36 | 37 | ## 0.1.3 38 | 39 | Version 0.1.3 is the same as 0.1.2 40 | 41 | ## 0.1.2 42 | 43 | ### Bugfixes 44 | 45 | - Fixed the build file so it works without any special properties, like usernames, password and private keys, 46 | that are only needed for releasing [#56](https://github.com/schauder/degraph/issues/56) 47 | 48 | - Fixed [Type annotations cause RuntimeException (#55)](https://github.com/schauder/degraph/issues/55) 49 | 50 | ### New Features 51 | 52 | - Java 8 Type Annotations get analyzed. 53 | 54 | ## 0.1.1 55 | 56 | ### Bugfixes 57 | 58 | - classes referenced in an array typed parameter of an annotation where not recognized as a dependency. This is fixed. 59 | 60 | - static methods that get referenced didn't create the appropriate dependency https://github.com/schauder/degraph/issues/53 61 | 62 | ### New Features 63 | 64 | - Degraph is now available from Maven Central, so if you are using it as a library you can specify it in your 65 | ´build.gradle´ file or ´pom.xml´ or whatever. The maven syntax for the dependency you need for tests is: 66 | 67 | 68 | de.schauderhaft.degraph 69 | degraph-check 70 | 0.1.1 71 | 72 | 73 | - ´.printTo("somepath.graphml")´ you can create a graphml diagram on test failure. 74 | 75 | ## 0.1.0 76 | 77 | ### Bugfixes 78 | 79 | - in the previous releases Degraph used DependencyFinder for analyzing dependencies. Unfortunately this missed many 80 | dependencies based on java language features post 1.5. I switched to ASM, which finds many more dependencies. 81 | Hopefully all, but to be honest: I can't really tell. Bug reports are highly welcome. 82 | 83 | ### New Features 84 | 85 | - Split into three modules: 86 | 87 | - core contains the main logic and algorithm. Use this when you want to build your own stuff based on 'my' algorithms 88 | 89 | - app contains the command line application. Also contains the parser for the configuration files 90 | 91 | - check contains the DSL for testing dependencies in your tests 92 | 93 | Since I strongly believe that a package should exist only in one module, some package names had to change as well. 94 | 95 | 96 | - you now can define and test dependency constraints using Java http://schauder.github.io/degraph/documentation.html for details 97 | 98 | ## 0.0.4 99 | 100 | ### Bugfixes 101 | 102 | - in previous releases dependencies only used inside a method didn't get picked up by Degraph. This is now fixed. 103 | 104 | ### New Features 105 | 106 | - you can now define and test dependency constraints, see http://schauder.github.io/degraph/documentation.html for details 107 | 108 | ### 0.0.3 109 | 110 | includes & excludes now use the ant like syntax used for slicing. This makes them easier to use. 111 | They loose some of the theoretical power, but that was of little use for this purpose anyway. 112 | 113 | Edges have now smooth corners making following them with the eye easier. 114 | 115 | For each slicing a check for circular dependencies is implemented. 116 | If a cycle is found all dependencies contributing to that circle get colored red. 117 | For each slicing there is at most one circle detected. 118 | So after breaking that circle Degraph might confront you with another one. 119 | This is a easy to implement solution for the final goal of marking a small as 120 | possible set of edges to break in order to get a directed acylcic graph for all slicings 121 | 122 | -g option removed 123 | 124 | ### 0.0.2 125 | 126 | Configuration files introduced. Thereby achieving a usable way to define slices. 127 | See https://github.com/schauder/degraph/wiki/Configuration-File-Format for usage 128 | 129 | Graph now has nice colors 130 | 131 | ### 0.0.1 132 | 133 | Basic graph creation 134 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include 'core', 'degraph', 'check', 'jdk8tests' 2 | -------------------------------------------------------------------------------- /shippable.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | build: 4 | pre_ci_boot: 5 | image_name: drydock/u16javall 6 | image_tag: v7.2.4 7 | pull: true 8 | 9 | ci: 10 | # specify JDK version 11 | - source shipctl jdk set oraclejdk11 12 | 13 | # preparation output folders 14 | - mkdir -p shippable/testresults 15 | - mkdir -p shippable/codecoverage 16 | 17 | # build 18 | - ./gradlew clean assemble 19 | 20 | on_success: 21 | # install 22 | - ./gradlew clean check install 23 | 24 | always: 25 | # prepare reports 26 | - cp -r */build/test-results/* shippable/testresults/ 27 | -------------------------------------------------------------------------------- /todo.md: -------------------------------------------------------------------------------- 1 | * speed up tests 2 | * refactor Categories to Slicing 3 | * Testcase for JDK8: Casting to multiple interfaces 4 | 5 | **Layout Algorithms** 6 | * http://en.wikipedia.org/wiki/Layered_graph_drawing 7 | * http://www.graphviz.org/Documentation/TSE93.pdf 8 | * http://en.wikipedia.org/wiki/Coffman%E2%80%93Graham_algorithm 9 | * http://en.wikipedia.org/wiki/Layered_graph_drawing especially first bullet point 10 | * http://graphstream-project.org/doc/Tutorials/Getting-Started_1.0/ 11 | 12 | **Other Resources and Stuff** 13 | * http://stackoverflow.com/questions/4421882/how-to-unit-test-package-dependencies-in-scala 14 | * Spanning Tree as Basis for Layout of cyclic directed graphs 15 | 16 | **Todo for release** 17 | * update release notes 18 | * set version in CommandLineParser 19 | * set version in example1.config 20 | * release 21 | * upload distribution files 22 | * update download links in readme 23 | * test examples 24 | * check for version numbers in the wiki pages 25 | 26 | --------------------------------------------------------------------------------