diff --git a/.buildscript/deploy_snapshot.sh b/.buildscript/deploy_snapshot.sh new file mode 100755 index 0000000..3acea63 --- /dev/null +++ b/.buildscript/deploy_snapshot.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# +# Deploy a jar, source jar, and javadoc jar to Sonatype's snapshot repo. +# +# Adapted from https://coderwall.com/p/9b_lfq and +# http://benlimmer.com/2013/12/26/automatically-publish-javadoc-to-gh-pages-with-travis-ci/ and +# https://github.com/JakeWharton/RxBinding/blob/master/.buildscript/deploy_snapshot.sh + +SLUG="vanniktech/gradle-dependency-graph-generator-plugin" +JDK="oraclejdk8" +BRANCH="master" + +set -e + +if [ "$TRAVIS_REPO_SLUG" != "$SLUG" ]; then + echo "Skipping snapshot deployment: wrong repository. Expected '$SLUG' but was '$TRAVIS_REPO_SLUG'." +elif [ "$TRAVIS_JDK_VERSION" != "$JDK" ]; then + echo "Skipping snapshot deployment: wrong JDK. Expected '$JDK' but was '$TRAVIS_JDK_VERSION'." +elif [ "$TRAVIS_PULL_REQUEST" != "false" ]; then + echo "Skipping snapshot deployment: was pull request." +elif [ "$TRAVIS_BRANCH" != "$BRANCH" ]; then + echo "Skipping snapshot deployment: wrong branch. Expected '$BRANCH' but was '$TRAVIS_BRANCH'." +else + echo "Deploying snapshot..." + ./gradlew uploadArchives + echo "Snapshot deployed!" +fi \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7ebd2b7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,2 @@ +[*.{kt,kts}] +indent_size=2 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..7da95b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.gradle/ +build/ +out/ + +.idea/ +*.iml \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..ea5de42 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,38 @@ +language: groovy + +groovy: + - 2.3.6 + - 2.3.9 + +jdk: + - oraclejdk8 + +before_install: + - pip install --user codecov + +install: true + +script: ./gradlew clean build + +after_success: + - codecov + - .buildscript/deploy_snapshot.sh + +env: + global: + - secure: "fo9HUps/nNCI2bW9yzGCB79lhh0EWBPXz9WBro50AT2Fm1E/ofVP2cZG1ZP8hFmt1oJxaRwHuzn6Pq2zNd/b0wuM7xbhnBiWaBkoguYS62N536xSJVi0xvM6VM/oSDqClS1WNH8f44fXB8JB68NwP31mUSc0DC5nJd9MUA2LZEQ8Abj9l04Cu/Iq1CjRi9u0wxkbK2A8/cqjfzfSS63G0E1Z9K+PG8rH+yB4eQK3mfNh+gDiI3Jyc93xM7oahRlGv1nCVdVYvRQG/AvRZQ4EF8UuMP73o4XisBBS2AfGdDUXSi+v3XS18Vz41ynNm4tyF/443M1sSRAz1LGWArGKehR6WG8PJB26K0Ag7WZLkSRzu58Uz6dNNcjztrUK+PUuvEuLK1WKjeeqLCEDCVgWT9ujQE/RFMzXaT6rPLP1hnLnWitUU6DUJb5A/JB0lo9lBt6PjTM8RXY7K3x7xV3lVnJDKVTv1EXe5hk2wZxyCtIqR0F3h6E3QIDMC0SFFYPvz7eiiwStIDv7QIZw594jt/xbM94fxGziSP+Ti1mP2GKAPfEtNea6+XVMN4jJgSPpT8vL5vW54WhHAWUkDPL+q/V+lMGuloRN+AS/LOWTX8lF0YF9vO5HmhV8UY6eBEXUhOuSRZBW1RqMEz+yDpS8Tu7eA5lFDmnnheOtledsiQA=" + - secure: "pNKrYMTjU0/Qyn1AG8YnPkxR3q8l61Z34pISGUr9c7sQK9hMiZ45dGYkqNmAoaK/lsiU6hvIOo7DCsxij4RnyLZZmUrrMtr2P9Vw1RxJjoPFtygQ0QVMT+9BE1Zynb//HyaJhVd+S4AYUBtuLs6tn78FkDmRCF9ZVu7B91kOdawIx9bgeJ2kHMileCe3Gpbcp/4/C/umD6xIcV3v6UUJCRKnEMGFj9BerLZHQRwiE18Bjz86nHnrQZEaM0gV1ZSKfA3gINCR6BWboikkk8FpmieiGJIcsS6p8mdzrQ5QeBFzdwFnFi07vbG7U8SBtPFDAnOiyPw/qmInWaK+I2v92KI4jpDfKF1lRr/bxdxLVAowuj45uxoj6z0AiUm95YgnPP34/4yw7aaTz+sGd+5XTqQSvzDtU8mIko8iUcDTdsCZS589qZQFf6HbVPVGSO+3pKgbALg6QY6YJgI5D1uC6O2H/iOPhUaD/+Eujm4ETdlmXhvlZVF4wM+6qvK7m3ms7vEvhcC9Dg4UlIYFIiEYH/faxP2EogL9V6qR+4AjCfPkefq2DOHFrc6FmvJ8hYa7PAyfEKXdErJZob0HB9is5o6Y+IcrC73FswVYxnKAm2BJOfF/i4pGX/PhfkIpzuG4QtefGyCJYBYpG68mdkPPg/wveQeun+R6EKXiWnIqqSM=" + +branches: + except: + - gh-pages + +notifications: + email: false + +sudo: false + +cache: + directories: + - $HOME/.gradle + - $HOME/.m2 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c61ea7d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# Change Log + +Version 0.2.0 *(In development)* +-------------------------------- + +Version 0.1.0 *(2018-03-04)* +---------------------------- + +- Initial release \ No newline at end of file diff --git a/LICENSE b/LICENSE index 261eeb9..f49a4e1 100644 --- a/LICENSE +++ b/LICENSE @@ -198,4 +198,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. + limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100755 index 0000000..a3fa71d --- /dev/null +++ b/README.md @@ -0,0 +1,88 @@ +# gradle-dependency-graph-generator-plugin + +[![Build Status](https://travis-ci.org/vanniktech/gradle-dependency-graph-generator-plugin.svg?branch=master)](https://travis-ci.org/vanniktech/gradle-dependency-graph-generator-plugin?branch=master) +[![Codecov](https://codecov.io/github/vanniktech/gradle-dependency-graph-generator-plugin/coverage.svg?branch=master)](https://codecov.io/github/vanniktech/gradle-dependency-graph-generator-plugin?branch=master) +[![License](http://img.shields.io/:license-apache-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) + +Gradle plugin that lets you visualize your dependencies in a graph. + +# Set up + +```groovy +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath "com.vanniktech:gradle-dependency-graph-generator-plugin:0.1.0" + } +} + +apply plugin: "com.vanniktech.dependency.graph.generator" +``` + +Note that this plugin can be applied at the root of the project or at a specific project. Both cases will just work. + +This plugin is using the `dot` command line tool for generating the graphs hence you need to install it. Mac users can use `brew install graphviz`. Ubuntu users can `sudo apt-get install graphviz`. + +Information: [This plugin is also available on Gradle plugins](https://plugins.gradle.org/plugin/com.vanniktech.dependency.graph.generator) + +### Snapshots + +Can be found [here](https://oss.sonatype.org/#nexus-search;quick~gradle-dependency-graph-generator-plugin). Current one is: + +```groovy +classpath "com.vanniktech:gradle-dependency-graph-generator-plugin:0.2.0-SNAPSHOT" +``` + +## Usage + +By default this plugin will generate a `generateDependencyGraph` task that can be used to generate a dependency graph that could look like this. This graph was generated from my [chess clock app](https://play.google.com/store/apps/details?id=com.vanniktech.chessclock). + +![Example graph.](example.png) + +There are extension points to be able to generate graphs which only include some dependencies and their transitive ones. The trick is to hook a [Generator](./src/main/kotlin/com/vanniktech/dependency/graph/generator/DependencyGraphGeneratorExtension.kt) in over the `dependencyGraphGenerator` extension. Note that this is extremely experimental and will likely change between releases. It's still fun though. + +### Generator Example + +We only want to show which Firebase libraries we're using and give them the typical Firebase orange. + +```groovy +import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorPlugin +import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorExtension.Generator +import com.vanniktech.dependency.graph.generator.dot.GraphFormattingOptions +import com.vanniktech.dependency.graph.generator.dot.Color +import com.vanniktech.dependency.graph.generator.dot.Shape +import com.vanniktech.dependency.graph.generator.dot.Style + +plugins.apply(DependencyGraphGeneratorPlugin) + +def firebaseGenerator = new Generator( + "firebaseLibraries", // Suffix for our Gradle task. + "", // Root suffix that we don't want in this case. + { dependency -> dependency.getModuleGroup().startsWith("com.google.firebase") }, // Only want Firebase. + { dependency -> true }, // Include transitive dependencies. + { dependency -> new GraphFormattingOptions(Shape.BOX, Style.FILLED, Color.fromRgb(255, 203, 43)) }, // Give them some color. +) + +dependencyGraphGenerator { + generators = [ firebaseGenerator ] +} +``` + +This will generate a new task `generateDependencyGraphFirebaseLibraries` which when run will yield this graph: + +![Example Firebase graph.](example-firebase.png) + +Note that when using the `dependencyGraphGenerator` extension with custom generators you loose the default one, to add it back simply use the `Generator.ALL` instance: + +```groovy +dependencyGraphGenerator { + generators = [ Generator.ALL, firebaseGenerator ] +} +``` +# License + +Copyright (C) 2018 Vanniktech - Niklas Baudy + +Licensed under the Apache License, Version 2.0 \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100755 index 0000000..2f9b847 --- /dev/null +++ b/build.gradle @@ -0,0 +1,133 @@ +buildscript { + ext.kotlinVersion = '1.2.30' + + repositories { + jcenter() + google() + gradlePluginPortal() + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" + classpath 'com.gradle.publish:plugin-publish-plugin:0.9.10' + classpath 'com.github.ben-manes:gradle-versions-plugin:0.17.0' + classpath 'com.vanniktech:gradle-code-quality-tools-plugin:0.9.0' + classpath 'com.vanniktech:gradle-android-junit-jacoco-plugin:0.11.0' + } +} + +apply plugin: 'groovy' +apply plugin: 'java-gradle-plugin' +apply plugin: 'kotlin' +apply plugin: 'com.github.ben-manes.versions' +apply plugin: 'com.vanniktech.code.quality.tools' +apply plugin: 'com.vanniktech.android.junit.jacoco' +apply plugin: 'com.gradle.plugin-publish' + +codeQualityTools { + ktlint { + toolVersion = '0.18.0' + } + detekt { + toolVersion = '1.0.0.RC6-3' + } + findbugs { + enabled = false + } + pmd { + enabled = false + } + checkstyle { + enabled = false + } + cpd { + enabled = false + } + errorProne { + enabled = false + } +} + +gradlePlugin { + plugins { + graphGeneratorDependencyPlugin { + id = 'com.vanniktech.dependency.graph.generator' + implementationClass = 'com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorPlugin' + } + } +} + +repositories { + jcenter() + google() + gradlePluginPortal() +} + +dependencies { + implementation localGroovy() + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" + implementation gradleApi() + + testImplementation 'junit:junit:4.12' + testImplementation 'org.assertj:assertj-core:3.9.1' + testImplementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion" +} + +sourceCompatibility = JavaVersion.VERSION_1_7 + +group = GROUP +version = VERSION_NAME + +pluginBundle { + website = POM_URL + vcsUrl = POM_SCM_URL + + plugins { + dependencyGraphGenerator { + id = 'com.vanniktech.dependency.graph.generator' + displayName = POM_NAME + tags = ['gradle', 'graph', 'dependencies', 'graphviz', 'dot'] + description = POM_DESCRIPTION + } + } +} + +task wrapper(type: Wrapper) { + gradleVersion = '4.6' + distributionType = Wrapper.DistributionType.ALL +} + +task groovydocJar(type: Jar, dependsOn: groovydoc) { + classifier = 'groovydoc' + from groovydoc.destinationDir +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +task sourcesJar(type: Jar) { + classifier = 'sources' + from sourceSets.main.allSource +} + +artifacts { + archives jar + archives groovydocJar + archives javadocJar + archives sourcesJar +} + +apply from: file('gradle/gradle-mvn-push.gradle') + +apply plugin: 'maven-publish' + +publishing { + publications { + main(MavenPublication) { + from components.java + artifact sourcesJar + } + } +} diff --git a/code_quality_tools/detekt.yml b/code_quality_tools/detekt.yml new file mode 100644 index 0000000..900b2e2 --- /dev/null +++ b/code_quality_tools/detekt.yml @@ -0,0 +1,19 @@ +failFast: true + +potential-bugs: + UnsafeCast: + active: false # We know what we're doing. + LateinitUsage: + active: false # We know what we're doing. + +style: + TopLevelPropertyNaming: + active: false + MaxLineLength: + active: false + +comments: + UndocumentedPublicClass: + active: false + UndocumentedPublicFunction: + active: false \ No newline at end of file diff --git a/example-firebase.png b/example-firebase.png new file mode 100644 index 0000000..a816862 Binary files /dev/null and b/example-firebase.png differ diff --git a/example.png b/example.png new file mode 100644 index 0000000..9f203f1 Binary files /dev/null and b/example.png differ diff --git a/gradle.properties b/gradle.properties new file mode 100755 index 0000000..be62ddc --- /dev/null +++ b/gradle.properties @@ -0,0 +1,21 @@ +GROUP=com.vanniktech +VERSION_NAME=0.2.0-SNAPSHOT + +POM_ARTIFACT_ID=gradle-dependency-graph-generator-plugin +POM_NAME=Gradle Graph Generator Plugin +POM_PACKAGING=jar + +POM_DESCRIPTION=Gradle plugin that generates dependency graphs from your project. +POM_INCEPTION_YEAR=2018 + +POM_URL=http://github.com/vanniktech/gradle-dependency-graph-generator-plugin/ +POM_SCM_URL=http://github.com/vanniktech/gradle-dependency-graph-generator-plugin/ +POM_SCM_CONNECTION=scm:git:git://github.com/vanniktech/gradle-dependency-graph-generator-plugin.git +POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/vanniktech/gradle-dependency-graph-generator-plugin.git + +POM_LICENCE_NAME=The Apache Software License, Version 2.0 +POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENCE_DIST=repo + +POM_DEVELOPER_ID=vanniktech +POM_DEVELOPER_NAME=Niklas Baudy \ No newline at end of file diff --git a/gradle/gradle-mvn-push.gradle b/gradle/gradle-mvn-push.gradle new file mode 100755 index 0000000..c4c6c5e --- /dev/null +++ b/gradle/gradle-mvn-push.gradle @@ -0,0 +1,85 @@ +/* + * Copyright 2013 Chris Banes + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +apply plugin: 'maven' +apply plugin: 'signing' + +def isReleaseBuild() { + return VERSION_NAME.contains("SNAPSHOT") == false +} + +def getRepositoryUsername() { + return hasProperty('SONATYPE_NEXUS_USERNAME') ? SONATYPE_NEXUS_USERNAME : System.env.SONATYPE_NEXUS_USERNAME +} + +def getRepositoryPassword() { + return hasProperty('SONATYPE_NEXUS_PASSWORD') ? SONATYPE_NEXUS_PASSWORD : System.env.SONATYPE_NEXUS_PASSWORD +} + +afterEvaluate { project -> + uploadArchives { + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + pom.groupId = GROUP + pom.artifactId = POM_ARTIFACT_ID + pom.version = VERSION_NAME + + repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { + authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) + } + snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { + authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) + } + + pom.project { + name POM_NAME + packaging POM_PACKAGING + description POM_DESCRIPTION + url POM_URL + inceptionYear POM_INCEPTION_YEAR + + scm { + url POM_SCM_URL + connection POM_SCM_CONNECTION + developerConnection POM_SCM_DEV_CONNECTION + } + + licenses { + license { + name POM_LICENCE_NAME + url POM_LICENCE_URL + distribution POM_LICENCE_DIST + } + } + + developers { + developer { + id POM_DEVELOPER_ID + name POM_DEVELOPER_NAME + } + } + } + } + } + } + + signing { + required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } + sign configurations.archives + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f6b961f Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..9a4163a --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..cccdd3d --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100755 index 0000000..f955316 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/main/kotlin/com/vanniktech/dependency/graph/generator/DependencyGraphGeneratorExtension.kt b/src/main/kotlin/com/vanniktech/dependency/graph/generator/DependencyGraphGeneratorExtension.kt new file mode 100644 index 0000000..3d65283 --- /dev/null +++ b/src/main/kotlin/com/vanniktech/dependency/graph/generator/DependencyGraphGeneratorExtension.kt @@ -0,0 +1,51 @@ +package com.vanniktech.dependency.graph.generator + +import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorExtension.Generator.Companion.ALL +import com.vanniktech.dependency.graph.generator.dot.GraphFormattingOptions +import com.vanniktech.dependency.graph.generator.dot.Header +import org.gradle.api.artifacts.ResolvedDependency + +/** + * Extension for dependency graph generation. + * @since 0.1.0 + */ +open class DependencyGraphGeneratorExtension { + var generators: List = listOf(ALL) + + /** + * Generator allows you to customize which dependencies you want to select. Further you can tweak some of the formatting. + * @since 0.1.0 + */ + data class Generator @JvmOverloads constructor( + /** + * The name of this type of generator that should be in lowerCamelCase. + * The task name as well as the output files will use this name. + */ + val name: String = "", + /** Optional suffix for the root - the project this generator is applied too. */ + val rootSuffix: String? = null, + /** Return true when you want to include this dependency, false otherwise. */ + val include: (ResolvedDependency) -> Boolean = { true }, + /** Return true when you want to include the children of this dependency, false otherwise. */ + val children: (ResolvedDependency) -> Boolean = { true }, + /** Allows to tweak the formatting of a single dependency in the graph. */ + val dependencyFormattingOptions: (ResolvedDependency) -> GraphFormattingOptions = { GraphFormattingOptions() }, + /** The formatting options for the root - the project this generator is applied too. */ + val rootFormattingOptions: GraphFormattingOptions = GraphFormattingOptions(), + /** Optional header that can be displayed wrapped around the graph. */ + val header: Header? = null + ) { + /** Gradle task name that is associated with this generator. */ + val gradleTaskName = "generateDependencyGraph${name.capitalize()}" + private val outputFileName = "dependency-graph${name.toHyphenCase().nonEmptyPrepend("-")}" + internal val outputFileNameDot = "$outputFileName.dot" + + /** Output file name of the genreated png which is stored on module level. */ + val outputFileNamePng = "$outputFileName.png" + + companion object { + /** Default behavior which will include everything as is. */ + @JvmStatic val ALL = Generator() + } + } +} diff --git a/src/main/kotlin/com/vanniktech/dependency/graph/generator/DependencyGraphGeneratorPlugin.kt b/src/main/kotlin/com/vanniktech/dependency/graph/generator/DependencyGraphGeneratorPlugin.kt new file mode 100644 index 0000000..04e480c --- /dev/null +++ b/src/main/kotlin/com/vanniktech/dependency/graph/generator/DependencyGraphGeneratorPlugin.kt @@ -0,0 +1,23 @@ +package com.vanniktech.dependency.graph.generator + +import org.gradle.api.Plugin +import org.gradle.api.Project +import java.io.File + +open class DependencyGraphGeneratorPlugin : Plugin { + override fun apply(project: Project) { + val extension = project.extensions.create("dependencyGraphGenerator", DependencyGraphGeneratorExtension::class.java) + + project.afterEvaluate { + extension.generators.forEach { + val task = project.tasks.create(it.gradleTaskName, DependencyGraphGeneratorTask::class.java) + task.generator = it + task.group = "reporting" + task.description = "Generates a dependency graph${it.name.nonEmptyPrepend(" for ")}" + task.inputFile = project.buildFile + task.dotOutputFile = File(project.buildDir, it.outputFileNameDot) + task.imageOutputFile = File(project.projectDir, it.outputFileNamePng) + } + } + } +} diff --git a/src/main/kotlin/com/vanniktech/dependency/graph/generator/DependencyGraphGeneratorTask.kt b/src/main/kotlin/com/vanniktech/dependency/graph/generator/DependencyGraphGeneratorTask.kt new file mode 100644 index 0000000..b3621cd --- /dev/null +++ b/src/main/kotlin/com/vanniktech/dependency/graph/generator/DependencyGraphGeneratorTask.kt @@ -0,0 +1,32 @@ +package com.vanniktech.dependency.graph.generator + +import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorExtension.Generator +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction +import java.io.File + +open class DependencyGraphGeneratorTask : DefaultTask() { + lateinit var generator: Generator // TODO does this need to be an input? Quick testing shows no. + @InputFile lateinit var inputFile: File + + @OutputFile lateinit var dotOutputFile: File + @OutputFile lateinit var imageOutputFile: File + + @TaskAction fun run() { + dotOutputFile.writeText(DotGenerator(project, generator).generateContent()) + + project.exec { + it.workingDir = dotOutputFile.parentFile + it.executable = "dot" + it.args("-Tpng", "-O", dotOutputFile.name) + } + + project.copy { + it.from(File(dotOutputFile.parentFile, "${dotOutputFile.name}.png").toString()) + it.into(project.projectDir.toString()) + it.rename("\\.dot", "") + } + } +} diff --git a/src/main/kotlin/com/vanniktech/dependency/graph/generator/DotGenerator.kt b/src/main/kotlin/com/vanniktech/dependency/graph/generator/DotGenerator.kt new file mode 100644 index 0000000..e63fb4c --- /dev/null +++ b/src/main/kotlin/com/vanniktech/dependency/graph/generator/DotGenerator.kt @@ -0,0 +1,80 @@ +package com.vanniktech.dependency.graph.generator + +import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorExtension.Generator +import org.gradle.api.Project +import org.gradle.api.artifacts.ResolvedDependency + +internal class DotGenerator( + private val project: Project, + private val generator: Generator +) { + // We keep a map of dependency to a parent identifier in order to not add unique dependencies more than once. + // One scenario is A depending on B and B on C. + // If a module depends on both A and B the connection from B to C could be wired twice. + private val addedConnections = mutableSetOf>() + + fun generateContent(): String { + // Some general documentation about dot. http://www.graphviz.org/pdf/dotguide.pdf + val content = StringBuilder("digraph G {\n") + + generator.header?.let { + content.append(" $it;\n") + } + + val projects = if (project.subprojects.size > 0) project.subprojects else setOf(project) + + val projectSuffix = generator.rootSuffix ?: "" + + // Generate top level projects. + projects.forEach { + val projectId = it.dotIdentifier(projectSuffix) + val settings = generator.rootFormattingOptions.withLabel(it.name + projectSuffix.nonEmptyPrepend(" ")) + content.append(" $projectId $settings;\n") + } + + // All projects should be displayed on the same level. + val ranking = projects.joinToString(separator = "; ") { "\"${it.dotIdentifier(projectSuffix)}\"" } + content.append(" { rank = same; $ranking };\n") + + // Let's gather everything and put it in the file. + projects + .flatMap { project -> + project.configurations + .filter { it.isCanBeResolved } + .filter { + // TODO this is not optimal but will do for now. + val isCompileClassPath = it.name == "compileClasspath" + val isAndroidProjectClassPath = it.name == "debugCompileClasspath" || it.name == "releaseCompileClasspath" + isCompileClassPath || isAndroidProjectClassPath + } + .flatMap { it.resolvedConfiguration.firstLevelModuleDependencies } + .map { project to it } + } + .forEach { (project, dependency) -> + append(dependency, project.dotIdentifier(projectSuffix), content) + } + + return content.append("}\n").toString() + } + + private fun append(dependency: ResolvedDependency, parentIdentifier: String, content: StringBuilder) { + if (generator.include.invoke(dependency)) { + val name = dependency.moduleName + val identifier = (dependency.moduleGroup + dependency.moduleName).dotIdentifier + + val pair = dependency to parentIdentifier + if (!addedConnections.contains(pair)) { // We don't want to re-add the same dependencies. + addedConnections.add(pair) + + content.append(" $identifier ${generator.dependencyFormattingOptions.invoke(dependency).withLabel(name)};\n") + content.append(" $parentIdentifier -> $identifier;\n") + } + + if (generator.children.invoke(dependency)) { + dependency.children.forEach { append(it, identifier, content) } + } + } + } +} + +private fun Project.dotIdentifier(suffix: String) = (name + suffix).dotIdentifier diff --git a/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/Color.kt b/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/Color.kt new file mode 100644 index 0000000..e47e948 --- /dev/null +++ b/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/Color.kt @@ -0,0 +1,23 @@ +package com.vanniktech.dependency.graph.generator.dot + +data class Color(private val value: String) { + override fun toString() = value + + companion object { + private val HEX_PATTERN = Regex("#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})") + + internal const val MAX_COLOR_VALUE = 255 + + @JvmStatic fun fromHex(hex: String): Color { + require(hex.matches(HEX_PATTERN)) { "Color is not a valid hex color." } + return Color(hex) + } + + @JvmStatic fun fromRgb(r: Int, g: Int, b: Int): Color { + require(r in 0..MAX_COLOR_VALUE) { "r should be in [0..256)" } + require(g in 0..MAX_COLOR_VALUE) { "g should be in [0..256)" } + require(b in 0..MAX_COLOR_VALUE) { "b should be in [0..256)" } + return Color(String.format("#%02x%02x%02x", r, g, b)) + } + } +} diff --git a/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/GraphFormattingOptions.kt b/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/GraphFormattingOptions.kt new file mode 100644 index 0000000..4879586 --- /dev/null +++ b/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/GraphFormattingOptions.kt @@ -0,0 +1,23 @@ +package com.vanniktech.dependency.graph.generator.dot + +import com.vanniktech.dependency.graph.generator.asDotSyntax + +class GraphFormattingOptions @JvmOverloads constructor( + private val shape: Shape = Shape.BOX, + private val style: Style? = null, + private val color: Color? = null +) { + private var label: String? = null + + override fun toString() = "[" + mapOf( + "label" to label, + "shape" to shape, + "style" to style, + "color" to color + ).asDotSyntax() + "]" + + /** Used internally for the label. */ + internal fun withLabel(label: String) = apply { + this.label = label + } +} diff --git a/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/Header.kt b/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/Header.kt new file mode 100644 index 0000000..c18d42f --- /dev/null +++ b/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/Header.kt @@ -0,0 +1,21 @@ +package com.vanniktech.dependency.graph.generator.dot + +import com.vanniktech.dependency.graph.generator.asDotSyntax +import com.vanniktech.dependency.graph.generator.dot.LabelJust.CENTER +import com.vanniktech.dependency.graph.generator.dot.LabelLoc.TOP + +data class Header @JvmOverloads constructor( + val text: String, + val fontsize: Int = 24, + val height: Int = 5, + val labelLoc: LabelLoc = TOP, + val labelJust: LabelJust = CENTER +) { + override fun toString() = mapOf( + "label" to text, + "fontsize" to fontsize, + "height" to height, + "labelloc" to labelLoc, + "labeljust" to labelJust + ).asDotSyntax(separator = " ") +} diff --git a/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/LabelJust.kt b/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/LabelJust.kt new file mode 100644 index 0000000..770c566 --- /dev/null +++ b/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/LabelJust.kt @@ -0,0 +1,9 @@ +package com.vanniktech.dependency.graph.generator.dot + +enum class LabelJust(val string: String) { + LEFT("l"), + CENTER("c"), + RIGHT("r"); + + override fun toString() = string +} diff --git a/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/LabelLoc.kt b/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/LabelLoc.kt new file mode 100644 index 0000000..1d7ce9d --- /dev/null +++ b/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/LabelLoc.kt @@ -0,0 +1,8 @@ +package com.vanniktech.dependency.graph.generator.dot + +enum class LabelLoc(val string: String) { + TOP("t"), + BOTTOM("b"); + + override fun toString() = string +} diff --git a/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/Shape.kt b/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/Shape.kt new file mode 100644 index 0000000..1cfc38c --- /dev/null +++ b/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/Shape.kt @@ -0,0 +1,68 @@ +package com.vanniktech.dependency.graph.generator.dot + +@Suppress("Detekt.EndOfSentenceFormat") // https://github.com/arturbosch/detekt/issues/768 +/** http://www.graphviz.org/doc/info/shapes.html */ +enum class Shape(val string: String) { + BOX("box"), + POLYGON("polygon"), + ELLIPSE("ellipse"), + OVAL("oval"), + CIRCLE("circle"), + POINT("point"), + EGG("egg"), + TRIANGLE("triangle"), + PLAINTEXT("plaintext"), + PLAIN("plain"), + DIAMOND("diamond"), + TRAPEZIUM("trapezium"), + PARALLELOGRAM("parallelogram"), + HOUSE("house"), + PENTAGON("pentagon"), + HEXAGON("hexagon"), + SEPTAGON("septagon"), + OCTAGON("octagon"), + DOUBLECIRCLE("doublecircle"), + DOUBLEOCTAGON("doubleoctagon"), + TRIPLEOCTAGON("tripleoctagon"), + INVTRIANGLE("invtriangle"), + INVTRAPEZIUM("invtrapezium"), + INVHOUSE("invhouse"), + MDIAMOND("Mdiamond"), + MSQUARE("Msquare"), + MCIRCLE("Mcircle"), + RECT("rect"), + RECTANGLE("rectangle"), + SQUARE("square"), + STAR("star"), + NONE("none"), + UNDERLINE("underline"), + CYLINDER("cylinder"), + NOTE("note"), + TAB("tab"), + FOLDER("folder"), + @Suppress("Detekt.EnumNaming") // Not yet released https://github.com/arturbosch/detekt/pull/717/ + BOX3D("box3d"), + COMPONENT("component"), + PROMOTER("promoter"), + CDS("cds"), + TERMINATOR("terminator"), + UTR("utr"), + PRIMERSITE("primersite"), + RESTRICTIONSITE("restrictionsite"), + FIVEPOVERHANG("fivepoverhang"), + THREEPOVERHANG("threepoverhang"), + NOVERHANG("noverhang"), + ASSEMBLY("assembly"), + SIGNATURE("signature"), + INSULATOR("insulator"), + RIBOSITE("ribosite"), + RNASTAB("rnastab"), + PROTEASESITE("proteasesite"), + PROTEINSTAB("proteinstab"), + RPROMOTER("rpromoter"), + RARROW("rarrow"), + LARROW("larrow"), + LPROMOTER("lpromoter"); + + override fun toString() = string +} diff --git a/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/Style.kt b/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/Style.kt new file mode 100644 index 0000000..1ecbcc8 --- /dev/null +++ b/src/main/kotlin/com/vanniktech/dependency/graph/generator/dot/Style.kt @@ -0,0 +1,14 @@ +package com.vanniktech.dependency.graph.generator.dot + +enum class Style(val string: String) { + SOLID("solid"), + DASHED("dashed"), + FILLED("filled"), + DIAGONALS("diagonals"), + ROUNDED("rounded"), + DOTTED("dotted"), + BOLD("bold"), + INVIS("invis"); + + override fun toString() = string +} diff --git a/src/main/kotlin/com/vanniktech/dependency/graph/generator/extensions.kt b/src/main/kotlin/com/vanniktech/dependency/graph/generator/extensions.kt new file mode 100644 index 0000000..099fa4d --- /dev/null +++ b/src/main/kotlin/com/vanniktech/dependency/graph/generator/extensions.kt @@ -0,0 +1,24 @@ +package com.vanniktech.dependency.graph.generator + +@Suppress("Detekt.TopLevelPropertyNaming") // https://github.com/arturbosch/detekt/issues/769 +private val WHITESPACE_REGEX = Regex("\\s") + +internal val String.dotIdentifier get() = replace("-", "") + .replace(".", "") + .replace(WHITESPACE_REGEX, "") + +internal fun String.nonEmptyPrepend(prepend: String) = + if (isNotEmpty()) prepend + this else this + +internal fun String.toHyphenCase(): String { + if (isBlank()) return this + + return this[0].toLowerCase().toString() + toCharArray() + .map { it.toString() } + .drop(1) + .joinToString(separator = "") { if (it[0].isUpperCase()) "-${it[0].toLowerCase()}" else it } +} + +internal fun Map.asDotSyntax(separator: String = ", ") = filter { (_, value) -> value != null } + .map { (key, value) -> "$key=\"$value\"" } + .joinToString(separator) diff --git a/src/test/java/com/vanniktech/dependency/graph/generator/DependencyGraphGeneratorExtensionTest.kt b/src/test/java/com/vanniktech/dependency/graph/generator/DependencyGraphGeneratorExtensionTest.kt new file mode 100644 index 0000000..64ed605 --- /dev/null +++ b/src/test/java/com/vanniktech/dependency/graph/generator/DependencyGraphGeneratorExtensionTest.kt @@ -0,0 +1,12 @@ +package com.vanniktech.dependency.graph.generator + +import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorExtension.Generator.Companion.ALL +import org.assertj.core.api.Java6Assertions.assertThat +import org.junit.Test + +class DependencyGraphGeneratorExtensionTest { + @Test fun defaults() { + val defaults = DependencyGraphGeneratorExtension() + assertThat(defaults.generators).containsExactly(ALL) + } +} diff --git a/src/test/java/com/vanniktech/dependency/graph/generator/DotGeneratorTest.kt b/src/test/java/com/vanniktech/dependency/graph/generator/DotGeneratorTest.kt new file mode 100644 index 0000000..7a065af --- /dev/null +++ b/src/test/java/com/vanniktech/dependency/graph/generator/DotGeneratorTest.kt @@ -0,0 +1,202 @@ +package com.vanniktech.dependency.graph.generator + +import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorExtension.Generator.Companion.ALL +import com.vanniktech.dependency.graph.generator.dot.Header +import com.vanniktech.dependency.graph.generator.dot.Color.Companion.MAX_COLOR_VALUE +import com.vanniktech.dependency.graph.generator.dot.Shape +import com.vanniktech.dependency.graph.generator.dot.Style +import com.vanniktech.dependency.graph.generator.dot.Color +import com.vanniktech.dependency.graph.generator.dot.GraphFormattingOptions +import org.assertj.core.api.Java6Assertions.assertThat +import org.gradle.api.Project +import org.gradle.api.artifacts.ResolvedDependency +import org.gradle.api.plugins.JavaLibraryPlugin +import org.gradle.testfixtures.ProjectBuilder +import org.junit.Before +import org.junit.Test +import java.util.Random + +class DotGeneratorTest { + private lateinit var singleEmpty: Project + private lateinit var singleProject: Project + + private lateinit var multiProject: Project + + @Before fun setUp() { + singleEmpty = ProjectBuilder.builder().withName("singleempty").build() + + singleProject = ProjectBuilder.builder().withName("single").build() + singleProject.plugins.apply(JavaLibraryPlugin::class.java) + singleProject.repositories.run { add(mavenCentral()) } + singleProject.dependencies.add("api", "org.jetbrains.kotlin:kotlin-stdlib:1.2.30") + singleProject.dependencies.add("implementation", "io.reactivex.rxjava2:rxjava:2.1.10") + + multiProject = ProjectBuilder.builder().withName("multi").build() + + val multiProject1 = ProjectBuilder.builder().withParent(multiProject).withName("multi1").build() + multiProject1.plugins.apply(JavaLibraryPlugin::class.java) + multiProject1.repositories.run { add(mavenCentral()) } + multiProject1.dependencies.add("api", "org.jetbrains.kotlin:kotlin-stdlib:1.2.30") + multiProject1.dependencies.add("implementation", "io.reactivex.rxjava2:rxjava:2.1.10") + + val multiProject2 = ProjectBuilder.builder().withParent(multiProject).withName("multi2").build() + multiProject2.plugins.apply(JavaLibraryPlugin::class.java) + multiProject2.repositories.run { add(mavenCentral()) } + multiProject2.dependencies.add("implementation", "io.reactivex.rxjava2:rxjava:2.1.10") + multiProject2.dependencies.add("implementation", "io.reactivex.rxjava2:rxandroid:2.0.2") + } + + @Test fun singleProjectEmptyAll() { + assertThat(DotGenerator(singleEmpty, ALL).generateContent()).isEqualTo(""" + |digraph G { + | singleempty [label="singleempty", shape="box"]; + | { rank = same; "singleempty" }; + |} + |""".trimMargin()) + } + + @Test fun singleProjectEmptyAllHeader() { + assertThat(DotGenerator(singleEmpty, ALL.copy(header = Header("my custom header"))).generateContent()).isEqualTo(""" + |digraph G { + | label="my custom header" fontsize="24" height="5" labelloc="t" labeljust="c"; + | singleempty [label="singleempty", shape="box"]; + | { rank = same; "singleempty" }; + |} + |""".trimMargin()) + } + + @Test fun singleProjectEmptyAllRootSuffix() { + assertThat(DotGenerator(singleEmpty, ALL.copy(rootSuffix = "my suffix")).generateContent()).isEqualTo(""" + |digraph G { + | singleemptymysuffix [label="singleempty my suffix", shape="box"]; + | { rank = same; "singleemptymysuffix" }; + |} + |""".trimMargin()) + } + + @Test fun singleProjectEmptyAllRootFormatted() { + assertThat(DotGenerator(singleEmpty, ALL.copy(rootFormattingOptions = GraphFormattingOptions(Shape.EGG, Style.DOTTED, Color.fromHex("#ff0099")))).generateContent()).isEqualTo(""" + |digraph G { + | singleempty [label="singleempty", shape="egg", style="dotted", color="#ff0099"]; + | { rank = same; "singleempty" }; + |} + |""".trimMargin()) + } + + @Test fun singleProjectAll() { + assertThat(DotGenerator(singleProject, ALL).generateContent()).isEqualTo(""" + |digraph G { + | single [label="single", shape="box"]; + | { rank = same; "single" }; + | orgjetbrainskotlinkotlinstdlib [label="kotlin-stdlib", shape="box"]; + | single -> orgjetbrainskotlinkotlinstdlib; + | orgjetbrainsannotations [label="annotations", shape="box"]; + | orgjetbrainskotlinkotlinstdlib -> orgjetbrainsannotations; + | ioreactivexrxjava2rxjava [label="rxjava", shape="box"]; + | single -> ioreactivexrxjava2rxjava; + | orgreactivestreamsreactivestreams [label="reactive-streams", shape="box"]; + | ioreactivexrxjava2rxjava -> orgreactivestreamsreactivestreams; + |} + |""".trimMargin()) + } + + @Test fun singleProjectAllDependencyFormattingOptions() { + // Generate a color for each dependency. + val dependencyFormattingOptions: (ResolvedDependency) -> GraphFormattingOptions = { + val random = Random(it.hashCode().toLong()) + GraphFormattingOptions(color = Color.fromRgb(random.nextInt(MAX_COLOR_VALUE), random.nextInt(MAX_COLOR_VALUE), random.nextInt(MAX_COLOR_VALUE)) + ) + } + + assertThat(DotGenerator(singleProject, ALL.copy(dependencyFormattingOptions = dependencyFormattingOptions)).generateContent()).isEqualTo(""" + |digraph G { + | single [label="single", shape="box"]; + | { rank = same; "single" }; + | orgjetbrainskotlinkotlinstdlib [label="kotlin-stdlib", shape="box", color="#6ba46e"]; + | single -> orgjetbrainskotlinkotlinstdlib; + | orgjetbrainsannotations [label="annotations", shape="box", color="#4a09b2"]; + | orgjetbrainskotlinkotlinstdlib -> orgjetbrainsannotations; + | ioreactivexrxjava2rxjava [label="rxjava", shape="box", color="#cb660b"]; + | single -> ioreactivexrxjava2rxjava; + | orgreactivestreamsreactivestreams [label="reactive-streams", shape="box", color="#7c70b6"]; + | ioreactivexrxjava2rxjava -> orgreactivestreamsreactivestreams; + |} + |""".trimMargin()) + } + + @Suppress("Detekt.UnnecessaryParentheses") // https://github.com/arturbosch/detekt/issues/767 + @Test fun singleProjectNoChildren() { + assertThat(DotGenerator(singleProject, ALL.copy(children = { false })).generateContent()).isEqualTo(""" + |digraph G { + | single [label="single", shape="box"]; + | { rank = same; "single" }; + | orgjetbrainskotlinkotlinstdlib [label="kotlin-stdlib", shape="box"]; + | single -> orgjetbrainskotlinkotlinstdlib; + | ioreactivexrxjava2rxjava [label="rxjava", shape="box"]; + | single -> ioreactivexrxjava2rxjava; + |} + |""".trimMargin()) + } + + @Suppress("Detekt.UnnecessaryParentheses") // https://github.com/arturbosch/detekt/issues/767 + @Test fun singleProjectFilterRxJavaOut() { + assertThat(DotGenerator(singleProject, ALL.copy(include = { it.moduleGroup != "io.reactivex.rxjava2" })).generateContent()).isEqualTo(""" + |digraph G { + | single [label="single", shape="box"]; + | { rank = same; "single" }; + | orgjetbrainskotlinkotlinstdlib [label="kotlin-stdlib", shape="box"]; + | single -> orgjetbrainskotlinkotlinstdlib; + | orgjetbrainsannotations [label="annotations", shape="box"]; + | orgjetbrainskotlinkotlinstdlib -> orgjetbrainsannotations; + |} + |""".trimMargin()) + } + + @Test fun singleProjectNoDuplicateDependencyConnections() { + // Both RxJava and RxAndroid point transitively on reactivestreams. + singleProject.dependencies.add("implementation", "io.reactivex.rxjava2:rxandroid:2.0.2") + + assertThat(DotGenerator(singleProject, ALL).generateContent()).isEqualTo(""" + |digraph G { + | single [label="single", shape="box"]; + | { rank = same; "single" }; + | orgjetbrainskotlinkotlinstdlib [label="kotlin-stdlib", shape="box"]; + | single -> orgjetbrainskotlinkotlinstdlib; + | orgjetbrainsannotations [label="annotations", shape="box"]; + | orgjetbrainskotlinkotlinstdlib -> orgjetbrainsannotations; + | ioreactivexrxjava2rxjava [label="rxjava", shape="box"]; + | single -> ioreactivexrxjava2rxjava; + | orgreactivestreamsreactivestreams [label="reactive-streams", shape="box"]; + | ioreactivexrxjava2rxjava -> orgreactivestreamsreactivestreams; + | ioreactivexrxjava2rxandroid [label="rxandroid", shape="box"]; + | single -> ioreactivexrxjava2rxandroid; + | ioreactivexrxjava2rxjava [label="rxjava", shape="box"]; + | ioreactivexrxjava2rxandroid -> ioreactivexrxjava2rxjava; + |} + |""".trimMargin()) + } + + @Test fun multiProjectAll() { + assertThat(DotGenerator(multiProject, ALL).generateContent()).isEqualTo(""" + |digraph G { + | multi1 [label="multi1", shape="box"]; + | multi2 [label="multi2", shape="box"]; + | { rank = same; "multi1"; "multi2" }; + | orgjetbrainskotlinkotlinstdlib [label="kotlin-stdlib", shape="box"]; + | multi1 -> orgjetbrainskotlinkotlinstdlib; + | orgjetbrainsannotations [label="annotations", shape="box"]; + | orgjetbrainskotlinkotlinstdlib -> orgjetbrainsannotations; + | ioreactivexrxjava2rxjava [label="rxjava", shape="box"]; + | multi1 -> ioreactivexrxjava2rxjava; + | orgreactivestreamsreactivestreams [label="reactive-streams", shape="box"]; + | ioreactivexrxjava2rxjava -> orgreactivestreamsreactivestreams; + | ioreactivexrxjava2rxjava [label="rxjava", shape="box"]; + | multi2 -> ioreactivexrxjava2rxjava; + | ioreactivexrxjava2rxandroid [label="rxandroid", shape="box"]; + | multi2 -> ioreactivexrxjava2rxandroid; + | ioreactivexrxjava2rxjava [label="rxjava", shape="box"]; + | ioreactivexrxjava2rxandroid -> ioreactivexrxjava2rxjava; + |} + |""".trimMargin()) + } +} diff --git a/src/test/java/com/vanniktech/dependency/graph/generator/ExtensionsTest.kt b/src/test/java/com/vanniktech/dependency/graph/generator/ExtensionsTest.kt new file mode 100644 index 0000000..fe34546 --- /dev/null +++ b/src/test/java/com/vanniktech/dependency/graph/generator/ExtensionsTest.kt @@ -0,0 +1,23 @@ +package com.vanniktech.dependency.graph.generator + +import org.assertj.core.api.Java6Assertions.assertThat +import org.junit.Test + +class ExtensionsTest { + @Test fun dotIdentifier() { + assertThat("java.inject".dotIdentifier).isEqualTo("javainject") + assertThat("firebase-core".dotIdentifier).isEqualTo("firebasecore") + assertThat("firebase core".dotIdentifier).isEqualTo("firebasecore") + } + + @Test fun nonEmptyPrepend() { + assertThat("".nonEmptyPrepend("-")).isEmpty() + assertThat("foo".nonEmptyPrepend("-")).isEqualTo("-foo") + } + + @Test fun toHyphenCase() { + assertThat("".toHyphenCase()).isEqualTo("") + assertThat("Something".toHyphenCase()).isEqualTo("something") + assertThat("SomethingBig".toHyphenCase()).isEqualTo("something-big") + } +} diff --git a/src/test/java/com/vanniktech/dependency/graph/generator/GeneratorTest.kt b/src/test/java/com/vanniktech/dependency/graph/generator/GeneratorTest.kt new file mode 100644 index 0000000..52d1492 --- /dev/null +++ b/src/test/java/com/vanniktech/dependency/graph/generator/GeneratorTest.kt @@ -0,0 +1,25 @@ +package com.vanniktech.dependency.graph.generator + +import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorExtension.Generator +import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorExtension.Generator.Companion.ALL +import org.assertj.core.api.Java6Assertions.assertThat +import org.junit.Test + +class GeneratorTest { + private val generatorFoo = Generator(name = "fooBar", rootSuffix = "bar") + + @Test fun gradleTaskName() { + assertThat(ALL.gradleTaskName).isEqualTo("generateDependencyGraph") + assertThat(generatorFoo.gradleTaskName).isEqualTo("generateDependencyGraphFooBar") + } + + @Test fun outputFileNameDot() { + assertThat(ALL.outputFileNameDot).isEqualTo("dependency-graph.dot") + assertThat(generatorFoo.outputFileNameDot).isEqualTo("dependency-graph-foo-bar.dot") + } + + @Test fun outputFileNamePng() { + assertThat(ALL.outputFileNamePng).isEqualTo("dependency-graph.png") + assertThat(generatorFoo.outputFileNamePng).isEqualTo("dependency-graph-foo-bar.png") + } +} diff --git a/src/test/java/com/vanniktech/dependency/graph/generator/dot/ColorTest.kt b/src/test/java/com/vanniktech/dependency/graph/generator/dot/ColorTest.kt new file mode 100644 index 0000000..69b296e --- /dev/null +++ b/src/test/java/com/vanniktech/dependency/graph/generator/dot/ColorTest.kt @@ -0,0 +1,31 @@ +package com.vanniktech.dependency.graph.generator.dot + +import com.vanniktech.dependency.graph.generator.assertThrows +import org.assertj.core.api.Java6Assertions.assertThat +import org.junit.Test + +class ColorTest { + @Test fun color() { + assertThat(Color.fromHex("#ff0000")).hasToString("#ff0000") + assertThat(Color.fromRgb(0, 128, 255)).hasToString("#0080ff") + assertThat(Color.fromRgb(255, 203, 43)).hasToString("#ffcb2b") + } + + @Test fun invalidHexColor() { + assertThrows { Color.fromHex("#") }.hasMessage("Color is not a valid hex color.") + assertThrows { Color.fromHex("#1") }.hasMessage("Color is not a valid hex color.") + assertThrows { Color.fromHex("#12") }.hasMessage("Color is not a valid hex color.") + assertThrows { Color.fromHex("#1234") }.hasMessage("Color is not a valid hex color.") + assertThrows { Color.fromHex("#12345") }.hasMessage("Color is not a valid hex color.") + assertThrows { Color.fromHex("#1234567") }.hasMessage("Color is not a valid hex color.") + } + + @Test fun invalidRgbColor() { + assertThrows { Color.fromRgb(-1, 0, 0) }.hasMessage("r should be in [0..256)") + assertThrows { Color.fromRgb(256, 0, 0) }.hasMessage("r should be in [0..256)") + assertThrows { Color.fromRgb(0, -1, 0) }.hasMessage("g should be in [0..256)") + assertThrows { Color.fromRgb(0, 256, 0) }.hasMessage("g should be in [0..256)") + assertThrows { Color.fromRgb(0, 0, -1) }.hasMessage("b should be in [0..256)") + assertThrows { Color.fromRgb(0, 0, 256) }.hasMessage("b should be in [0..256)") + } +} diff --git a/src/test/java/com/vanniktech/dependency/graph/generator/dot/GraphFormattingOptionsTest.kt b/src/test/java/com/vanniktech/dependency/graph/generator/dot/GraphFormattingOptionsTest.kt new file mode 100644 index 0000000..6a94648 --- /dev/null +++ b/src/test/java/com/vanniktech/dependency/graph/generator/dot/GraphFormattingOptionsTest.kt @@ -0,0 +1,38 @@ +package com.vanniktech.dependency.graph.generator.dot + +import com.vanniktech.dependency.graph.generator.dot.Style.BOLD +import com.vanniktech.dependency.graph.generator.dot.Style.DASHED +import com.vanniktech.dependency.graph.generator.dot.Style.DIAGONALS +import com.vanniktech.dependency.graph.generator.dot.Style.DOTTED +import com.vanniktech.dependency.graph.generator.dot.Style.FILLED +import com.vanniktech.dependency.graph.generator.dot.Style.INVIS +import com.vanniktech.dependency.graph.generator.dot.Style.ROUNDED +import com.vanniktech.dependency.graph.generator.dot.Style.SOLID +import org.assertj.core.api.Java6Assertions.assertThat +import org.junit.Test + +class GraphFormattingOptionsTest { + @Test fun shape() { + assertThat(GraphFormattingOptions()).hasToString("""[shape="box"]""") + } + + @Test fun style() { + assertThat(GraphFormattingOptions(style = SOLID)).hasToString("""[shape="box", style="solid"]""") + assertThat(GraphFormattingOptions(style = DASHED)).hasToString("""[shape="box", style="dashed"]""") + assertThat(GraphFormattingOptions(style = FILLED)).hasToString("""[shape="box", style="filled"]""") + assertThat(GraphFormattingOptions(style = DIAGONALS)).hasToString("""[shape="box", style="diagonals"]""") + assertThat(GraphFormattingOptions(style = ROUNDED)).hasToString("""[shape="box", style="rounded"]""") + assertThat(GraphFormattingOptions(style = DOTTED)).hasToString("""[shape="box", style="dotted"]""") + assertThat(GraphFormattingOptions(style = BOLD)).hasToString("""[shape="box", style="bold"]""") + assertThat(GraphFormattingOptions(style = INVIS)).hasToString("""[shape="box", style="invis"]""") + } + + @Test fun color() { + assertThat(GraphFormattingOptions(color = Color.fromHex("#ff00AA"))).hasToString("""[shape="box", color="#ff00AA"]""") + assertThat(GraphFormattingOptions(color = Color.fromRgb(255, 128, 0))).hasToString("""[shape="box", color="#ff8000"]""") + } + + @Test fun withLabel() { + assertThat(GraphFormattingOptions().withLabel("label")).hasToString("""[label="label", shape="box"]""") + } +} diff --git a/src/test/java/com/vanniktech/dependency/graph/generator/dot/HeaderTest.kt b/src/test/java/com/vanniktech/dependency/graph/generator/dot/HeaderTest.kt new file mode 100644 index 0000000..c050ec2 --- /dev/null +++ b/src/test/java/com/vanniktech/dependency/graph/generator/dot/HeaderTest.kt @@ -0,0 +1,12 @@ +package com.vanniktech.dependency.graph.generator.dot + +import com.vanniktech.dependency.graph.generator.dot.LabelJust.LEFT +import com.vanniktech.dependency.graph.generator.dot.LabelLoc.BOTTOM +import org.assertj.core.api.Java6Assertions.assertThat +import org.junit.Test + +class HeaderTest { + @Test fun syntax() { + assertThat(Header("1234", 100, 5, BOTTOM, LEFT)).hasToString("label=\"1234\" fontsize=\"100\" height=\"5\" labelloc=\"b\" labeljust=\"l\"") + } +} diff --git a/src/test/java/com/vanniktech/dependency/graph/generator/dot/LabelJustTest.kt b/src/test/java/com/vanniktech/dependency/graph/generator/dot/LabelJustTest.kt new file mode 100644 index 0000000..b268663 --- /dev/null +++ b/src/test/java/com/vanniktech/dependency/graph/generator/dot/LabelJustTest.kt @@ -0,0 +1,19 @@ +package com.vanniktech.dependency.graph.generator.dot + +import com.vanniktech.dependency.graph.generator.dot.LabelJust.CENTER +import com.vanniktech.dependency.graph.generator.dot.LabelJust.LEFT +import com.vanniktech.dependency.graph.generator.dot.LabelJust.RIGHT +import org.assertj.core.api.Java6Assertions.assertThat +import org.junit.Test + +class LabelJustTest { + @Test fun values() { + assertThat(LabelJust.values()).containsExactly(LEFT, CENTER, RIGHT) + } + + @Test fun string() { + assertThat(LEFT).hasToString("l") + assertThat(CENTER).hasToString("c") + assertThat(RIGHT).hasToString("r") + } +} diff --git a/src/test/java/com/vanniktech/dependency/graph/generator/dot/LabelLocTest.kt b/src/test/java/com/vanniktech/dependency/graph/generator/dot/LabelLocTest.kt new file mode 100644 index 0000000..cc2d393 --- /dev/null +++ b/src/test/java/com/vanniktech/dependency/graph/generator/dot/LabelLocTest.kt @@ -0,0 +1,17 @@ +package com.vanniktech.dependency.graph.generator.dot + +import com.vanniktech.dependency.graph.generator.dot.LabelLoc.BOTTOM +import com.vanniktech.dependency.graph.generator.dot.LabelLoc.TOP +import org.assertj.core.api.Java6Assertions.assertThat +import org.junit.Test + +class LabelLocTest { + @Test fun values() { + assertThat(LabelLoc.values()).containsExactly(TOP, BOTTOM) + } + + @Test fun string() { + assertThat(TOP).hasToString("t") + assertThat(BOTTOM).hasToString("b") + } +} diff --git a/src/test/java/com/vanniktech/dependency/graph/generator/dot/ShapeTest.kt b/src/test/java/com/vanniktech/dependency/graph/generator/dot/ShapeTest.kt new file mode 100644 index 0000000..9edf31a --- /dev/null +++ b/src/test/java/com/vanniktech/dependency/graph/generator/dot/ShapeTest.kt @@ -0,0 +1,131 @@ +package com.vanniktech.dependency.graph.generator.dot + +import com.vanniktech.dependency.graph.generator.dot.Shape.BOX +import com.vanniktech.dependency.graph.generator.dot.Shape.POLYGON +import com.vanniktech.dependency.graph.generator.dot.Shape.ELLIPSE +import com.vanniktech.dependency.graph.generator.dot.Shape.OVAL +import com.vanniktech.dependency.graph.generator.dot.Shape.CIRCLE +import com.vanniktech.dependency.graph.generator.dot.Shape.POINT +import com.vanniktech.dependency.graph.generator.dot.Shape.EGG +import com.vanniktech.dependency.graph.generator.dot.Shape.TRIANGLE +import com.vanniktech.dependency.graph.generator.dot.Shape.PLAINTEXT +import com.vanniktech.dependency.graph.generator.dot.Shape.PLAIN +import com.vanniktech.dependency.graph.generator.dot.Shape.DIAMOND +import com.vanniktech.dependency.graph.generator.dot.Shape.TRAPEZIUM +import com.vanniktech.dependency.graph.generator.dot.Shape.PARALLELOGRAM +import com.vanniktech.dependency.graph.generator.dot.Shape.HOUSE +import com.vanniktech.dependency.graph.generator.dot.Shape.PENTAGON +import com.vanniktech.dependency.graph.generator.dot.Shape.HEXAGON +import com.vanniktech.dependency.graph.generator.dot.Shape.SEPTAGON +import com.vanniktech.dependency.graph.generator.dot.Shape.OCTAGON +import com.vanniktech.dependency.graph.generator.dot.Shape.DOUBLECIRCLE +import com.vanniktech.dependency.graph.generator.dot.Shape.DOUBLEOCTAGON +import com.vanniktech.dependency.graph.generator.dot.Shape.TRIPLEOCTAGON +import com.vanniktech.dependency.graph.generator.dot.Shape.INVTRIANGLE +import com.vanniktech.dependency.graph.generator.dot.Shape.INVTRAPEZIUM +import com.vanniktech.dependency.graph.generator.dot.Shape.INVHOUSE +import com.vanniktech.dependency.graph.generator.dot.Shape.MDIAMOND +import com.vanniktech.dependency.graph.generator.dot.Shape.MSQUARE +import com.vanniktech.dependency.graph.generator.dot.Shape.MCIRCLE +import com.vanniktech.dependency.graph.generator.dot.Shape.RECT +import com.vanniktech.dependency.graph.generator.dot.Shape.RECTANGLE +import com.vanniktech.dependency.graph.generator.dot.Shape.SQUARE +import com.vanniktech.dependency.graph.generator.dot.Shape.STAR +import com.vanniktech.dependency.graph.generator.dot.Shape.NONE +import com.vanniktech.dependency.graph.generator.dot.Shape.UNDERLINE +import com.vanniktech.dependency.graph.generator.dot.Shape.CYLINDER +import com.vanniktech.dependency.graph.generator.dot.Shape.NOTE +import com.vanniktech.dependency.graph.generator.dot.Shape.TAB +import com.vanniktech.dependency.graph.generator.dot.Shape.FOLDER +import com.vanniktech.dependency.graph.generator.dot.Shape.BOX3D +import com.vanniktech.dependency.graph.generator.dot.Shape.COMPONENT +import com.vanniktech.dependency.graph.generator.dot.Shape.PROMOTER +import com.vanniktech.dependency.graph.generator.dot.Shape.CDS +import com.vanniktech.dependency.graph.generator.dot.Shape.TERMINATOR +import com.vanniktech.dependency.graph.generator.dot.Shape.UTR +import com.vanniktech.dependency.graph.generator.dot.Shape.PRIMERSITE +import com.vanniktech.dependency.graph.generator.dot.Shape.RESTRICTIONSITE +import com.vanniktech.dependency.graph.generator.dot.Shape.FIVEPOVERHANG +import com.vanniktech.dependency.graph.generator.dot.Shape.THREEPOVERHANG +import com.vanniktech.dependency.graph.generator.dot.Shape.NOVERHANG +import com.vanniktech.dependency.graph.generator.dot.Shape.ASSEMBLY +import com.vanniktech.dependency.graph.generator.dot.Shape.SIGNATURE +import com.vanniktech.dependency.graph.generator.dot.Shape.INSULATOR +import com.vanniktech.dependency.graph.generator.dot.Shape.RIBOSITE +import com.vanniktech.dependency.graph.generator.dot.Shape.RNASTAB +import com.vanniktech.dependency.graph.generator.dot.Shape.PROTEASESITE +import com.vanniktech.dependency.graph.generator.dot.Shape.PROTEINSTAB +import com.vanniktech.dependency.graph.generator.dot.Shape.RPROMOTER +import com.vanniktech.dependency.graph.generator.dot.Shape.RARROW +import com.vanniktech.dependency.graph.generator.dot.Shape.LARROW +import com.vanniktech.dependency.graph.generator.dot.Shape.LPROMOTER +import org.assertj.core.api.Java6Assertions.assertThat +import org.junit.Test + +class ShapeTest { + @Test fun values() { + assertThat(Shape.values()).containsExactly(BOX, POLYGON, ELLIPSE, OVAL, CIRCLE, POINT, EGG, TRIANGLE, PLAINTEXT, PLAIN, DIAMOND, TRAPEZIUM, PARALLELOGRAM, HOUSE, PENTAGON, HEXAGON, SEPTAGON, OCTAGON, DOUBLECIRCLE, DOUBLEOCTAGON, TRIPLEOCTAGON, INVTRIANGLE, INVTRAPEZIUM, INVHOUSE, MDIAMOND, MSQUARE, MCIRCLE, RECT, RECTANGLE, SQUARE, STAR, NONE, UNDERLINE, CYLINDER, NOTE, TAB, FOLDER, BOX3D, COMPONENT, PROMOTER, CDS, TERMINATOR, UTR, PRIMERSITE, RESTRICTIONSITE, FIVEPOVERHANG, THREEPOVERHANG, NOVERHANG, ASSEMBLY, SIGNATURE, INSULATOR, RIBOSITE, RNASTAB, PROTEASESITE, PROTEINSTAB, RPROMOTER, RARROW, LARROW, LPROMOTER) + } + + @Test @Suppress("Detekt.LongMethod") fun string() { + assertThat(BOX).hasToString("box") + assertThat(POLYGON).hasToString("polygon") + assertThat(ELLIPSE).hasToString("ellipse") + assertThat(OVAL).hasToString("oval") + assertThat(CIRCLE).hasToString("circle") + assertThat(POINT).hasToString("point") + assertThat(EGG).hasToString("egg") + assertThat(TRIANGLE).hasToString("triangle") + assertThat(PLAINTEXT).hasToString("plaintext") + assertThat(PLAIN).hasToString("plain") + assertThat(DIAMOND).hasToString("diamond") + assertThat(TRAPEZIUM).hasToString("trapezium") + assertThat(PARALLELOGRAM).hasToString("parallelogram") + assertThat(HOUSE).hasToString("house") + assertThat(PENTAGON).hasToString("pentagon") + assertThat(HEXAGON).hasToString("hexagon") + assertThat(SEPTAGON).hasToString("septagon") + assertThat(OCTAGON).hasToString("octagon") + assertThat(DOUBLECIRCLE).hasToString("doublecircle") + assertThat(DOUBLEOCTAGON).hasToString("doubleoctagon") + assertThat(TRIPLEOCTAGON).hasToString("tripleoctagon") + assertThat(INVTRIANGLE).hasToString("invtriangle") + assertThat(INVTRAPEZIUM).hasToString("invtrapezium") + assertThat(INVHOUSE).hasToString("invhouse") + assertThat(MDIAMOND).hasToString("Mdiamond") + assertThat(MSQUARE).hasToString("Msquare") + assertThat(MCIRCLE).hasToString("Mcircle") + assertThat(RECT).hasToString("rect") + assertThat(RECTANGLE).hasToString("rectangle") + assertThat(SQUARE).hasToString("square") + assertThat(STAR).hasToString("star") + assertThat(NONE).hasToString("none") + assertThat(UNDERLINE).hasToString("underline") + assertThat(CYLINDER).hasToString("cylinder") + assertThat(NOTE).hasToString("note") + assertThat(TAB).hasToString("tab") + assertThat(FOLDER).hasToString("folder") + assertThat(BOX3D).hasToString("box3d") + assertThat(COMPONENT).hasToString("component") + assertThat(PROMOTER).hasToString("promoter") + assertThat(CDS).hasToString("cds") + assertThat(TERMINATOR).hasToString("terminator") + assertThat(UTR).hasToString("utr") + assertThat(PRIMERSITE).hasToString("primersite") + assertThat(RESTRICTIONSITE).hasToString("restrictionsite") + assertThat(FIVEPOVERHANG).hasToString("fivepoverhang") + assertThat(THREEPOVERHANG).hasToString("threepoverhang") + assertThat(NOVERHANG).hasToString("noverhang") + assertThat(ASSEMBLY).hasToString("assembly") + assertThat(SIGNATURE).hasToString("signature") + assertThat(INSULATOR).hasToString("insulator") + assertThat(RIBOSITE).hasToString("ribosite") + assertThat(RNASTAB).hasToString("rnastab") + assertThat(PROTEASESITE).hasToString("proteasesite") + assertThat(PROTEINSTAB).hasToString("proteinstab") + assertThat(RPROMOTER).hasToString("rpromoter") + assertThat(RARROW).hasToString("rarrow") + assertThat(LARROW).hasToString("larrow") + assertThat(LPROMOTER).hasToString("lpromoter") + } +} diff --git a/src/test/java/com/vanniktech/dependency/graph/generator/dot/StyleTest.kt b/src/test/java/com/vanniktech/dependency/graph/generator/dot/StyleTest.kt new file mode 100644 index 0000000..d1bce0d --- /dev/null +++ b/src/test/java/com/vanniktech/dependency/graph/generator/dot/StyleTest.kt @@ -0,0 +1,29 @@ +package com.vanniktech.dependency.graph.generator.dot + +import com.vanniktech.dependency.graph.generator.dot.Style.BOLD +import com.vanniktech.dependency.graph.generator.dot.Style.DASHED +import com.vanniktech.dependency.graph.generator.dot.Style.DIAGONALS +import com.vanniktech.dependency.graph.generator.dot.Style.DOTTED +import com.vanniktech.dependency.graph.generator.dot.Style.FILLED +import com.vanniktech.dependency.graph.generator.dot.Style.INVIS +import com.vanniktech.dependency.graph.generator.dot.Style.ROUNDED +import com.vanniktech.dependency.graph.generator.dot.Style.SOLID +import org.assertj.core.api.Java6Assertions.assertThat +import org.junit.Test + +class StyleTest { + @Test fun values() { + assertThat(Style.values()).containsExactly(SOLID, DASHED, FILLED, DIAGONALS, ROUNDED, DOTTED, BOLD, INVIS) + } + + @Test fun string() { + assertThat(SOLID).hasToString("solid") + assertThat(DASHED).hasToString("dashed") + assertThat(FILLED).hasToString("filled") + assertThat(DIAGONALS).hasToString("diagonals") + assertThat(ROUNDED).hasToString("rounded") + assertThat(DOTTED).hasToString("dotted") + assertThat(BOLD).hasToString("bold") + assertThat(INVIS).hasToString("invis") + } +} diff --git a/src/test/java/com/vanniktech/dependency/graph/generator/testextensions.kt b/src/test/java/com/vanniktech/dependency/graph/generator/testextensions.kt new file mode 100644 index 0000000..9b6ff81 --- /dev/null +++ b/src/test/java/com/vanniktech/dependency/graph/generator/testextensions.kt @@ -0,0 +1,19 @@ +package com.vanniktech.dependency.graph.generator + +import org.assertj.core.api.AbstractThrowableAssert +import org.assertj.core.api.Java6Assertions.assertThat + +@Suppress("Detekt.TooGenericExceptionCaught", "Detekt.InstanceOfCheckForException", "Detekt.RethrowCaughtException") // https://github.com/arturbosch/detekt/issues/766 +inline fun assertThrows(block: () -> Unit): AbstractThrowableAssert<*, out Throwable> { + try { + block() + } catch (e: Throwable) { + if (e is T) { + return assertThat(e) + } else { + throw e + } + } + + throw AssertionError("Expected ${T::class.simpleName}") +}