diff --git a/melting-pot.sh b/melting-pot.sh
index f7b904e..5998306 100755
--- a/melting-pot.sh
+++ b/melting-pot.sh
@@ -1,35 +1,560 @@
#!/bin/sh
-# melting-pot.sh - A script to build the entire SciJava software stack
-# from the latest code on the respective master branches.
-
-rm -rf melting-pot
-mkdir melting-pot
-cd melting-pot
-
-echo '' > pom.xml
-echo '' >> pom.xml
-echo ' 4.0.0' >> pom.xml
-echo >> pom.xml
-echo ' org.scijava' >> pom.xml
-echo ' melting-pot' >> pom.xml
-echo ' 0.0.0-SNAPSHOT' >> pom.xml
-echo ' pom' >> pom.xml
-echo >> pom.xml
-echo ' SciJava Uber Build' >> pom.xml
-echo >> pom.xml
-echo ' ' >> pom.xml
-
-for repo in $(sj-hierarchy.pl -g)
-do
- git clone $repo --depth 1
- module=${repo##*/}
- module=${module%.git}
- echo " $module" >> pom.xml
-done
-
-echo ' ' >> pom.xml
-echo
-echo '' >> pom.xml
-
-mvn -Pdev.scijava,dev.imglib2,dev.scifio,dev.imagej validate
+# ============================================================================
+# melting-pot.sh
+# ============================================================================
+# Tests all components of a project affected by changes in its dependencies.
+#
+# First, an anecdote illustrating the problem this script solves:
+#
+# Suppose you have a large application, org:app:1.0.0, with many dependencies:
+# org:foo:1.2.3, org:bar:3.4.5, and many others.
+#
+# Now suppose you make some changes to foo, and want to know whether deploying
+# them (i.e., releasing a new foo and updating app to depend on that release)
+# will break the app. So you manually update your local copy of app to depend
+# on org:foo:1.3.0-SNAPSHOT, and run the build (including tests, of course).
+#
+# The build passes, but this alone is insufficient: org:bar:3.4.5 also depends
+# on org:foo:1.2.3, so you manually update bar to use org:foo:1.3.0-SNAPSHOT,
+# then build bar to verify that it also is not broken by the update.
+#
+# This process quickly becomes very tedious when there are dozens of
+# components of app which all depend on foo.
+#
+# And more importantly, testing each component individually in this manner is
+# still insufficient to determine whether all of them will truly work together
+# at runtime, where only a single version of each component is deployed.
+#
+# For example: suppose org:bar:3.4.5 depends on org:lib:8.0.0, while
+# org:foo:1.2.3 depends on org:lib:7.0.0. The relevant facts are:
+#
+# * Your new foo (org:foo:1.3.0-SNAPSHOT) builds against lib 7, and portions
+# of it rely on lib-7-specific API.
+#
+# * The bar component pinned to foo 1.3.0-SNAPSHOT builds against lib 8; it
+# compiles with passing tests because bar only invokes portions of the foo
+# API which do not require lib-7-specific API.
+#
+# In this scenario, it is lib 8 that is actually deployed at runtime with the
+# app, so parts of foo will be broken, even though both foo and bar build with
+# passing tests individually.
+#
+# This "melting pot" build seeks to overcome many of these issues by unifying
+# all components of the app into a single multi-module build, with all
+# versions uniformly pinned to the ones that will actually be deployed at
+# runtime.
+#
+# This goal is achieved by synthesizing a multi-module build including all
+# affected components (or optionally, all components period) of the specific
+# project, and then executing a Maven build with uniformly overridden versions
+# of all components to the ones resolved for the project itself.
+#
+# IMPORTANT IMPLEMENTATION DETAIL! The override works by setting a version
+# property for each component of the form "artifactId.version"; it is assumed
+# that all components declare their dependencies using version properties of
+# this form. E.g.:
+#
+#
+# com.google.guava
+# guava
+# ${guava.version}
+#
+#
+# Using dependencyManagement is fine too, as long as it then uses this pattern
+# to declare the versions as properties, which can be overridden.
+#
+# Any dependency which does not declare a version property matching this
+# assumption will not be properly overridden in the melting pot!
+#
+# Author: Curtis Rueden
+# ============================================================================
+
+# -- Functions --
+
+stderr() {
+ >&2 echo "$@"
+}
+
+debug() {
+ test "$debug" &&
+ stderr "+ $@"
+}
+
+info() {
+ test "$verbose" &&
+ stderr "[INFO] $@"
+}
+
+error() {
+ stderr "[ERROR] $@"
+}
+
+unknownArg() {
+ error "Unknown option: $@"
+ usage=1
+}
+
+checkPrereqs() {
+ while [ $# -gt 0 ]
+ do
+ which $1 > /dev/null 2> /dev/null
+ test $? -ne 0 && echo "Missing prerequisite: $1" && exit 255
+ shift
+ done
+}
+
+verifyPrereqs() {
+ checkPrereqs git mvn xmllint
+}
+
+parseArguments() {
+ while [ $# -gt 0 ]
+ do
+ case "$1" in
+ -b|--branch)
+ branch="$2"
+ shift
+ ;;
+ -c|--changes)
+ changes="$2"
+ shift
+ ;;
+ -i|--includes)
+ includes="$2"
+ shift
+ ;;
+ -e|--excludes)
+ excludes="$2"
+ shift
+ ;;
+ -r|--remoteRepos)
+ remoteRepos="$2"
+ shift
+ ;;
+ -l|--localRepo)
+ repoBase="$2"
+ shift
+ ;;
+ -o|--outputDir)
+ outputDir="$2"
+ shift
+ ;;
+ -p|--prune)
+ prune=1
+ ;;
+ -v|--verbose)
+ verbose=1
+ ;;
+ -d|--debug)
+ debug=1
+ ;;
+ -f|--force)
+ force=1
+ ;;
+ -s|--skipBuild)
+ skipBuild=1
+ ;;
+ -h|--help)
+ usage=1
+ ;;
+ -*)
+ unknownArg "$1"
+ ;;
+ *)
+ test -z "$project" && project="$1" ||
+ unknownArg "$1"
+ ;;
+ esac
+ shift
+ done
+
+ test -z "$project" -a -z "$usage" &&
+ error "No project specified!" && usage=1
+
+ if [ "$usage" ]
+ then
+ echo "Usage: $(basename "$0") [-b ] [-c ] \\
+ [-i ] [-e ] [-r ] [-l ] [-o ] [-pvfsh]
+
+
+ The project to build, including dependencies, with consistent versions.
+-b, --branch
+ Override the branch/tag of the project to build. By default,
+ the branch used will be the tag named \"artifactId-version\".
+-c, --changes
+ Comma-separated list of GAVs to inject into the project, replacing
+ normal versions. E.g.: \"com.mycompany:myartifact:1.2.3-SNAPSHOT\"
+-i, --includes
+ Comma-separated list of GAs (no version; wildcards OK for G or A) to
+ include in the build. All by default. E.g.: \"mystuff:*,myotherstuff:*\"
+-e, --excludes
+ Comma-separated list of GAs (no version; wildcards OK for G or A) to
+ exclude from the build. E.g.: \"mystuff:extraneous,mystuff:irrelevant\"
+-r, --remoteRepos
+ Comma-separated list of additional remote Maven repositories to check
+ for artifacts, in the format id::[layout]::url or just url.
+-l, --localRepos
+ Overrides the directory of the Maven local repository cache.
+-o, --outputDir
+ Overrides the output directory. The default is \"melting-pot\".
+-p, --prune
+ Build only the components which themselves depend on a changed
+ artifact. This will make the build much faster, at the expense of
+ not fully testing runtime compatibility across all components.
+-v, --verbose
+ Enable verbose/debugging output.
+-f, --force
+ Wipe out the output directory if it already exists.
+-s, --skipBuild
+ Skips the final build step. Useful for automated testing.
+-h, --help
+ Display this usage information."
+ exit 1
+ fi
+
+ # Assign default parameter values.
+ test "$outputDir" || outputDir="melting-pot"
+ test "$repoBase" || repoBase="$HOME/.m2/repository"
+}
+
+createDir() {
+ test -z "$force" -a -e "$1" &&
+ error "Directory already exists: $1" && exit 2
+
+ rm -rf "$1"
+ mkdir -p "$1"
+ cd "$1"
+}
+
+groupId() {
+ echo "${1%%:*}"
+}
+
+artifactId() {
+ local result="${1#*:}" # strip groupId
+ echo "${result%%:*}"
+}
+
+version() {
+ local result="${1#*:}" # strip groupId
+ case "$result" in
+ *:*)
+ result="${result#*:}" # strip artifactId
+ case "$result" in
+ *:*:*:*)
+ # G:A:P:C:V:S
+ result="${result#*:}" # strip packaging
+ result="${result#*:}" # strip classifier
+ ;;
+ *:*:*)
+ # G:A:P:V:S
+ result="${result#*:}" # strip packaging
+ ;;
+ *)
+ # G:A:V or G:A:V:?
+ ;;
+ esac
+ echo "${result%%:*}"
+ ;;
+ esac
+}
+
+# Converts the given GAV into a path in the local repository cache.
+repoPath() {
+ local gPath="$(echo "$(groupId "$1")" | tr :. /)"
+ local aPath="$(artifactId "$1")"
+ local vPath="$(version "$1")"
+ echo "$repoBase/$gPath/$aPath/$vPath"
+}
+
+# Gets the path to the given GAV's POM file in the local repository cache.
+pomPath() {
+ local pomFile="$(artifactId "$1")-$(version "$1").pom"
+ echo "$(repoPath "$1")/$pomFile"
+}
+
+# Fetches the POM for the given GAV into the local repository cache.
+downloadPOM() {
+ local g="$(groupId "$1")"
+ local a="$(artifactId "$1")"
+ local v="$(version "$1")"
+ debug "mvn dependency:get \\
+ -DrepoUrl=\"$remoteRepos\" \\
+ -DgroupId=\"$g\" \\
+ -DartifactId=\"$a\" \\
+ -Dversion=\"$v\" \\
+ -Dpackaging=pom"
+ mvn dependency:get \
+ -DrepoUrl="$remoteRepos" \
+ -DgroupId="$g" \
+ -DartifactId="$a" \
+ -Dversion="$v" \
+ -Dpackaging=pom > /dev/null
+}
+
+# Gets the POM path for the given GAV, ensuring it exists locally.
+pom() {
+ local pomPath="$(pomPath "$1")"
+ test -f "$pomPath" || downloadPOM "$1"
+ echo "$pomPath"
+}
+
+# For the given XML file on disk ($1), gets the value of the
+# specified XPath expression of the form "//$2/$3/$4/...".
+xpath() {
+ local xmlFile="$1"
+ shift
+ local xpath="/"
+ while [ $# -gt 0 ]
+ do
+ # NB: Ignore namespace issues; see: http://stackoverflow.com/a/8266075
+ xpath="$xpath/*[local-name()='$1']"
+ shift
+ done
+ debug "xmllint --xpath \"$xpath\" \"$xmlFile\""
+ xmllint --xpath "$xpath" "$xmlFile" 2> /dev/null |
+ sed -E 's/^[^>]*>(.*)<[^<]*$/\1/'
+}
+
+# For the given GAV ($1), recursively gets the value of the
+# specified XPath expression of the form "//$2/$3/$4/...".
+pomValue() {
+ local pomPath="$(pom "$1")"
+ shift
+ local value="$(xpath "$pomPath" $@)"
+ if [ "$value" ]
+ then
+ echo "$value"
+ else
+ # Path not found in POM; look in the parent POM.
+ local pg="$(xpath "$pomPath" project parent groupId)"
+ if [ "$pg" ]
+ then
+ # There is a parent POM declaration in this POM.
+ local pa="$(xpath "$pomPath" project parent artifactId)"
+ local pv="$(xpath "$pomPath" project parent version)"
+ pomValue "$pg:$pa:$pv" $@
+ fi
+ fi
+}
+
+# Gets the SCM URL for the given GAV.
+scmURL() {
+ pomValue "$1" project scm connection | sed -E 's/^scm:git://'
+}
+
+# Gets the SCM tag for the given GAV.
+scmTag() {
+ echo "$(artifactId "$1")-$(version "$1")"
+}
+
+# Fetches the source code for the given GAV. Returns the directory.
+retrieveSource() {
+ local scmURL="$(scmURL "$1")"
+ local scmBranch
+ test "$2" && scmBranch="$2" || scmBranch="$(scmTag "$1")"
+ local dir="$(groupId "$1")/$(artifactId "$1")"
+ debug "git clone \"$scmURL\" --branch \"$scmBranch\" --depth 1 \"$dir\""
+ git clone "$scmURL" --branch "$scmBranch" --depth 1 "$dir" 2> /dev/null
+ echo "$dir"
+}
+
+# Gets the list of dependencies for the project in the CWD.
+deps() {
+ cd "$1"
+ debug "mvn dependency:list"
+ mvn dependency:list |
+ grep '^\[INFO\] [^ ]' | sed 's/\[INFO\] //' | sort
+ cd - > /dev/null
+}
+
+# Checks whether the given GA(V) matches the specified filter pattern.
+gaMatch() {
+ local ga="$1"
+ local filter="$2"
+ local g="$(groupId "$ga")"
+ local a="$(artifactId "$ga")"
+ local fg="$(groupId "$filter")"
+ local fa="$(artifactId "$filter")"
+ test "$fg" = "$g" -o "$fg" = "*" || return
+ test "$fa" = "$a" -o "$fa" = "*" || return
+ echo 1
+}
+
+# Determines whether the given GA(V) version is being overridden.
+isChanged() {
+ local IFS=","
+
+ local change
+ for change in $changes
+ do
+ test "$(gaMatch "$1" "$change")" && echo 1 && return
+ done
+}
+
+# Determines whether the given GA(V) meets the inclusion criteria.
+isIncluded() {
+ # do not include the changed artifacts we are testing against
+ test "$(isChanged "$1")" && return
+
+ local IFS=","
+
+ # ensure GA is not excluded
+ local exclude
+ for exclude in $excludes
+ do
+ test "$(gaMatch "$1" "$exclude")" && return
+ done
+
+ # ensure GA is included
+ test -z "$includes" && echo 1 && return
+ local include
+ for include in $includes
+ do
+ test "$(gaMatch "$1" "$include")" && echo 1 && return
+ done
+}
+
+# Deletes components which do not depend on a changed GAV.
+pruneReactor() {
+ local dir
+ for dir in */*
+ do
+ info "Checking relevance of component $dir"
+ local deps="$(deps "$dir")"
+
+ # Determine whether the component depends on a changed GAV.
+ local keep
+ unset keep
+ local dep
+ for dep in $deps
+ do
+ test "$(isChanged "$dep")" && keep=1 && break
+ done
+
+ # If the component is irrelevant, prune it.
+ if [ -z "$keep" ]
+ then
+ info "Pruning irrelevant component: $dir"
+ rm -rf "$dir"
+ fi
+ done
+}
+
+# Tests if the given directory contains the appropriate source code.
+isProject() {
+ local a="$(xpath "$1/pom.xml" project artifactId)"
+ test "$a" = "$(basename "$1")" && echo 1
+}
+
+# Generates an aggregator POM for all modules in the current directory.
+generatePOM() {
+ echo '' > pom.xml
+ echo '> pom.xml
+ echo ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' >> pom.xml
+ echo ' xsi:schemaLocation="http://maven.apache.org/POM/4.0.0' >> pom.xml
+ echo ' http://maven.apache.org/xsd/maven-4.0.0.xsd">' >> pom.xml
+ echo ' 4.0.0' >> pom.xml
+ echo >> pom.xml
+ echo ' melting-pot' >> pom.xml
+ echo ' melting-pot' >> pom.xml
+ echo ' 0.0.0-SNAPSHOT' >> pom.xml
+ echo ' pom' >> pom.xml
+ echo >> pom.xml
+ echo ' Melting Pot' >> pom.xml
+ echo >> pom.xml
+ echo ' ' >> pom.xml
+ local dir
+ for dir in */*
+ do
+ if [ "$(isProject "$dir")" ]
+ then
+ echo " $dir" >> pom.xml
+ else
+ # Check for a child component of a multi-module project.
+ local childDir="$dir/$(basename "$dir")"
+ test "$(isProject "$childDir")" &&
+ echo " $childDir" >> pom.xml
+ fi
+ done
+ echo ' ' >> pom.xml
+ echo '' >> pom.xml
+}
+
+# Creates and tests an appropriate multi-module reactor for the given project.
+# All relevant dependencies which match the inclusion criteria are linked into
+# the multi-module build, with each changed GAV overridding the originally
+# specified version for the corresponding GA.
+meltDown() {
+ # Fetch the project source code.
+ info "$1: fetching project source"
+ local dir="$(retrieveSource "$1" "$branch")"
+ test ! -d "$dir" && error "Could not fetch project source" && exit 3
+
+ # Get the project dependencies.
+ info "$1: determining project dependencies"
+ local deps="$(deps "$dir")"
+
+ local args="-Denforcer.skip"
+
+ # Process the dependencies.
+ info "$1: processing project dependencies"
+ local dep
+ for dep in $deps
+ do
+ local g="$(groupId "$dep")"
+ local a="$(artifactId "$dep")"
+ local v="$(version "$dep")"
+ local gav="$g:$a:$v"
+
+ test -z "$(isChanged "$gav")" &&
+ args="$args -D$a.version=$v"
+
+ if [ "$(isIncluded "$gav")" ]
+ then
+ info "$1: $a: fetching component source"
+ dir="$(retrieveSource "$gav")"
+ fi
+ done
+
+ # Override versions of changed GAVs.
+ info "$1: processing changed components"
+ local TLS=,
+ local gav
+ for gav in $changes
+ do
+ local a="$(artifactId "$gav")"
+ local v="$(version "$gav")"
+ args="$args -D$a.version=$v"
+ done
+ unset TLS
+
+ # Prune the build, if applicable.
+ test "$prune" && pruneReactor
+
+ # Generate the aggregator POM.
+ info "Generating aggregator POM"
+ generatePOM
+
+ # Build everything.
+ if [ "$skipBuild" ]
+ then
+ info "Skipping the build; the command would have been:"
+ info "mvn $args test"
+ else
+ info "Building the project!"
+ # NB: All code is fresh; no need to clean.
+ debug "mvn $args test"
+ mvn $args test
+ fi
+
+ info "$1: complete"
+}
+
+# -- Main --
+
+verifyPrereqs
+parseArguments $@
+createDir "$outputDir"
+meltDown "$project"
diff --git a/tests/melting-pot-multi.t b/tests/melting-pot-multi.t
new file mode 100644
index 0000000..0c87511
--- /dev/null
+++ b/tests/melting-pot-multi.t
@@ -0,0 +1,29 @@
+Test that recursive SCM retrieval and multi-module projects work:
+
+ $ sh "$TESTDIR/../melting-pot.sh" sc.fiji:TrakEM2_:1.0f -r http://maven.imagej.net/content/groups/public -i 'sc.fiji:TrakEM2_' -v -s -d -f
+ [INFO] sc.fiji:TrakEM2_:1.0f: fetching project source
+ \+ xmllint --xpath ".*'project'.*'scm'.*'connection'.*" ".*/sc/fiji/TrakEM2_/1.0f/TrakEM2_-1.0f.pom" (re)
+ \+ xmllint --xpath ".*'project'.*'parent'.*'groupId'.*" ".*/sc/fiji/TrakEM2_/1.0f/TrakEM2_-1.0f.pom" (re)
+ \+ xmllint --xpath ".*'project'.*'parent'.*'artifactId'.*" ".*/sc/fiji/TrakEM2_/1.0f/TrakEM2_-1.0f.pom" (re)
+ \+ xmllint --xpath ".*'project'.*'parent'.*'version'.*" ".*/sc/fiji/TrakEM2_/1.0f/TrakEM2_-1.0f.pom" (re)
+ \+ xmllint --xpath ".*'project'.*'scm'.*'connection'.*" ".*/sc/fiji/pom-trakem2/1.3.2/pom-trakem2-1.3.2.pom" (re)
+ + git clone "git://github.com/trakem2/TrakEM2" --branch "TrakEM2_-1.0f" --depth 1 "sc.fiji/TrakEM2_"
+ [INFO] sc.fiji:TrakEM2_:1.0f: determining project dependencies
+ + mvn dependency:list
+ [INFO] sc.fiji:TrakEM2_:1.0f: processing project dependencies
+ [INFO] sc.fiji:TrakEM2_:1.0f: processing changed components
+ [INFO] Generating aggregator POM
+ + xmllint --xpath "//*[local-name()='project']/*[local-name()='artifactId']" "sc.fiji/TrakEM2_/pom.xml"
+ + xmllint --xpath "//*[local-name()='project']/*[local-name()='artifactId']" "sc.fiji/TrakEM2_/TrakEM2_/pom.xml"
+ [INFO] Skipping the build; the command would have been:
+ [INFO] mvn -Denforcer.skip -Dbatik.version=1.8 -Dlogback-classic.version=1.1.1 -Dlogback-core.version=1.1.1 -Dkryo.version=2.21 -Dgentyref.version=1.1.0 -Djgoodies-common.version=1.7.0 -Djgoodies-forms.version=1.7.2 -Djai-codec.version=1.1.3 -Dtools.version=1.4.2 -Dtools.version=1.4.2 -Dmines-jtk.version=20100113 -Dudunits.version=4.3.18 -Djama.version=1.0.3 -Djama.version=1.0.3 -Dj3d-core-utils.version=1.5.2 -Dj3d-core.version=1.5.2 -Dvecmath.version=1.5.2 -Djai-core.version=1.1.3 -Djoda-time.version=2.3 -Djunit.version=4.11 -Dmpicbg.version=1.0.1 -Dmpicbg.version=1.0.1 -Dmpicbg_.version=1.0.1 -Dij1-patcher.version=0.12.0 -Dij.version=1.49p -Dij.version=1.49p -Dimagej-common.version=0.12.2 -Dimglib2-ij.version=2.0.0-beta-30 -Dimglib2-roi.version=0.3.0 -Dimglib2.version=2.2.1 -Dtrove4j.version=3.0.3 -Dnone.version= -Dformats-api.version=5.0.7 -Dformats-bsd.version=5.0.7 -Dformats-common.version=5.0.7 -Djai_imageio.version=5.0.7 -Dome-xml.version=5.0.7 -Dspecification.version=5.0.7 -Dturbojpeg.version=5.0.7 -Dcommons-math3.version=3.4.1 -Deventbus.version=1.4 -Dhamcrest-core.version=1.3 -Djavassist.version=3.16.1-GA -Djcommon.version=1.0.23 -Djfreechart.version=1.0.19 -Dperf4j.version=0.9.13 -Djython-shaded.version=2.5.3 -Dnative-lib-loader.version=2.0.2 -Dscijava-common.version=2.39.0 -Dslf4j-api.version=1.7.6 -Dpostgresql.version=8.2-507.jdbc3 -D3D_Viewer.version=3.0.1 -DAnalyzeSkeleton_.version=2.0.4 -DFiji_Plugins.version=3.0.0 -DLasso_and_Blow_Tool.version=2.0.1 -DSimple_Neurite_Tracer.version=2.0.3 -DSkeletonize3D_.version=1.0.1 -DVIB-lib.version=2.0.1 -DVIB_.version=2.0.2 -DVectorString.version=1.0.2 -DbUnwarpJ_.version=2.6.2 -Dfiji-lib.version=2.1.0 -Dlegacy-imglib1.version=1.1.2-DEPRECATED -Dlevel_sets.version=1.0.1 -Dmpicbg-trakem2.version=1.2.2 -Dpal-optimization.version=2.0.0 test
+ [INFO] sc.fiji:TrakEM2_:1.0f: complete
+
+ $ find melting-pot -maxdepth 2 | sort
+ melting-pot
+ melting-pot/pom.xml
+ melting-pot/sc.fiji
+ melting-pot/sc.fiji/TrakEM2_
+
+ $ grep 'sc.fiji/TrakEM2_/TrakEM2_' melting-pot/pom.xml
+ \t\tsc.fiji/TrakEM2_/TrakEM2_ (esc)
diff --git a/tests/melting-pot-out-dir.t b/tests/melting-pot-out-dir.t
new file mode 100644
index 0000000..2a236ae
--- /dev/null
+++ b/tests/melting-pot-out-dir.t
@@ -0,0 +1,5 @@
+Script should fail if output directory already exists:
+
+ $ mkdir melting-pot && sh "$TESTDIR/../melting-pot.sh" foo:bar
+ [ERROR] Directory already exists: melting-pot
+ [2]
diff --git a/tests/melting-pot-prune.t b/tests/melting-pot-prune.t
new file mode 100644
index 0000000..e1c6d6b
--- /dev/null
+++ b/tests/melting-pot-prune.t
@@ -0,0 +1,25 @@
+Test that the '--prune' flag works as intended:
+
+ $ sh "$TESTDIR/../melting-pot.sh" net.imagej:imagej-common:0.15.1 -r http://maven.imagej.net/content/groups/public -c org.scijava:scijava-common:2.44.2 -i 'org.scijava:*,net.imagej:*,net.imglib2:*' -p -v -f -s
+ [INFO] net.imagej:imagej-common:0.15.1: fetching project source
+ [INFO] net.imagej:imagej-common:0.15.1: determining project dependencies
+ [INFO] net.imagej:imagej-common:0.15.1: processing project dependencies
+ [INFO] net.imagej:imagej-common:0.15.1: imglib2-roi: fetching component source
+ [INFO] net.imagej:imagej-common:0.15.1: imglib2: fetching component source
+ [INFO] net.imagej:imagej-common:0.15.1: processing changed components
+ [INFO] Checking relevance of component net.imagej/imagej-common
+ [INFO] Checking relevance of component net.imglib2/imglib2
+ [INFO] Pruning irrelevant component: net.imglib2/imglib2
+ [INFO] Checking relevance of component net.imglib2/imglib2-roi
+ [INFO] Pruning irrelevant component: net.imglib2/imglib2-roi
+ [INFO] Generating aggregator POM
+ [INFO] Skipping the build; the command would have been:
+ [INFO] mvn -Denforcer.skip -Dgentyref.version=1.1.0 -Dudunits.version=4.3.18 -Djunit.version=4.11 -Dimglib2-roi.version=0.3.0 -Dimglib2.version=2.2.1 -Dtrove4j.version=3.0.3 -Deventbus.version=1.4 -Dhamcrest-core.version=1.3 -Dscijava-common.version=2.44.2 test
+ [INFO] net.imagej:imagej-common:0.15.1: complete
+
+ $ find melting-pot -maxdepth 2 | sort
+ melting-pot
+ melting-pot/net.imagej
+ melting-pot/net.imagej/imagej-common
+ melting-pot/net.imglib2
+ melting-pot/pom.xml
diff --git a/tests/melting-pot-simple.t b/tests/melting-pot-simple.t
new file mode 100644
index 0000000..6e817b6
--- /dev/null
+++ b/tests/melting-pot-simple.t
@@ -0,0 +1,20 @@
+Down-the-middle test of a relatively simply project:
+
+ $ sh "$TESTDIR/../melting-pot.sh" net.imagej:imagej-common:0.15.1 -r http://maven.imagej.net/content/groups/public -c org.scijava:scijava-common:2.44.2 -i 'org.scijava:*,net.imagej:*,net.imglib2:*,io.scif:*' -e net.imglib2:imglib2-roi -v -f -s
+ [INFO] net.imagej:imagej-common:0.15.1: fetching project source
+ [INFO] net.imagej:imagej-common:0.15.1: determining project dependencies
+ [INFO] net.imagej:imagej-common:0.15.1: processing project dependencies
+ [INFO] net.imagej:imagej-common:0.15.1: imglib2: fetching component source
+ [INFO] net.imagej:imagej-common:0.15.1: processing changed components
+ [INFO] Generating aggregator POM
+ [INFO] Skipping the build; the command would have been:
+ [INFO] mvn -Denforcer.skip -Dgentyref.version=1.1.0 -Dudunits.version=4.3.18 -Djunit.version=4.11 -Dimglib2-roi.version=0.3.0 -Dimglib2.version=2.2.1 -Dtrove4j.version=3.0.3 -Deventbus.version=1.4 -Dhamcrest-core.version=1.3 -Dscijava-common.version=2.44.2 test
+ [INFO] net.imagej:imagej-common:0.15.1: complete
+
+ $ find melting-pot -maxdepth 2 | sort
+ melting-pot
+ melting-pot/net.imagej
+ melting-pot/net.imagej/imagej-common
+ melting-pot/net.imglib2
+ melting-pot/net.imglib2/imglib2
+ melting-pot/pom.xml
diff --git a/tests/readme.txt b/tests/readme.txt
new file mode 100644
index 0000000..91db61b
--- /dev/null
+++ b/tests/readme.txt
@@ -0,0 +1,5 @@
+This directory houses automated tests for use with cram.
+
+ https://bitheap.org/cram/
+
+To run them, type "cram tests" from the toplevel directory.