summaryrefslogtreecommitdiff
path: root/core/src/test/scala/forge
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/test/scala/forge')
-rw-r--r--core/src/test/scala/forge/EvaluationTests.scala144
-rw-r--r--core/src/test/scala/forge/GraphTests.scala187
-rw-r--r--core/src/test/scala/forge/IntegrationTests.scala138
-rw-r--r--core/src/test/scala/forge/TarjanTests.scala89
-rw-r--r--core/src/test/scala/forge/TestGraphs.scala73
-rw-r--r--core/src/test/scala/forge/TestMain.scala100
-rw-r--r--core/src/test/scala/forge/TestUtil.scala36
-rw-r--r--core/src/test/scala/forge/UTestFramework.scala11
8 files changed, 778 insertions, 0 deletions
diff --git a/core/src/test/scala/forge/EvaluationTests.scala b/core/src/test/scala/forge/EvaluationTests.scala
new file mode 100644
index 00000000..b58d5ccc
--- /dev/null
+++ b/core/src/test/scala/forge/EvaluationTests.scala
@@ -0,0 +1,144 @@
+package forge
+
+
+import forge.util.OSet
+import utest._
+import utest.framework.TestPath
+
+object EvaluationTests extends TestSuite{
+
+ val tests = Tests{
+ val graphs = new TestGraphs()
+ import graphs._
+ 'evaluateSingle - {
+
+ class Checker[T: Discovered](base: T)(implicit tp: TestPath) {
+ val workspace = ammonite.ops.pwd / 'target / 'workspace / tp.value
+ ammonite.ops.rm(ammonite.ops.Path(workspace, ammonite.ops.pwd))
+ // Make sure data is persisted even if we re-create the evaluator each time
+ def evaluator = new Evaluator(
+ workspace,
+
+ Discovered.mapping(base)
+ )
+ def apply(target: Target[_], expValue: Any,
+ expEvaled: OSet[Target[_]],
+ extraEvaled: Int = 0) = {
+
+ val Evaluator.Results(returnedValues, returnedEvaluated) = evaluator.evaluate(OSet(target))
+
+ val (matchingReturnedEvaled, extra) = returnedEvaluated.items.partition(expEvaled.contains)
+
+ assert(
+ returnedValues == Seq(expValue),
+ matchingReturnedEvaled.toSet == expEvaled.toSet,
+ extra.length == extraEvaled
+ )
+
+ // Second time the value is already cached, so no evaluation needed
+ val Evaluator.Results(returnedValues2, returnedEvaluated2) = evaluator.evaluate(OSet(target))
+ assert(
+ returnedValues2 == returnedValues,
+ returnedEvaluated2 == OSet()
+ )
+ }
+ }
+
+ 'singleton - {
+ import singleton._
+ val check = new Checker(singleton)
+ // First time the target is evaluated
+ check(single, expValue = 0, expEvaled = OSet(single))
+
+ single.counter += 1
+ // After incrementing the counter, it forces re-evaluation
+ check(single, expValue = 1, expEvaled = OSet(single))
+ }
+ 'pair - {
+ import pair._
+ val check = new Checker(pair)
+ check(down, expValue = 0, expEvaled = OSet(up, down))
+
+ down.counter += 1
+ check(down, expValue = 1, expEvaled = OSet(down))
+
+ up.counter += 1
+ check(down, expValue = 2, expEvaled = OSet(up, down))
+ }
+ 'anonTriple - {
+ import anonTriple._
+ val check = new Checker(anonTriple)
+ val middle = down.inputs(0)
+ check(down, expValue = 0, expEvaled = OSet(up, middle, down))
+
+ down.counter += 1
+ check(down, expValue = 1, expEvaled = OSet(middle, down))
+
+ up.counter += 1
+ check(down, expValue = 2, expEvaled = OSet(up, middle, down))
+
+ middle.asInstanceOf[TestUtil.Test].counter += 1
+
+ check(down, expValue = 3, expEvaled = OSet(middle, down))
+ }
+ 'diamond - {
+ import diamond._
+ val check = new Checker(diamond)
+ check(down, expValue = 0, expEvaled = OSet(up, left, right, down))
+
+ down.counter += 1
+ check(down, expValue = 1, expEvaled = OSet(down))
+
+ up.counter += 1
+ // Increment by 2 because up is referenced twice: once by left once by right
+ check(down, expValue = 3, expEvaled = OSet(up, left, right, down))
+
+ left.counter += 1
+ check(down, expValue = 4, expEvaled = OSet(left, down))
+
+ right.counter += 1
+ check(down, expValue = 5, expEvaled = OSet(right, down))
+ }
+ 'anonDiamond - {
+ import anonDiamond._
+ val check = new Checker(anonDiamond)
+ val left = down.inputs(0).asInstanceOf[TestUtil.Test]
+ val right = down.inputs(1).asInstanceOf[TestUtil.Test]
+ check(down, expValue = 0, expEvaled = OSet(up, left, right, down))
+
+ down.counter += 1
+ check(down, expValue = 1, expEvaled = OSet(left, right, down))
+
+ up.counter += 1
+ // Increment by 2 because up is referenced twice: once by left once by right
+ check(down, expValue = 3, expEvaled = OSet(up, left, right, down))
+
+ left.counter += 1
+ check(down, expValue = 4, expEvaled = OSet(left, right, down))
+
+ right.counter += 1
+ check(down, expValue = 5, expEvaled = OSet(left, right, down))
+ }
+
+ 'bigSingleTerminal - {
+ import bigSingleTerminal._
+ val check = new Checker(bigSingleTerminal)
+
+ check(j, expValue = 0, expEvaled = OSet(a, b, e, f, i, j), extraEvaled = 22)
+
+ j.counter += 1
+ check(j, expValue = 1, expEvaled = OSet(j), extraEvaled = 3)
+
+ i.counter += 1
+ // increment value by 2 because `i` is used twice on the way to `j`
+ check(j, expValue = 3, expEvaled = OSet(j, i), extraEvaled = 8)
+
+ b.counter += 1
+ // increment value by 4 because `b` is used four times on the way to `j`
+ check(j, expValue = 7, expEvaled = OSet(b, e, f, i, j), extraEvaled = 20)
+ }
+ }
+
+
+ }
+}
diff --git a/core/src/test/scala/forge/GraphTests.scala b/core/src/test/scala/forge/GraphTests.scala
new file mode 100644
index 00000000..572e459e
--- /dev/null
+++ b/core/src/test/scala/forge/GraphTests.scala
@@ -0,0 +1,187 @@
+package forge
+
+import utest._
+import TestUtil.test
+import forge.util.OSet
+
+object GraphTests extends TestSuite{
+
+ val tests = Tests{
+
+
+ val graphs = new TestGraphs()
+ import graphs._
+
+ 'discovery{
+ class CanNest{
+ val single = test()
+ val invisible: Any = test()
+ }
+ object outer {
+ val single = test()
+ val invisible: Any = test()
+ object nested{
+ val single = test()
+ val invisible: Any = test()
+
+ }
+ val classInstance = new CanNest
+
+ }
+ val discovered = Discovered[outer.type].apply(outer).map(x => (x._1, x._3))
+ val expected = Seq(
+ (List("classInstance", "single"), outer.classInstance.single),
+ (List("nested", "single"), outer.nested.single),
+ (List("single"), outer.single)
+ )
+ assert(discovered == expected)
+ }
+
+
+ 'topoSortedTransitiveTargets - {
+ def check(targets: OSet[Target[_]], expected: OSet[Target[_]]) = {
+ val result = Evaluator.topoSortedTransitiveTargets(targets).values
+ TestUtil.checkTopological(result)
+ assert(result == expected)
+ }
+
+ 'singleton - check(
+ targets = OSet(singleton.single),
+ expected = OSet(singleton.single)
+ )
+ 'pair - check(
+ targets = OSet(pair.down),
+ expected = OSet(pair.up, pair.down)
+ )
+ 'anonTriple - check(
+ targets = OSet(anonTriple.down),
+ expected = OSet(anonTriple.up, anonTriple.down.inputs(0), anonTriple.down)
+ )
+ 'diamond - check(
+ targets = OSet(diamond.down),
+ expected = OSet(diamond.up, diamond.left, diamond.right, diamond.down)
+ )
+ 'anonDiamond - check(
+ targets = OSet(diamond.down),
+ expected = OSet(
+ diamond.up,
+ diamond.down.inputs(0),
+ diamond.down.inputs(1),
+ diamond.down
+ )
+ )
+ 'bigSingleTerminal - {
+ val result = Evaluator.topoSortedTransitiveTargets(OSet(bigSingleTerminal.j)).values
+ TestUtil.checkTopological(result)
+ assert(result.size == 28)
+ }
+ }
+
+ 'groupAroundNamedTargets - {
+ def check[T: Discovered](base: T,
+ target: TestUtil.Test,
+ expected: OSet[(OSet[TestUtil.Test], Int)]) = {
+
+ val mapping = Discovered.mapping(base)
+ val topoSortedTransitive = Evaluator.topoSortedTransitiveTargets(OSet(target))
+
+ val grouped = Evaluator.groupAroundNamedTargets(topoSortedTransitive, mapping)
+ val flattened = OSet.from(grouped.values().flatMap(_.items))
+
+ TestUtil.checkTopological(flattened)
+ for(((expectedPresent, expectedSize), i) <- expected.items.zipWithIndex){
+ val grouping = grouped.lookupKey(i)
+ assert(
+ grouping.size == expectedSize,
+ grouping.filter(mapping.contains) == expectedPresent
+ )
+ }
+ }
+ 'singleton - check(
+ singleton,
+ singleton.single,
+ OSet(OSet(singleton.single) -> 1)
+ )
+ 'pair - check(
+ pair,
+ pair.down,
+ OSet(OSet(pair.up) -> 1, OSet(pair.down) -> 1)
+ )
+ 'anonTriple - check(
+ anonTriple,
+ anonTriple.down,
+ OSet(OSet(anonTriple.up) -> 1, OSet(anonTriple.down) -> 2)
+ )
+ 'diamond - check(
+ diamond,
+ diamond.down,
+ OSet(
+ OSet(diamond.up) -> 1,
+ OSet(diamond.left) -> 1,
+ OSet(diamond.right) -> 1,
+ OSet(diamond.down) -> 1
+ )
+ )
+ 'anonDiamond - check(
+ anonDiamond,
+ anonDiamond.down,
+ OSet(
+ OSet(anonDiamond.up) -> 1,
+ OSet(anonDiamond.down) -> 3
+ )
+ )
+ 'bigSingleTerminal - check(
+ bigSingleTerminal,
+ bigSingleTerminal.j,
+ OSet(
+ OSet(bigSingleTerminal.a) -> 3,
+ OSet(bigSingleTerminal.b) -> 2,
+ OSet(bigSingleTerminal.e) -> 9,
+ OSet(bigSingleTerminal.i) -> 6,
+ OSet(bigSingleTerminal.f) -> 4,
+ OSet(bigSingleTerminal.j) -> 4
+ )
+ )
+ }
+
+ 'labeling - {
+
+ def check[T: Discovered](base: T, t: Target[_], relPath: Option[String]) = {
+
+
+ val names: Seq[(Target[_], Seq[String])] = Discovered.mapping(base).mapValues(_.segments).toSeq
+ val nameMap = names.toMap
+
+ val targetLabel = nameMap.get(t).map(_.mkString("."))
+ assert(targetLabel == relPath)
+ }
+ 'singleton - check(singleton, singleton.single, Some("single"))
+ 'pair - {
+ check(pair, pair.up, Some("up"))
+ check(pair, pair.down, Some("down"))
+ }
+
+ 'anonTriple - {
+ check(anonTriple, anonTriple.up, Some("up"))
+ check(anonTriple, anonTriple.down.inputs(0), None)
+ check(anonTriple, anonTriple.down, Some("down"))
+ }
+
+ 'diamond - {
+ check(diamond, diamond.up, Some("up"))
+ check(diamond, diamond.left, Some("left"))
+ check(diamond, diamond.right, Some("right"))
+ check(diamond, diamond.down, Some("down"))
+ }
+
+ 'anonDiamond - {
+ check(anonDiamond, anonDiamond.up, Some("up"))
+ check(anonDiamond, anonDiamond.down.inputs(0), None)
+ check(anonDiamond, anonDiamond.down.inputs(1), None)
+ check(anonDiamond, anonDiamond.down, Some("down"))
+ }
+
+ }
+
+ }
+}
diff --git a/core/src/test/scala/forge/IntegrationTests.scala b/core/src/test/scala/forge/IntegrationTests.scala
new file mode 100644
index 00000000..03173eac
--- /dev/null
+++ b/core/src/test/scala/forge/IntegrationTests.scala
@@ -0,0 +1,138 @@
+package forge
+
+import java.io.FileOutputStream
+import java.util.jar.JarEntry
+
+import ammonite.ops._
+import forge.util.{Args, OSet, PathRef}
+import utest._
+
+object IntegrationTests extends TestSuite{
+ def compileAll(sources: Target[Seq[PathRef]]) = {
+ new Target.Subprocess(
+ Seq(sources),
+ args =>
+ Seq("javac") ++
+ args[Seq[PathRef]](0).map(_.path.toString) ++
+ Seq("-d", args.dest.toString)
+ ).map(_.dest)
+ }
+
+ def list(root: Target[PathRef]): Target[Seq[PathRef]] = {
+ root.map(x => ls.rec(x.path).map(PathRef(_)))
+ }
+
+ case class jarUp(roots: Target[PathRef]*) extends Target[PathRef]{
+
+ val inputs = roots
+ def evaluate(args: Args): PathRef = {
+
+ val output = new java.util.jar.JarOutputStream(new FileOutputStream(args.dest.toIO))
+ for{
+ root0 <- args.args
+ root = root0.asInstanceOf[PathRef]
+
+ path <- ls.rec(root.path)
+ if path.isFile
+ }{
+ val relative = path.relativeTo(root.path)
+ output.putNextEntry(new JarEntry(relative.toString))
+ output.write(read.bytes(path))
+ }
+ output.close()
+ PathRef(args.dest)
+ }
+ }
+
+ val tests = Tests{
+ 'javac {
+ val workspacePath = pwd / 'target / 'workspace / 'javac
+ val javacSrcPath = pwd / 'core / 'src / 'test / 'examples / 'javac
+ val javacDestPath = workspacePath / 'src
+
+ mkdir(pwd / 'target / 'workspace / 'javac)
+ cp(javacSrcPath, javacDestPath)
+
+ object Build {
+ val sourceRootPath = javacDestPath / 'src
+ val resourceRootPath = javacDestPath / 'resources
+
+ // sourceRoot -> allSources -> classFiles
+ // |
+ // v
+ // resourceRoot ----> jar
+ val sourceRoot = Target.path(sourceRootPath)
+ val resourceRoot = Target.path(resourceRootPath)
+ val allSources = list(sourceRoot)
+ val classFiles = compileAll(allSources)
+ val jar = jarUp(resourceRoot, classFiles)
+ }
+ import Build._
+ val mapping = Discovered.mapping(Build)
+
+ def check(targets: OSet[Target[_]], expected: OSet[Target[_]]) = {
+ val evaluator = new Evaluator(workspacePath, mapping)
+ val evaluated = evaluator.evaluate(targets).evaluated.filter(mapping.contains)
+ assert(evaluated == expected)
+ }
+
+ def append(path: Path, txt: String) = ammonite.ops.write.append(path, txt)
+
+
+ check(
+ targets = OSet(jar),
+ expected = OSet(resourceRoot, sourceRoot, allSources, classFiles, jar)
+ )
+
+ // Re-running with no changes results in nothing being evaluated
+ check(targets = OSet(jar), expected = OSet())
+
+ // Appending an empty string gets ignored due to file-content hashing
+ append(sourceRootPath / "Foo.java", "")
+ check(targets = OSet(jar), expected = OSet())
+
+ // Appending whitespace forces a recompile, but the classfilesend up
+ // exactly the same so no re-jarring.
+ append(sourceRootPath / "Foo.java", " ")
+ check(targets = OSet(jar), expected = OSet(sourceRoot, allSources, classFiles))
+
+ // Appending a new class changes the classfiles, which forces us to
+ // re-create the final jar
+ append(sourceRootPath / "Foo.java", "\nclass FooTwo{}")
+ check(targets = OSet(jar), expected = OSet(sourceRoot, allSources, classFiles, jar))
+
+ // Tweaking the resources forces rebuild of the final jar, without
+ // recompiling classfiles
+ append(resourceRootPath / "hello.txt", " ")
+ check(targets = OSet(jar), expected = OSet(resourceRoot, jar))
+
+ // Asking for an intermediate target forces things to be build up to that
+ // target only; these are re-used for any downstream targets requested
+ append(sourceRootPath / "Bar.java", "\nclass BarTwo{}")
+ append(resourceRootPath / "hello.txt", " ")
+ check(targets = OSet(classFiles), expected = OSet(sourceRoot, allSources, classFiles))
+ check(targets = OSet(jar), expected = OSet(resourceRoot, jar))
+ check(targets = OSet(allSources), expected = OSet())
+
+ append(sourceRootPath / "Bar.java", "\nclass BarThree{}")
+ append(resourceRootPath / "hello.txt", " ")
+ check(targets = OSet(resourceRoot), expected = OSet(resourceRoot))
+ check(targets = OSet(allSources), expected = OSet(sourceRoot, allSources))
+ check(targets = OSet(jar), expected = OSet(classFiles, jar))
+
+ val jarContents = %%('jar, "-tf", workspacePath/'jar)(workspacePath).out.string
+ val expectedJarContents =
+ """hello.txt
+ |test/Bar.class
+ |test/BarThree.class
+ |test/BarTwo.class
+ |test/Foo.class
+ |test/FooTwo.class
+ |""".stripMargin
+ assert(jarContents == expectedJarContents)
+
+ val executed = %%('java, "-cp", workspacePath/'jar, "test.Foo")(workspacePath).out.string
+ assert(executed == (31337 + 271828) + "\n")
+ }
+ }
+}
diff --git a/core/src/test/scala/forge/TarjanTests.scala b/core/src/test/scala/forge/TarjanTests.scala
new file mode 100644
index 00000000..5b118368
--- /dev/null
+++ b/core/src/test/scala/forge/TarjanTests.scala
@@ -0,0 +1,89 @@
+package forge
+import utest._
+object TarjanTests extends TestSuite{
+ def check(input: Seq[Seq[Int]], expected: Seq[Seq[Int]]) = {
+ val result = Tarjans(input).map(_.sorted)
+ val sortedExpected = expected.map(_.sorted)
+ assert(result == sortedExpected)
+ }
+ val tests = Tests{
+ //
+ 'empty - check(Seq(), Seq())
+
+ // (0)
+ 'singleton - check(Seq(Seq()), Seq(Seq(0)))
+
+
+ // (0)-.
+ // ^._/
+ 'selfCycle - check(Seq(Seq(0)), Seq(Seq(0)))
+
+ // (0) <-> (1)
+ 'simpleCycle- check(Seq(Seq(1), Seq(0)), Seq(Seq(1, 0)))
+
+ // (0) (1) (2)
+ 'multipleSingletons - check(
+ Seq(Seq(), Seq(), Seq()),
+ Seq(Seq(0), Seq(1), Seq(2))
+ )
+
+ // (0) -> (1) -> (2)
+ 'straightLineNoCycles- check(
+ Seq(Seq(1), Seq(2), Seq()),
+ Seq(Seq(2), Seq(1), Seq(0))
+ )
+
+ // (0) <- (1) <- (2)
+ 'straightLineNoCyclesReversed- check(
+ Seq(Seq(), Seq(0), Seq(1)),
+ Seq(Seq(0), Seq(1), Seq(2))
+ )
+
+ // (0) <-> (1) (2) -> (3) -> (4)
+ // ^.____________/
+ 'independentSimpleCycles - check(
+ Seq(Seq(1), Seq(0), Seq(3), Seq(4), Seq(2)),
+ Seq(Seq(1, 0), Seq(4, 3, 2))
+ )
+
+ // ___________________
+ // v \
+ // (0) <-> (1) (2) -> (3) -> (4)
+ // ^.____________/
+ 'independentLinkedCycles - check(
+ Seq(Seq(1), Seq(0), Seq(3), Seq(4), Seq(2, 1)),
+ Seq(Seq(1, 0), Seq(4, 3, 2))
+ )
+ // _____________
+ // / v
+ // (0) <-> (1) (2) -> (3) -> (4)
+ // ^.____________/
+ 'independentLinkedCycles2 - check(
+ Seq(Seq(1, 2), Seq(0), Seq(3), Seq(4), Seq(2)),
+ Seq(Seq(4, 3, 2), Seq(1, 0))
+ )
+
+ // _____________
+ // / v
+ // (0) <-> (1) (2) -> (3) -> (4)
+ // ^. ^.____________/
+ // \________________/
+ 'combinedCycles - check(
+ Seq(Seq(1, 2), Seq(0), Seq(3), Seq(4), Seq(2, 1)),
+ Seq(Seq(4, 3, 2, 1, 0))
+ )
+ //
+ // (0) <-> (1) <- (2) <- (3) <-> (4) <- (5)
+ // ^.____________/ / /
+ // / /
+ // (6) <- (7) <-/ (8) <-'
+ // / /
+ // v /
+ // (9) <--------'
+ 'combinedCycles - check(
+ Seq(Seq(1), Seq(0), Seq(0, 1), Seq(2, 4, 7, 9), Seq(3), Seq(4, 8), Seq(9), Seq(6), Seq(), Seq()),
+ Seq(Seq(0, 1), Seq(2), Seq(9), Seq(6), Seq(7), Seq(3, 4), Seq(8), Seq(5))
+ )
+
+ }
+} \ No newline at end of file
diff --git a/core/src/test/scala/forge/TestGraphs.scala b/core/src/test/scala/forge/TestGraphs.scala
new file mode 100644
index 00000000..0ee48a18
--- /dev/null
+++ b/core/src/test/scala/forge/TestGraphs.scala
@@ -0,0 +1,73 @@
+package forge
+
+import forge.TestUtil.test
+
+class TestGraphs(){
+ // single
+ object singleton {
+ val single = test()
+ }
+
+ // up---down
+ object pair {
+ val up = test()
+ val down = test(up)
+ }
+
+ // up---o---down
+ object anonTriple{
+ val up = test()
+ val down = test(test(up))
+ }
+
+ // left
+ // / \
+ // up down
+ // \ /
+ // right
+ object diamond{
+ val up = test()
+ val left = test(up)
+ val right = test(up)
+ val down = test(left, right)
+ }
+
+ // o
+ // / \
+ // up down
+ // \ /
+ // o
+ object anonDiamond{
+ val up = test()
+ val down = test(test(up), test(up))
+ }
+
+ // o g-----o
+ // \ \ \
+ // o o h-----I---o
+ // \ / \ / \ / \ \
+ // A---c--o E o-o \ \
+ // / \ / \ / \ o---J
+ // o d o--o o / /
+ // \ / \ / /
+ // o o---F---o
+ // / /
+ // o--B o
+ object bigSingleTerminal{
+ val a = test(test(), test())
+ val b = test(test())
+ val e = {
+ val c = test(a)
+ val d = test(a)
+ test(test(test(), test(c)), test(test(c, test(d, b))))
+ }
+ val f = test(test(test(), test(e)))
+
+ val i = {
+ val g = test()
+ val h = test(g, e)
+ test(test(g), test(test(h)))
+ }
+ val j = test(test(i), test(i, f), test(f))
+ }
+}
diff --git a/core/src/test/scala/forge/TestMain.scala b/core/src/test/scala/forge/TestMain.scala
new file mode 100644
index 00000000..c94e13f0
--- /dev/null
+++ b/core/src/test/scala/forge/TestMain.scala
@@ -0,0 +1,100 @@
+package forge
+import ammonite.ops._
+import java.io.File
+
+import coursier._
+import sbt.internal.inc.{FreshCompilerCache, ScalaInstance, ZincUtil}
+import sbt.internal.util.{ConsoleOut, MainAppender}
+import sbt.util.LogExchange
+import xsbti.api.{ClassLike, DependencyContext}
+import xsbti.compile._
+
+import scalaz.concurrent.Task
+
+object TestMain {
+ def main(args: Array[String]): Unit = {
+ val scalaVersion = "2.12.4"
+ val start = Resolution(
+ Set(
+ Dependency(Module("org.scala-lang", "scala-reflect"), scalaVersion),
+ Dependency(Module("org.scala-lang", "scala-compiler"), scalaVersion),
+ Dependency(Module("org.scala-lang", "scala-reflect"), scalaVersion),
+ Dependency(Module("org.scala-sbt", "compiler-bridge_2.12"), "1.0.3"),
+ Dependency(Module("com.lihaoyi", "sourcecode_2.12"), "0.1.4"),
+ Dependency(Module("com.lihaoyi", "pprint_2.12"), "0.5.3"),
+ Dependency(Module("com.lihaoyi", "ammonite_2.12.4"), "1.0.3"),
+ Dependency(Module("com.typesafe.play", "play-json_2.12"), "2.6.6"),
+ Dependency(Module("org.scala-sbt", "zinc_2.12"), "1.0.3")
+ )
+ )
+ val repositories = Seq(
+ Cache.ivy2Local,
+ MavenRepository("https://repo1.maven.org/maven2")
+ )
+
+ val fetch = Fetch.from(repositories, Cache.fetch())
+ val resolution = start.process.run(fetch).unsafePerformSync
+
+
+ val localArtifacts: Seq[File] = Task.gatherUnordered(
+ resolution.artifacts.map(Cache.file(_).run)
+ ).unsafePerformSync.flatMap(_.toOption)
+
+ pprint.log(localArtifacts)
+ def grepJar(s: String) = localArtifacts.find(_.toString.endsWith(s)).get
+
+ val scalac = ZincUtil.scalaCompiler(
+ new ScalaInstance(
+ version = scalaVersion,
+ loader = getClass.getClassLoader,
+ libraryJar = grepJar(s"scala-library-$scalaVersion.jar"),
+ compilerJar = grepJar(s"scala-compiler-$scalaVersion.jar"),
+ allJars = localArtifacts.toArray,
+ explicitActual = None
+ ),
+ grepJar("compiler-bridge_2.12-1.0.3.jar")
+ )
+
+ val outputDir = pwd/'target/'zinc
+ mkdir(outputDir)
+ val scalaFiles = ls.rec(pwd/'src/'main/'scala/'forge).filter(_.ext == "scala").map(_.toIO).toArray
+
+ pprint.log(scalaFiles)
+ scalac.apply(
+ sources = scalaFiles,
+ changes = new DependencyChanges {
+ def isEmpty = true
+ def modifiedBinaries() = Array[File]()
+ def modifiedClasses() = Array[String]()
+ },
+ classpath = localArtifacts.toArray,
+ singleOutput = outputDir.toIO,
+ options = Array(),
+ callback = new xsbti.AnalysisCallback {
+ def startSource(source: File) = ()
+ def apiPhaseCompleted() = ()
+ def enabled() = true
+ def binaryDependency(onBinaryEntry: File, onBinaryClassName: String, fromClassName: String, fromSourceFile: File, context: DependencyContext) = ()
+ def generatedNonLocalClass(source: File, classFile: File, binaryClassName: String, srcClassName: String) = ()
+ def problem(what: String, pos: xsbti.Position, msg: String, severity: xsbti.Severity, reported: Boolean) = ()
+ def dependencyPhaseCompleted() = ()
+ def classDependency(onClassName: String, sourceClassName: String, context: DependencyContext) = ()
+ def generatedLocalClass(source: File, classFile: File) = ()
+ def api(sourceFile: File, classApi: ClassLike) = ()
+
+ def mainClass(sourceFile: File, className: String) = ()
+ def usedName(className: String, name: String, useScopes: java.util.EnumSet[xsbti.UseScope]) = ()
+ },
+ maximumErrors = 10,
+ cache = new FreshCompilerCache(),
+ log = {
+ val console = ConsoleOut.systemOut
+ val consoleAppender = MainAppender.defaultScreen(console)
+ val l = LogExchange.logger("Hello")
+ LogExchange.unbindLoggerAppenders("Hello")
+ LogExchange.bindLoggerAppenders("Hello", (consoleAppender -> sbt.util.Level.Warn) :: Nil)
+ l
+ }
+ )
+ }
+}
diff --git a/core/src/test/scala/forge/TestUtil.scala b/core/src/test/scala/forge/TestUtil.scala
new file mode 100644
index 00000000..9337fbe0
--- /dev/null
+++ b/core/src/test/scala/forge/TestUtil.scala
@@ -0,0 +1,36 @@
+package forge
+
+import forge.util.{Args, OSet}
+import utest.assert
+import scala.collection.mutable
+
+object TestUtil {
+ def test(inputs: Target[Int]*) = {
+ new Test(inputs, pure = inputs.nonEmpty)
+ }
+
+ /**
+ * A dummy target that takes any number of inputs, and whose output can be
+ * controlled externally, so you can construct arbitrary dataflow graphs and
+ * test how changes propagate.
+ */
+ class Test(val inputs: Seq[Target[Int]],
+ val pure: Boolean) extends Target[Int]{
+ var counter = 0
+ def evaluate(args: Args) = {
+ counter + args.args.map(_.asInstanceOf[Int]).sum
+ }
+
+ override def sideHash = counter
+ }
+ def checkTopological(targets: OSet[Target[_]]) = {
+ val seen = mutable.Set.empty[Target[_]]
+ for(t <- targets.items.reverseIterator){
+ seen.add(t)
+ for(upstream <- t.inputs){
+ assert(!seen(upstream))
+ }
+ }
+ }
+
+}
diff --git a/core/src/test/scala/forge/UTestFramework.scala b/core/src/test/scala/forge/UTestFramework.scala
new file mode 100644
index 00000000..3435e9c0
--- /dev/null
+++ b/core/src/test/scala/forge/UTestFramework.scala
@@ -0,0 +1,11 @@
+package forge
+
+class UTestFramework extends utest.runner.Framework {
+ override def exceptionStackFrameHighlighter(s: StackTraceElement) = {
+ s.getClassName.startsWith("forge.")
+ }
+ override def setup() = {
+ import ammonite.ops._
+ rm(pwd / 'target / 'workspace)
+ }
+}