package forge import utest._ import Target.test import java.nio.{file => jnio} object ForgeTests extends TestSuite{ val tests = Tests{ val baseCtx = DefCtx("forge.ForgeTests.tests ") val evaluator = new Evaluator(jnio.Paths.get("target/workspace"), baseCtx) object Singleton { val single = T{ test() } } object Pair { val up = T{ test() } val down = T{ test(up) } } object AnonTriple{ val up = T{ test() } val down = T{ test(test(up)) } } object Diamond{ val up = T{ test() } val left = T{ test(up) } val right = T{ test(up) } val down = T{ test(left, right) } } object AnonDiamond{ val up = T{ test() } val down = T{ test(test(up), test(up)) } } 'syntaxLimits - { // Make sure that we properly prohibit cases where a `test()` target can // be created more than once with the same `DefCtx`, while still allowing // cases where the `test()` target is created exactly one time, or even // zero-or-one times (since that's ok, as long as it's not more than once) 'neg - { 'nakedTest - { compileError("test()") () } 'notFunctionCall - { compileError("T{ 123 }") () } 'functionCallWithoutImplicit - { compileError("T{ println() }") () } // Make sure the snippets without `test()`s compile, but the same snippets // *with* the `test()` calls do not (presumably due to the `@compileTimeOnly` // annotation) // // For some reason, `if(false)` isn't good enough because scalac constant // folds the conditional, eliminates the entire code block, and makes any // `@compileTimeOnly`s annotations disappear... 'canEvaluateMoreThanOnce - { if (math.random() > 10) T{ Seq(1, 2).map(_ => ???); test() } compileError("T{ Seq(1, 2).map(_ => test()); test() }") if (math.random() > 10) T{ class Foo{ ??? }; test() } compileError("T{ class Foo{ test() }; test() }") if (math.random() > 10) T{ test({while(true){ }; ???}) } compileError("T{ test({while(true){ test() }; ???}) }") if (math.random() > 10) T{ do{ } while(true); test() } compileError("T{ do{ test() } while(true); test() }") if (math.random() > 10) T{ def foo() = ???; test() } compileError("T{ def foo() = test(); test() }") if (math.random() > 10) T{ None.getOrElse(???); test() } if (math.random() > 10) T{ None.contains(test()); test() } compileError("T{ None.getOrElse(test()); test() }") () } } 'pos - { T{ test({val x = test(); x}) } T{ test({lazy val x = test(); x}) } T { object foo {val x = test()}; test(foo.x) } T{ test({val x = if (math.random() > 0.5) test() else test(); x}) } () } } 'topoSortedTransitiveTargets - { def check(targets: Seq[Target[_]], expected: Seq[Target[_]]) = { val result = Evaluator.topoSortedTransitiveTargets(targets) assert(result == expected) } 'singleton - check( targets = Seq(Singleton.single), expected = Seq(Singleton.single) ) 'pair - check( targets = Seq(Pair.down), expected = Seq(Pair.up, Pair.down) ) 'anonTriple - check( targets = Seq(AnonTriple.down), expected = Seq(AnonTriple.up, AnonTriple.down.inputs(0), AnonTriple.down) ) 'diamond - check( targets = Seq(Diamond.down), expected = Seq(Diamond.up, Diamond.left, Diamond.right, Diamond.down) ) 'anonDiamond - check( targets = Seq(Diamond.down), expected = Seq( Diamond.up, Diamond.down.inputs(0), Diamond.down.inputs(1), Diamond.down ) ) } 'labeling - { def check(t: Target[_], relPath: String) = { val targetLabel = t.defCtx.label val expectedLabel = baseCtx.label + relPath assert(targetLabel == expectedLabel) } 'singleton - check(Singleton.single, "Singleton.single") 'pair - { check(Pair.up, "Pair.up") check(Pair.down, "Pair.down") } 'anonTriple - { check(AnonTriple.up, "AnonTriple.up") check(AnonTriple.down.inputs(0), "AnonTriple.down1") check(AnonTriple.down, "AnonTriple.down") } 'diamond - { check(Diamond.up, "Diamond.up") check(Diamond.left, "Diamond.left") check(Diamond.right, "Diamond.right") check(Diamond.down, "Diamond.down") } 'anonDiamond - { check(AnonDiamond.up, "AnonDiamond.up") check(AnonDiamond.down.inputs(0), "AnonDiamond.down1") check(AnonDiamond.down.inputs(1), "AnonDiamond.down2") check(AnonDiamond.down, "AnonDiamond.down") } } 'evaluateSingle - { def check(target: Target[_], expValue: Any, expEvaled: Seq[Target[_]]) = { val Evaluator.Results(returnedValues, returnedEvaluated) = evaluator.evaluate(Seq(target)) assert( returnedValues == Seq(expValue), returnedEvaluated == expEvaled ) // Second time the value is already cached, so no evaluation needed val Evaluator.Results(returnedValues2, returnedEvaluated2) = evaluator.evaluate(Seq(target)) assert( returnedValues2 == returnedValues, returnedEvaluated2 == Nil ) } 'singleton - { import Singleton._ // First time the target is evaluated check(single, expValue = 0, expEvaled = Seq(single)) single.counter += 1 // After incrementing the counter, it forces re-evaluation check(single, expValue = 1, expEvaled = Seq(single)) } 'pair - { import Pair._ check(down, expValue = 0, expEvaled = Seq(up, down)) down.counter += 1 check(down, expValue = 1, expEvaled = Seq(down)) up.counter += 1 check(down, expValue = 2, expEvaled = Seq(up, down)) } 'anonTriple - { import AnonTriple._ val middle = down.inputs(0) check(down, expValue = 0, expEvaled = Seq(up, middle, down)) down.counter += 1 check(down, expValue = 1, expEvaled = Seq(down)) up.counter += 1 check(down, expValue = 2, expEvaled = Seq(up, middle, down)) middle.asInstanceOf[Target.Test].counter += 1 check(down, expValue = 3, expEvaled = Seq(middle, down)) } 'diamond - { import Diamond._ check(down, expValue = 0, expEvaled = Seq(up, left, right, down)) down.counter += 1 check(down, expValue = 1, expEvaled = Seq(down)) up.counter += 1 // Increment by 2 because up is referenced twice: once by left once by right check(down, expValue = 3, expEvaled = Seq(up, left, right, down)) left.counter += 1 check(down, expValue = 4, expEvaled = Seq(left, down)) right.counter += 1 check(down, expValue = 5, expEvaled = Seq(right, down)) } 'anoniamond - { import AnonDiamond._ val left = down.inputs(0).asInstanceOf[Target.Test] val right = down.inputs(1).asInstanceOf[Target.Test] check(down, expValue = 0, expEvaled = Seq(up, left, right, down)) down.counter += 1 check(down, expValue = 1, expEvaled = Seq(down)) up.counter += 1 // Increment by 2 because up is referenced twice: once by left once by right check(down, expValue = 3, expEvaled = Seq(up, left, right, down)) left.counter += 1 check(down, expValue = 4, expEvaled = Seq(left, down)) right.counter += 1 check(down, expValue = 5, expEvaled = Seq(right, down)) } } // 'full - { // val sourceRoot = Target.path(jnio.Paths.get("src/test/resources/example/src")) // val resourceRoot = Target.path(jnio.Paths.get("src/test/resources/example/resources")) // val allSources = list(sourceRoot) // val classFiles = compileAll(allSources) // val jar = jarUp(resourceRoot, classFiles) // Evaluator.apply(jar, jnio.Paths.get("target/workspace")) // } } }