From 8175b657bd5d008db67fc5d4f28e8463acb8d1ad Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Tue, 24 Oct 2017 22:44:50 -0700 Subject: Macro-based anonymous `Target` naming now works: we use the `T{...}` macro look at any `Target`s defined lexically within the call, and as long as they can only be evaluated once (relative to the `T{...}`) we assign them labels with an incrementing integer suffixed onto the enclosing `T{...}`s label --- src/test/scala/forge/ForgeTests.scala | 143 ++++++++++++++++++++++++++++------ 1 file changed, 118 insertions(+), 25 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/forge/ForgeTests.scala b/src/test/scala/forge/ForgeTests.scala index d5275d0c..cf903461 100644 --- a/src/test/scala/forge/ForgeTests.scala +++ b/src/test/scala/forge/ForgeTests.scala @@ -6,7 +6,8 @@ import java.nio.{file => jnio} object ForgeTests extends TestSuite{ val tests = Tests{ - val evaluator = new Evaluator(jnio.Paths.get("target/workspace"), implicitly) + val baseCtx = DefCtx("forge.ForgeTests.tests ") + val evaluator = new Evaluator(jnio.Paths.get("target/workspace"), baseCtx) object Singleton { val single = T{ test() } } @@ -30,11 +31,69 @@ object ForgeTests extends TestSuite{ val down = T{ test(test(up), test(up)) } } - 'neg - { - compileError("T{ 123 }") - compileError("T{ println() }") - () + + '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) @@ -67,55 +126,89 @@ object ForgeTests extends TestSuite{ ) ) } + '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") + } + + } 'evaluate - { def check(targets: Seq[Target[_]], - values: Seq[Any], - evaluated: Seq[Target[_]]) = { + expectedValues: Seq[Any], + expectedEvaluated: Seq[Target[_]]) = { val Evaluator.Results(returnedValues, returnedEvaluated) = evaluator.evaluate(targets) assert( - returnedValues == values, - returnedEvaluated == evaluated + returnedValues == expectedValues, + returnedEvaluated == expectedEvaluated ) } 'singleton - { import Singleton._ // First time the target is evaluated - check(Seq(single), values = Seq(0), evaluated = Seq(single)) + check(Seq(single), expectedValues = Seq(0), expectedEvaluated = Seq(single)) // Second time the value is already cached, so no evaluation needed - check(Seq(single), values = Seq(0), evaluated = Seq()) + check(Seq(single), expectedValues = Seq(0), expectedEvaluated = Seq()) single.counter += 1 // After incrementing the counter, it forces re-evaluation - check(Seq(single), values = Seq(1), evaluated = Seq(single)) + check(Seq(single), expectedValues = Seq(1), expectedEvaluated = Seq(single)) // Then it's cached again - check(Seq(single), values = Seq(1), evaluated = Seq()) + check(Seq(single), expectedValues = Seq(1), expectedEvaluated = Seq()) } 'pair - { import Pair._ - check(Seq(down), values = Seq(0), evaluated = Seq(up, down)) - check(Seq(down), values = Seq(0), evaluated = Seq()) + check(Seq(down), expectedValues = Seq(0), expectedEvaluated = Seq(up, down)) + check(Seq(down), expectedValues = Seq(0), expectedEvaluated = Seq()) down.counter += 1 - check(Seq(down), values = Seq(1), evaluated = Seq(down)) - check(Seq(down), values = Seq(1), evaluated = Seq()) + check(Seq(down), expectedValues = Seq(1), expectedEvaluated = Seq(down)) + check(Seq(down), expectedValues = Seq(1), expectedEvaluated = Seq()) up.counter += 1 - check(Seq(down), values = Seq(2), evaluated = Seq(up, down)) - check(Seq(down), values = Seq(2), evaluated = Seq()) + check(Seq(down), expectedValues = Seq(2), expectedEvaluated = Seq(up, down)) + check(Seq(down), expectedValues = Seq(2), expectedEvaluated = Seq()) } 'anonTriple - { import AnonTriple._ val middle = down.inputs(0) - check(Seq(down), values = Seq(0), evaluated = Seq(up, middle, down)) - check(Seq(down), values = Seq(0), evaluated = Seq()) + check(Seq(down), expectedValues = Seq(0), expectedEvaluated = Seq(up, middle, down)) + check(Seq(down), expectedValues = Seq(0), expectedEvaluated = Seq()) down.counter += 1 - check(Seq(down), values = Seq(1), evaluated = Seq(middle, down)) - check(Seq(down), values = Seq(1), evaluated = Seq()) + check(Seq(down), expectedValues = Seq(1), expectedEvaluated = Seq(down)) + check(Seq(down), expectedValues = Seq(1), expectedEvaluated = Seq()) up.counter += 1 - check(Seq(down), values = Seq(2), evaluated = Seq(up, middle, down)) - check(Seq(down), values = Seq(2), evaluated = Seq()) + check(Seq(down), expectedValues = Seq(2), expectedEvaluated = Seq(up, middle, down)) + check(Seq(down), expectedValues = Seq(2), expectedEvaluated = Seq()) } } -- cgit v1.2.3