From 3cb0e784a05db7d0b542cec9bf4c5fbf3772a6cf Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Sat, 4 Aug 2012 21:55:35 +0200 Subject: Basing Futures on Try instead of Either --- test/files/jvm/future-spec/FutureTests.scala | 19 ++-- test/files/jvm/future-spec/PromiseTests.scala | 14 +-- test/files/jvm/future-spec/TryTests.scala | 119 ++++++++++++++++++++++++ test/files/jvm/future-spec/main.scala | 4 +- test/files/jvm/scala-concurrent-tck.scala | 91 ++---------------- test/files/jvm/try-type-tests.scala | 128 +------------------------- 6 files changed, 148 insertions(+), 227 deletions(-) create mode 100644 test/files/jvm/future-spec/TryTests.scala (limited to 'test/files') diff --git a/test/files/jvm/future-spec/FutureTests.scala b/test/files/jvm/future-spec/FutureTests.scala index 30e1a722bf..31bb8c4e44 100644 --- a/test/files/jvm/future-spec/FutureTests.scala +++ b/test/files/jvm/future-spec/FutureTests.scala @@ -6,6 +6,7 @@ import scala.concurrent.util.duration._ import scala.concurrent.util.Duration.Inf import scala.collection._ import scala.runtime.NonLocalReturnControl +import scala.util.{Try,Success,Failure} @@ -197,7 +198,7 @@ object FutureTests extends MinimalScalaTest { } andThen { case _ => q.add(2) } andThen { - case Right(0) => q.add(Int.MaxValue) + case Success(0) => q.add(Int.MaxValue) } andThen { case _ => q.add(3); } @@ -260,13 +261,13 @@ object FutureTests extends MinimalScalaTest { } val futures = (0 to 9) map { - idx => async(idx, idx * 200) + idx => async(idx, idx * 20) } val folded = Future.fold(futures)(0)(_ + _) Await.result(folded, timeout) mustBe (45) val futuresit = (0 to 9) map { - idx => async(idx, idx * 200) + idx => async(idx, idx * 20) } val foldedit = Future.fold(futures)(0)(_ + _) Await.result(foldedit, timeout) mustBe (45) @@ -279,7 +280,7 @@ object FutureTests extends MinimalScalaTest { add } def futures = (0 to 9) map { - idx => async(idx, idx * 200) + idx => async(idx, idx * 20) } val folded = futures.foldLeft(Future(0)) { case (fr, fa) => for (r <- fr; a <- fa) yield (r + a) @@ -295,7 +296,7 @@ object FutureTests extends MinimalScalaTest { add } def futures = (0 to 9) map { - idx => async(idx, idx * 100) + idx => async(idx, idx * 10) } val folded = Future.fold(futures)(0)(_ + _) intercept[IllegalArgumentException] { @@ -326,7 +327,7 @@ object FutureTests extends MinimalScalaTest { "shouldReduceResults" in { def async(idx: Int) = future { - Thread.sleep(idx * 200) + Thread.sleep(idx * 20) idx } val timeout = 10000 millis @@ -348,7 +349,7 @@ object FutureTests extends MinimalScalaTest { } val timeout = 10000 millis def futures = (1 to 10) map { - idx => async(idx, idx * 100) + idx => async(idx, idx * 10) } val failed = Future.reduce(futures)(_ + _) intercept[IllegalArgumentException] { @@ -472,7 +473,7 @@ object FutureTests extends MinimalScalaTest { p1.future.isCompleted mustBe (false) f4.isCompleted mustBe (false) - p1 complete Right("Hello") + p1 complete Success("Hello") Await.ready(latch(7), TestLatch.DefaultTimeout) @@ -509,7 +510,7 @@ object FutureTests extends MinimalScalaTest { } "should not throw when Await.ready" in { - val expected = try Right(5 / 0) catch { case a: ArithmeticException => Left(a) } + val expected = try Success(5 / 0) catch { case a: ArithmeticException => Failure(a) } val f = future(5).map(_ / 0) Await.ready(f, defaultTimeout).value.get.toString mustBe expected.toString } diff --git a/test/files/jvm/future-spec/PromiseTests.scala b/test/files/jvm/future-spec/PromiseTests.scala index d15bb31f36..d9aaa1d5ed 100644 --- a/test/files/jvm/future-spec/PromiseTests.scala +++ b/test/files/jvm/future-spec/PromiseTests.scala @@ -6,7 +6,7 @@ import scala.concurrent.util.duration._ import scala.concurrent.util.Duration.Inf import scala.collection._ import scala.runtime.NonLocalReturnControl - +import scala.util.{Try,Success,Failure} object PromiseTests extends MinimalScalaTest { @@ -48,27 +48,27 @@ object PromiseTests extends MinimalScalaTest { "A successful Promise" should { val result = "test value" - val promise = Promise[String]().complete(Right(result)) + val promise = Promise[String]().complete(Success(result)) promise.isCompleted mustBe (true) futureWithResult(_(promise.future, result)) } "A failed Promise" should { val message = "Expected Exception" - val promise = Promise[String]().complete(Left(new RuntimeException(message))) + val promise = Promise[String]().complete(Failure(new RuntimeException(message))) promise.isCompleted mustBe (true) futureWithException[RuntimeException](_(promise.future, message)) } "An interrupted Promise" should { val message = "Boxed InterruptedException" - val future = Promise[String]().complete(Left(new InterruptedException(message))).future + val future = Promise[String]().complete(Failure(new InterruptedException(message))).future futureWithException[ExecutionException](_(future, message)) } "A NonLocalReturnControl failed Promise" should { val result = "test value" - val future = Promise[String]().complete(Left(new NonLocalReturnControl[String]("test", result))).future + val future = Promise[String]().complete(Failure(new NonLocalReturnControl[String]("test", result))).future futureWithResult(_(future, result)) } @@ -76,7 +76,7 @@ object PromiseTests extends MinimalScalaTest { "be completed" in { f((future, _) => future.isCompleted mustBe (true)) } - "contain a value" in { f((future, result) => future.value mustBe (Some(Right(result)))) } + "contain a value" in { f((future, result) => future.value mustBe (Some(Success(result)))) } "return when ready with 'Await.ready'" in { f((future, result) => Await.ready(future, defaultTimeout).isCompleted mustBe (true)) } @@ -159,7 +159,7 @@ object PromiseTests extends MinimalScalaTest { "contain a value" in { f((future, message) => { - future.value.get.left.get.getMessage mustBe (message) + future.value.get.failed.get.getMessage mustBe (message) }) } diff --git a/test/files/jvm/future-spec/TryTests.scala b/test/files/jvm/future-spec/TryTests.scala new file mode 100644 index 0000000000..9d749c44ba --- /dev/null +++ b/test/files/jvm/future-spec/TryTests.scala @@ -0,0 +1,119 @@ + +// This is a port of the com.twitter.util Try spec. +// -- +// It lives in the future-spec directory simply because it requires a specs-like +// DSL which has already been minimally implemented for the future spec tests. + +import scala.util.{Try,Success,Failure} + +object TryTests extends MinimalScalaTest { + class MyException extends Exception + val e = new Exception("this is an exception") + + "Try()" should { + "catch exceptions and lift into the Try type" in { + Try[Int](1) mustEqual Success(1) + Try[Int] { throw e } mustEqual Failure(e) + } + } + + "Try" should { + "recoverWith" in { + val myException = new MyException + Success(1) recoverWith { case _ => Success(2) } mustEqual Success(1) + Failure(e) recoverWith { case _ => Success(2) } mustEqual Success(2) + Failure(e) recoverWith { case _ => Failure(e) } mustEqual Failure(e) + } + + "getOrElse" in { + Success(1) getOrElse 2 mustEqual 1 + Failure(e) getOrElse 2 mustEqual 2 + } + + "orElse" in { + Success(1) orElse Success(2) mustEqual Success(1) + Failure(e) orElse Success(2) mustEqual Success(2) + } + + "map" in { + "when there is no exception" in { + Success(1) map(1+) mustEqual Success(2) + Failure[Int](e) map(1+) mustEqual Failure(e) + } + + "when there is an exception" in { + Success(1) map(_ => throw e) mustEqual Failure(e) + + val e2 = new Exception + Failure[Int](e) map(_ => throw e2) mustEqual Failure(e) + } + } + + "flatMap" in { + "when there is no exception" in { + Success(1) flatMap(x => Success(1 + x)) mustEqual Success(2) + Failure[Int](e) flatMap(x => Success(1 + x)) mustEqual Failure(e) + } + + "when there is an exception" in { + Success(1).flatMap[Int](_ => throw e) mustEqual Failure(e) + + val e2 = new Exception + Failure[Int](e).flatMap[Int](_ => throw e2) mustEqual Failure(e) + } + } + + "flatten" in { + "is a Success(Success)" in { + Success(Success(1)).flatten mustEqual Success(1) + } + + "is a Success(Failure)" in { + val e = new Exception + Success(Failure(e)).flatten mustEqual Failure(e) + } + + "is a Throw" in { + val e = new Exception + Failure[Try[Int]](e).flatten mustEqual Failure(e) + } + } + + "for" in { + "with no Failure values" in { + val result = for { + i <- Success(1) + j <- Success(1) + } yield (i + j) + result mustEqual Success(2) + } + + "with Failure values" in { + "throws before" in { + val result = for { + i <- Failure[Int](e) + j <- Success(1) + } yield (i + j) + result mustEqual Failure(e) + } + + "throws after" in { + val result = for { + i <- Success(1) + j <- Failure[Int](e) + } yield (i + j) + result mustEqual Failure(e) + } + + "returns the FIRST Failure" in { + val e2 = new Exception + val result = for { + i <- Failure[Int](e) + j <- Failure[Int](e2) + } yield (i + j) + result mustEqual Failure(e) + } + } + } + } +} \ No newline at end of file diff --git a/test/files/jvm/future-spec/main.scala b/test/files/jvm/future-spec/main.scala index 45b59d5d20..57183d8cea 100644 --- a/test/files/jvm/future-spec/main.scala +++ b/test/files/jvm/future-spec/main.scala @@ -12,6 +12,7 @@ object Test { def main(args: Array[String]) { FutureTests.check() PromiseTests.check() + TryTests.check() } } @@ -47,7 +48,7 @@ trait MinimalScalaTest extends Output { snippet bufferPrintln("[OK] Test passed.") } catch { - case e => + case e: Throwable => bufferPrintln("[FAILED] " + e) bufferPrintln(e.getStackTrace().mkString("\n")) throwables += e @@ -59,6 +60,7 @@ trait MinimalScalaTest extends Output { implicit def objectops(obj: Any) = new { def mustBe(other: Any) = assert(obj == other, obj + " is not " + other) + def mustEqual(other: Any) = mustBe(other) } diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index 43d4c9dc71..36ab910593 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -601,10 +601,10 @@ trait FutureProjections extends TestBase { throw cause } f.failed onComplete { - case Right(t) => + case Success(t) => assert(t == cause) done() - case Left(t) => + case Failure(t) => assert(false) } } @@ -626,9 +626,9 @@ trait FutureProjections extends TestBase { done => val f = future { 0 } f.failed onComplete { - case Right(t) => + case Success(t) => assert(false) - case Left(t) => + case Failure(t) => assert(t.isInstanceOf[NoSuchElementException]) done() } @@ -803,81 +803,6 @@ trait Exceptions extends TestBase { } -// trait TryEitherExtractor extends TestBase { - -// import scala.util.{Try, Success, Failure} - -// def testSuccessMatch(): Unit = once { -// done => -// val thisIsASuccess = Success(42) -// thisIsASuccess match { -// case Success(v) => -// done() -// assert(v == 42) -// case Failure(e) => -// done() -// assert(false) -// case other => -// done() -// assert(false) -// } -// } - -// def testRightMatch(): Unit = once { -// done => -// val thisIsNotASuccess: Right[Throwable, Int] = Right(43) -// thisIsNotASuccess match { -// case Success(v) => -// done() -// assert(v == 43) -// case Failure(e) => -// done() -// assert(false) -// case other => -// done() -// assert(false) -// } -// } - -// def testFailureMatch(): Unit = once { -// done => -// val thisIsAFailure = Failure(new Exception("I'm an exception")) -// thisIsAFailure match { -// case Success(v) => -// done() -// assert(false) -// case Failure(e) => -// done() -// assert(e.getMessage == "I'm an exception") -// case other => -// done() -// assert(false) -// } -// } - -// def testLeftMatch(): Unit = once { -// done => -// val thisIsNotAFailure: Left[Throwable, Int] = Left(new Exception("I'm an exception")) -// thisIsNotAFailure match { -// case Success(v) => -// done() -// assert(false) -// case Failure(e) => -// done() -// assert(e.getMessage == "I'm an exception") -// case other => -// done() -// assert(false) -// } - -// } - -// testSuccessMatch() -// testRightMatch() -// testFailureMatch() -// testLeftMatch() -// } - trait CustomExecutionContext extends TestBase { import scala.concurrent.{ ExecutionContext, Awaitable } @@ -975,13 +900,13 @@ trait CustomExecutionContext extends TestBase { } flatMap { x => Promise.successful(x + 1).future.map(addOne).map(addOne) } onComplete { - case Left(t) => + case Failure(t) => try { throw new AssertionError("error in test: " + t.getMessage, t) } finally { done() } - case Right(x) => + case Success(x) => assertEC() assert(x == 14) done() @@ -1009,12 +934,8 @@ with FutureProjections with Promises with BlockContexts with Exceptions -// with TryEitherExtractor with CustomExecutionContext { System.exit(0) } - - - diff --git a/test/files/jvm/try-type-tests.scala b/test/files/jvm/try-type-tests.scala index eecbb0ae57..9dece8d6d8 100644 --- a/test/files/jvm/try-type-tests.scala +++ b/test/files/jvm/try-type-tests.scala @@ -59,12 +59,12 @@ trait TryStandard { def testRescueSuccess(): Unit = { val t = Success(1) - t.rescue{ case x => assert(false); Try() } + t.recoverWith{ case x => assert(false); Try() } } def testRescueFailure(): Unit = { val t = Failure(new Exception("foo")) - val n = t.rescue{ case x => Try(1) } + val n = t.recoverWith{ case x => Try(1) } assert(n.get == 1) } @@ -121,130 +121,8 @@ trait TryStandard { testFailedFailure() } -// tests that implicit conversions from Try to Either behave as expected -trait TryImplicitConversionTry2Either { - - def testTry2RightMap(): Unit = { - val t = Success(1) - val n = t.right.map(x => x * 100) - assert(n == Right(100)) - } - - def testTry2LeftMap(): Unit = { - val e = new Exception("foo") - val t = Failure(e) - val n = t.left.map(x => x) - assert(n == Left(e)) - } - - def testTry2FoldSuccess(): Unit = { - val t = Success(1) - val n = t.fold(x => assert(false), y => y * 200) - assert(n == 200) - } - - def testTry2FoldFailure(): Unit = { - val e = new Exception("foo") - val t = Failure(e) - val n = t.fold(x => x, y => assert(false)) - assert(n == e) - } - - def testTry2SwapSuccess(): Unit = { - val t = Success(1) - val n = t.swap - assert(n == Left(1)) - } - - def testTry2SwapFailure(): Unit = { - val e = new Exception("foo") - val t = Failure(e) - val n = t.swap - assert(n == Right(e)) - } - - // def testTry2MergeSucccess(): Unit = { - // val t: Try[Int] = Success(1) - // val n = (t: Either[Any, Any]).t.merge // connecting two implicit conversions - // assert(n == 1) - // } - - // def testTry2MergeFailure(): Unit = { - // val e = new Exception("foo") - // val t = Failure(e) - // val n = (t: Either[Any, Any]).merge // connecting two implicit conversions - // assert(n == e) - // } - - testTry2RightMap() - testTry2LeftMap() - testTry2FoldSuccess() - testTry2FoldFailure() - testTry2SwapSuccess() - testTry2SwapFailure() - // testTry2MergeSucccess() - // testTry2MergeFailure() -} - -// tests that implicit conversions from Either to Try behave as expected -trait TryImplicitConversionEither2Try { - - def testRight2FilterSuccessTrue(): Unit = { - def expectsTry[U <% Try[Int]](rght: U): Try[Int] = { - val n = rght.filter(x => x > 0) // this should be converted to a Try - n - } - val r = Right(1) - val n = expectsTry(r) - assert(n == Success(1)) - } - - def testRight2FilterSuccessFalse(): Unit = { - def expectsTry[U <% Try[Int]](rght: U): Try[Int] = { - val n = rght.filter(x => x < 0) // this should be converted to a Try - n - } - val r = Right(1) - val n = expectsTry(r) - n match { - case Failure(e: NoSuchElementException) => assert(true) - case _ => assert(false) - } - } - - def testLeft2FilterFailure(): Unit = { - def expectsTry[U <% Try[Int]](rght: U): Try[Int] = { - val n = rght.filter(x => x > 0) // this should be converted to a Try - n - } - val r = Left(new Exception("foo")) - val n = expectsTry(r) - n match { - case Failure(e: Exception) => assert(true) - case _ => assert(false) - } - } - - def testRight2GetSuccess(): Unit = { - def expectsTry[U <% Try[Int]](rght: U): Int = { - val n = rght.get // this should be converted to a Try - n - } - val r = Right(1) - val n = expectsTry(r) - assert(n == 1) - } - - testRight2FilterSuccessTrue() - testRight2FilterSuccessFalse() - testLeft2FilterFailure() - testRight2GetSuccess() -} - object Test extends App -with TryStandard -with TryImplicitConversionTry2Either -with TryImplicitConversionEither2Try { +with TryStandard { System.exit(0) } \ No newline at end of file -- cgit v1.2.3 From 5b82a9702de3ffd5b131caf8c550877b476e8f9c Mon Sep 17 00:00:00 2001 From: phaller Date: Sun, 5 Aug 2012 00:20:01 +0200 Subject: SI-6185 - add "prepare" hook to ExecutionContext Enables important abstractions to be built on top of futures, such as Twitter's "Local" for handling data local to a callback chain. --- .../scala/concurrent/ExecutionContext.scala | 8 +++ src/library/scala/concurrent/impl/Promise.scala | 12 +++-- test/files/jvm/scala-concurrent-tck.scala | 61 ++++++++++++++++++++++ 3 files changed, 78 insertions(+), 3 deletions(-) (limited to 'test/files') diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 8081bb32da..ac462ac9d2 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -12,6 +12,7 @@ package scala.concurrent import java.util.concurrent.{ ExecutorService, Executor } import scala.concurrent.util.Duration import scala.annotation.implicitNotFound +import scala.util.Try /** * An `ExecutionContext` is an abstraction over an entity that can execute program logic. @@ -27,6 +28,13 @@ trait ExecutionContext { */ def reportFailure(t: Throwable): Unit + /** Prepares for the execution of callback `f`. Returns the prepared + * execution context which should be used to schedule the execution + * of the task associated with `f`. + */ + def prepare[T, U](f: Try[T] => U): ExecutionContext = + this + } /** diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala index fab6b55c52..9f30529dc8 100644 --- a/src/library/scala/concurrent/impl/Promise.scala +++ b/src/library/scala/concurrent/impl/Promise.scala @@ -22,7 +22,9 @@ private[concurrent] trait Promise[T] extends scala.concurrent.Promise[T] with sc def future: this.type = this } -private class CallbackRunnable[T](val executor: ExecutionContext, val onComplete: (Try[T]) => Any) extends Runnable with OnCompleteRunnable { +/* Precondition: `executor` is prepared, i.e., `executor` has been returned from invocation of `prepare` on some other `ExecutionContext`. + */ +private class CallbackRunnable[T](val executor: ExecutionContext, val onComplete: Try[T] => Any) extends Runnable with OnCompleteRunnable { // must be filled in before running it var value: Try[T] = null @@ -34,6 +36,8 @@ private class CallbackRunnable[T](val executor: ExecutionContext, val onComplete def executeWithValue(v: Try[T]): Unit = { require(value eq null) // can't complete it twice value = v + // Note that we cannot prepare the ExecutionContext at this point, since we might + // already be running on a different thread! executor.execute(this) } } @@ -125,7 +129,8 @@ private[concurrent] object Promise { } def onComplete[U](func: Try[T] => U)(implicit executor: ExecutionContext): Unit = { - val runnable = new CallbackRunnable[T](executor, func) + val preparedEC = executor.prepare(func) + val runnable = new CallbackRunnable[T](preparedEC, func) @tailrec //Tries to add the callback, if already completed, it dispatches the callback to be executed def dispatchOrAddCallback(): Unit = @@ -151,7 +156,8 @@ private[concurrent] object Promise { def onComplete[U](func: Try[T] => U)(implicit executor: ExecutionContext): Unit = { val completedAs = value.get - (new CallbackRunnable(executor, func)).executeWithValue(completedAs) + val preparedEC = executor.prepare(func) + (new CallbackRunnable(preparedEC, func)).executeWithValue(completedAs) } def ready(atMost: Duration)(implicit permit: CanAwait): this.type = this diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index 36ab910593..976d98a337 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -926,6 +926,66 @@ trait CustomExecutionContext extends TestBase { testCallbackChainCustomEC() } +trait ExecutionContextPrepare extends TestBase { + val theLocal = new ThreadLocal[String] { + override protected def initialValue(): String = "" + } + + class PreparingExecutionContext extends ExecutionContext { + def delegate = ExecutionContext.global + + override def execute(runnable: Runnable): Unit = + delegate.execute(runnable) + + override def prepare[T, U](f: Try[T] => U): ExecutionContext = { + // save object stored in ThreadLocal storage + val localData = theLocal.get + new PreparingExecutionContext { + override def execute(runnable: Runnable): Unit = { + val wrapper = new Runnable { + override def run(): Unit = { + // now we're on the new thread + // put localData into theLocal + theLocal.set(localData) + runnable.run() + } + } + delegate.execute(wrapper) + } + } + } + + override def reportFailure(t: Throwable): Unit = + delegate.reportFailure(t) + } + + implicit val ec = new PreparingExecutionContext + + def testOnComplete(): Unit = once { + done => + theLocal.set("secret") + val fut = future { 42 } + fut onComplete { + case _ => + assert(theLocal.get == "secret") + done() + } + } + + def testMap(): Unit = once { + done => + theLocal.set("secret2") + val fut = future { 42 } + fut map { x => + assert(theLocal.get == "secret2") + done() + } + } + + testOnComplete() + testMap() +} + object Test extends App with FutureCallbacks @@ -935,6 +995,7 @@ with Promises with BlockContexts with Exceptions with CustomExecutionContext +with ExecutionContextPrepare { System.exit(0) } -- cgit v1.2.3 From cb6066ee6136d78ceb8c3b5c06cefac998966dd0 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Sun, 5 Aug 2012 12:38:13 +0200 Subject: Temporarily skips failing test due to optimizer bug SI-6188 Also swaps the arguments to method transform on Try, so as to mirror transform on scala.concurrent.Future. --- src/library/scala/util/Try.scala | 2 +- test/files/jvm/future-spec/TryTests.scala | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'test/files') diff --git a/src/library/scala/util/Try.scala b/src/library/scala/util/Try.scala index 487ddaced3..c834f6d514 100644 --- a/src/library/scala/util/Try.scala +++ b/src/library/scala/util/Try.scala @@ -142,7 +142,7 @@ sealed abstract class Try[+T] { /** Completes this `Try` by applying the function `f` to this if this is of type `Failure`, or conversely, by applying * `s` if this is a `Success`. */ - def transform[U](f: Throwable => Try[U], s: T => Try[U]): Try[U] = this match { + def transform[U](s: T => Try[U], f: Throwable => Try[U]): Try[U] = this match { case Success(v) => s(v) case Failure(e) => f(e) } diff --git a/test/files/jvm/future-spec/TryTests.scala b/test/files/jvm/future-spec/TryTests.scala index 9d749c44ba..47ce9c2e15 100644 --- a/test/files/jvm/future-spec/TryTests.scala +++ b/test/files/jvm/future-spec/TryTests.scala @@ -55,12 +55,12 @@ object TryTests extends MinimalScalaTest { Failure[Int](e) flatMap(x => Success(1 + x)) mustEqual Failure(e) } - "when there is an exception" in { - Success(1).flatMap[Int](_ => throw e) mustEqual Failure(e) + // "when there is an exception" in { + // Success(1).flatMap[Int](_ => throw e) mustEqual Failure(e) - val e2 = new Exception - Failure[Int](e).flatMap[Int](_ => throw e2) mustEqual Failure(e) - } + // val e2 = new Exception + // Failure[Int](e).flatMap[Int](_ => throw e2) mustEqual Failure(e) + // } } "flatten" in { -- cgit v1.2.3 From ea81ab3314359c98986e6fac74c807fa1accdfb6 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Tue, 7 Aug 2012 18:36:36 +0200 Subject: Added tests, removal of unnecessary methods, fixes prepare --- src/library/scala/concurrent/ExecutionContext.scala | 8 +++----- src/library/scala/concurrent/impl/Promise.scala | 9 +++------ src/library/scala/util/Try.scala | 13 +------------ test/files/jvm/future-spec/TryTests.scala | 12 ++++++------ test/files/jvm/try-type-tests.scala | 14 ++++++++++++++ 5 files changed, 27 insertions(+), 29 deletions(-) (limited to 'test/files') diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index ac462ac9d2..82c1cff4b1 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -28,12 +28,10 @@ trait ExecutionContext { */ def reportFailure(t: Throwable): Unit - /** Prepares for the execution of callback `f`. Returns the prepared - * execution context which should be used to schedule the execution - * of the task associated with `f`. + /** Prepares for the execution of a task. Returns the prepared + * execution context. */ - def prepare[T, U](f: Try[T] => U): ExecutionContext = - this + def prepare(): ExecutionContext = this } diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala index 9f30529dc8..b19bed004b 100644 --- a/src/library/scala/concurrent/impl/Promise.scala +++ b/src/library/scala/concurrent/impl/Promise.scala @@ -129,7 +129,7 @@ private[concurrent] object Promise { } def onComplete[U](func: Try[T] => U)(implicit executor: ExecutionContext): Unit = { - val preparedEC = executor.prepare(func) + val preparedEC = executor.prepare val runnable = new CallbackRunnable[T](preparedEC, func) @tailrec //Tries to add the callback, if already completed, it dispatches the callback to be executed @@ -156,16 +156,13 @@ private[concurrent] object Promise { def onComplete[U](func: Try[T] => U)(implicit executor: ExecutionContext): Unit = { val completedAs = value.get - val preparedEC = executor.prepare(func) + val preparedEC = executor.prepare (new CallbackRunnable(preparedEC, func)).executeWithValue(completedAs) } def ready(atMost: Duration)(implicit permit: CanAwait): this.type = this - def result(atMost: Duration)(implicit permit: CanAwait): T = value.get match { - case Failure(e) => throw e - case Success(r) => r - } + def result(atMost: Duration)(implicit permit: CanAwait): T = value.get.get } } diff --git a/src/library/scala/util/Try.scala b/src/library/scala/util/Try.scala index c834f6d514..f381a18b0c 100644 --- a/src/library/scala/util/Try.scala +++ b/src/library/scala/util/Try.scala @@ -54,7 +54,7 @@ import language.implicitConversions * * `Try` comes to the Scala standard library after years of use as an integral part of Twitter's stack. * - * @author based on Marius Eriksen's original implementation in com.twitter.util. + * @author based on Twitter's original implementation in com.twitter.util. * @since 2.10 */ sealed abstract class Try[+T] { @@ -116,17 +116,6 @@ sealed abstract class Try[+T] { */ def toOption = if (isSuccess) Some(get) else None - /** - * Returns an empty `Seq` (usually a `List`) if this is a `Failure` or a `Seq` containing the value if this is a `Success`. - */ - def toSeq = if (isSuccess) Seq(get) else Seq() - - /** - * Returns the given function applied to the value from this `Success` or returns this if this is a `Failure`. - * Alias for `flatMap`. - */ - def andThen[U](f: T => Try[U]): Try[U] = flatMap(f) - /** * Transforms a nested `Try`, ie, a `Try` of type `Try[Try[T]]`, * into an un-nested `Try`, ie, a `Try` of type `Try[T]`. diff --git a/test/files/jvm/future-spec/TryTests.scala b/test/files/jvm/future-spec/TryTests.scala index 47ce9c2e15..db0be0dfff 100644 --- a/test/files/jvm/future-spec/TryTests.scala +++ b/test/files/jvm/future-spec/TryTests.scala @@ -1,4 +1,4 @@ - +x // This is a port of the com.twitter.util Try spec. // -- // It lives in the future-spec directory simply because it requires a specs-like @@ -55,12 +55,12 @@ object TryTests extends MinimalScalaTest { Failure[Int](e) flatMap(x => Success(1 + x)) mustEqual Failure(e) } - // "when there is an exception" in { - // Success(1).flatMap[Int](_ => throw e) mustEqual Failure(e) + "when there is an exception" in { + Success(1).flatMap[Int](_ => throw e) mustEqual Failure(e) - // val e2 = new Exception - // Failure[Int](e).flatMap[Int](_ => throw e2) mustEqual Failure(e) - // } + val e2 = new Exception + Failure[Int](e).flatMap[Int](_ => throw e2) mustEqual Failure(e) + } } "flatten" in { diff --git a/test/files/jvm/try-type-tests.scala b/test/files/jvm/try-type-tests.scala index 9dece8d6d8..351f02b183 100644 --- a/test/files/jvm/try-type-tests.scala +++ b/test/files/jvm/try-type-tests.scala @@ -103,6 +103,20 @@ trait TryStandard { } } + def testSuccessTransform(): Unit = { + val s = Success(1) + val succ = (x: Int) => Success(x * 10) + val fail = (x: Throwable) => Success(0) + assert(s.transform(succ, fail).get == s.get) + } + + def testFailureTransform(): Unit = { + val f = Failure(new Exception("foo")) + val succ = (x: Int) => Success(x * 10) + val fail = (x: Throwable) => Success(0) + assert(f.transform(succ, fail).get == 0) + } + testForeachSuccess() testForeachFailure() testFlatMapSuccess() -- cgit v1.2.3 From 540706a95d15b43dd94b5258477d36468e76474b Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Wed, 8 Aug 2012 08:03:17 +0200 Subject: Doc fix on exec ctx prepare method, fix to tests --- src/library/scala/concurrent/ExecutionContext.scala | 3 ++- src/library/scala/concurrent/Future.scala | 2 +- test/files/jvm/future-spec/TryTests.scala | 1 - test/files/jvm/try-type-tests.scala | 4 +++- 4 files changed, 6 insertions(+), 4 deletions(-) (limited to 'test/files') diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 82c1cff4b1..1be6050303 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -29,7 +29,8 @@ trait ExecutionContext { def reportFailure(t: Throwable): Unit /** Prepares for the execution of a task. Returns the prepared - * execution context. + * execution context. A valid implementation of `prepare` is one + * that simply returns `this`. */ def prepare(): ExecutionContext = this diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 09e29d971d..bc0b437a33 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -312,7 +312,7 @@ trait Future[+T] extends Awaitable[T] { case Success(v) => try { if (pred(v)) p success v - else p failure new NoSuchElementException("Future.filter predicate is not satisfied by: " + v) + else p failure new NoSuchElementException("Future.filter predicate is not satisfied") } catch { case NonFatal(t) => p failure t } diff --git a/test/files/jvm/future-spec/TryTests.scala b/test/files/jvm/future-spec/TryTests.scala index db0be0dfff..82ca12276f 100644 --- a/test/files/jvm/future-spec/TryTests.scala +++ b/test/files/jvm/future-spec/TryTests.scala @@ -1,4 +1,3 @@ -x // This is a port of the com.twitter.util Try spec. // -- // It lives in the future-spec directory simply because it requires a specs-like diff --git a/test/files/jvm/try-type-tests.scala b/test/files/jvm/try-type-tests.scala index 351f02b183..17811f64c5 100644 --- a/test/files/jvm/try-type-tests.scala +++ b/test/files/jvm/try-type-tests.scala @@ -107,7 +107,7 @@ trait TryStandard { val s = Success(1) val succ = (x: Int) => Success(x * 10) val fail = (x: Throwable) => Success(0) - assert(s.transform(succ, fail).get == s.get) + assert(s.transform(succ, fail).get == 10) } def testFailureTransform(): Unit = { @@ -133,6 +133,8 @@ trait TryStandard { testFlattenSuccess() testFailedSuccess() testFailedFailure() + testSuccessTransform() + testFailureTransform() } object Test -- cgit v1.2.3 From fd13a937b2470d002037562528510cf58f275481 Mon Sep 17 00:00:00 2001 From: phaller Date: Wed, 8 Aug 2012 09:35:34 +0200 Subject: Fix test for ExecutionContext.prepare --- test/files/jvm/scala-concurrent-tck.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test/files') diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index 976d98a337..ffb5608fd2 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -937,7 +937,7 @@ trait ExecutionContextPrepare extends TestBase { override def execute(runnable: Runnable): Unit = delegate.execute(runnable) - override def prepare[T, U](f: Try[T] => U): ExecutionContext = { + override def prepare(): ExecutionContext = { // save object stored in ThreadLocal storage val localData = theLocal.get new PreparingExecutionContext { -- cgit v1.2.3