From 9ba4cb69331386dfde9bac69dc2d5b22401face3 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Wed, 12 Dec 2018 16:56:02 -0800 Subject: collapse boilerplate folder structure within src/ folders (#505) * collapse boilerplate folder structure within src/ folders * . --- main/test/src/define/ApplicativeTests.scala | 125 ++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 main/test/src/define/ApplicativeTests.scala (limited to 'main/test/src/define/ApplicativeTests.scala') diff --git a/main/test/src/define/ApplicativeTests.scala b/main/test/src/define/ApplicativeTests.scala new file mode 100644 index 00000000..9dd2132f --- /dev/null +++ b/main/test/src/define/ApplicativeTests.scala @@ -0,0 +1,125 @@ +package mill.define + +import mill.api.Ctx.ImplicitStub +import utest._ + +import scala.annotation.compileTimeOnly +import scala.language.experimental.macros + + +object ApplicativeTests extends TestSuite { + implicit def optionToOpt[T](o: Option[T]): Opt[T] = new Opt(o) + class Opt[T](val self: Option[T]) extends Applicative.Applyable[Option, T] + object Opt extends OptGenerated with Applicative.Applyer[Opt, Option, Applicative.Id, String]{ + + val injectedCtx = "helloooo" + def underlying[A](v: Opt[A]) = v.self + def apply[T](t: T): Option[T] = macro Applicative.impl[Option, T, String] + + def mapCtx[A, B](a: Option[A])(f: (A, String) => B): Option[B] = a.map(f(_, injectedCtx)) + def zip() = Some(()) + def zip[A](a: Option[A]) = a.map(Tuple1(_)) + } + class Counter{ + var value = 0 + def apply() = { + value += 1 + value + } + } + @compileTimeOnly("Target.ctx() can only be used with a T{...} block") + @ImplicitStub + implicit def taskCtx: String = ??? + + val tests = Tests{ + + 'selfContained - { + + 'simple - assert(Opt("lol " + 1) == Some("lol 1")) + 'singleSome - assert(Opt("lol " + Some("hello")()) == Some("lol hello")) + 'twoSomes - assert(Opt(Some("lol ")() + Some("hello")()) == Some("lol hello")) + 'singleNone - assert(Opt("lol " + None()) == None) + 'twoNones - assert(Opt("lol " + None() + None()) == None) + } + 'context - { + assert(Opt(Opt.ctx() + Some("World")()) == Some("hellooooWorld")) + } + 'capturing - { + val lol = "lol " + def hell(o: String) = "hell" + o + 'simple - assert(Opt(lol + 1) == Some("lol 1")) + 'singleSome - assert(Opt(lol + Some(hell("o"))()) == Some("lol hello")) + 'twoSomes - assert(Opt(Some(lol)() + Some(hell("o"))()) == Some("lol hello")) + 'singleNone - assert(Opt(lol + None()) == None) + 'twoNones - assert(Opt(lol + None() + None()) == None) + } + 'allowedLocalDef - { + // Although x is defined inside the Opt{...} block, it is also defined + // within the LHS of the Applyable#apply call, so it is safe to life it + // out into the `zipMap` arguments list. + val res = Opt{ "lol " + Some("hello").flatMap(x => Some(x)).apply() } + assert(res == Some("lol hello")) + } + 'upstreamAlwaysEvaluated - { + // Whether or not control-flow reaches the Applyable#apply call inside an + // Opt{...} block, we always evaluate the LHS of the Applyable#apply + // because it gets lifted out of any control flow statements + val counter = new Counter() + def up = Opt{ "lol " + counter() } + val down = Opt{ if ("lol".length > 10) up() else "fail" } + assert( + down == Some("fail"), + counter.value == 1 + ) + } + 'upstreamEvaluatedOnlyOnce - { + // Even if control-flow reaches the Applyable#apply call more than once, + // it only gets evaluated once due to its lifting out of the Opt{...} block + val counter = new Counter() + def up = Opt{ "lol " + counter() } + def runTwice[T](t: => T) = (t, t) + val down = Opt{ runTwice(up()) } + assert( + down == Some(("lol 1", "lol 1")), + counter.value == 1 + ) + } + 'evaluationsInsideLambdasWork - { + // This required some fiddling with owner chains inside the macro to get + // working, so ensure it doesn't regress + val counter = new Counter() + def up = Opt{ "hello" + counter() } + val down1 = Opt{ (() => up())() } + val down2 = Opt{ Seq(1, 2, 3).map(n => up() * n) } + assert( + down1 == Some("hello1"), + down2 == Some(Seq("hello2", "hello2hello2", "hello2hello2hello2")) + ) + } + 'appliesEvaluatedOncePerLexicalCallsite - { + // If you have multiple Applyable#apply() lexically in the source code of + // your Opt{...} call, each one gets evaluated once, even if the LHS of each + // apply() call is identical. It's up to the downstream zipMap() + // implementation to decide if it wants to dedup them or do other things. + val counter = new Counter() + def up = Opt{ "hello" + counter() } + val down = Opt{ Seq(1, 2, 3).map(n => n + up() + up()) } + assert(down == Some(Seq("1hello1hello2", "2hello1hello2", "3hello1hello2"))) + } + 'appliesEvaluateBeforehand - { + // Every Applyable#apply() within a Opt{...} block evaluates before any + // other logic within that block, even if they would happen first in the + // normal Scala evaluation order + val counter = new Counter() + def up = Opt{ counter() } + val down = Opt{ + val res = counter() + val one = up() + val two = up() + val three = up() + (res, one, two, three) + } + assert(down == Some((4, 1, 2, 3))) + } + } +} -- cgit v1.2.3