diff options
author | Li Haoyi <haoyi.sg@gmail.com> | 2017-11-03 23:44:39 -0700 |
---|---|---|
committer | Li Haoyi <haoyi.sg@gmail.com> | 2017-11-03 23:44:39 -0700 |
commit | 13270145903b457c906a9fa77bd152afb6448ef5 (patch) | |
tree | e85b7ed530e0c8e3c3041cbf17641857c448b602 /core/src/test/scala/forge | |
parent | 66f1c5c2438aeb8f2496575f52c25b09cf5793a6 (diff) | |
download | mill-13270145903b457c906a9fa77bd152afb6448ef5.tar.gz mill-13270145903b457c906a9fa77bd152afb6448ef5.tar.bz2 mill-13270145903b457c906a9fa77bd152afb6448ef5.zip |
Split up forge into `scalaplugin` an `core` subprojects, to allow us to use the `T#apply` macro in the implementation of `scalaplugin.Subproject`
Also needed to implement inter-`Subproject` dependencies so the `MetacircularTests` can continue to support the new layout
Diffstat (limited to 'core/src/test/scala/forge')
-rw-r--r-- | core/src/test/scala/forge/EvaluationTests.scala | 144 | ||||
-rw-r--r-- | core/src/test/scala/forge/GraphTests.scala | 187 | ||||
-rw-r--r-- | core/src/test/scala/forge/IntegrationTests.scala | 138 | ||||
-rw-r--r-- | core/src/test/scala/forge/TarjanTests.scala | 89 | ||||
-rw-r--r-- | core/src/test/scala/forge/TestGraphs.scala | 73 | ||||
-rw-r--r-- | core/src/test/scala/forge/TestMain.scala | 100 | ||||
-rw-r--r-- | core/src/test/scala/forge/TestUtil.scala | 36 | ||||
-rw-r--r-- | core/src/test/scala/forge/UTestFramework.scala | 11 |
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) + } +} |