Skip to content

Commit

Permalink
added await on Future[T : AsResult] to return a result. fixes #241
Browse files Browse the repository at this point in the history
  • Loading branch information
etorreborre committed Mar 12, 2014
1 parent fcb1f46 commit 7631010
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 12 deletions.
15 changes: 13 additions & 2 deletions guide/src/test/scala/org/specs2/guide/Matchers.scala
Expand Up @@ -141,13 +141,24 @@ iterator.next must be_==(3).eventually

* using `await` to create a matcher that will match on `Matcher[Future[T]]`: ${snippet{
// 8<--
import time.NoTimeConversions._
import scala.concurrent._
import duration._
import ExecutionContext.Implicits.global
// 8<--
future(1) must be_>(0).await
future { Thread.sleep(100); 1 } must be_>(0).await(retries = 2, timeout = 100.millis)
}}

* using `await` to create a `Result` on a `Future` that returns a `Matcher[T]`: ${snippet{
// 8<--
import time.NoTimeConversions._
import scala.concurrent._
import duration._
import ExecutionContext.Implicits.global
// 8<--
future(1) must be_>(0).await
future { Thread.sleep(100); 1 } must be_>(0).await(retries = 2, timeout = 100.millis)
future(1 === 1).await
future(1 === 1).await(retries = 2, timeout = 100.millis)
}}

* using `when` or `unless` to apply a matcher only if a condition is satisfied: ${snippet{
Expand Down
31 changes: 21 additions & 10 deletions matcher/src/main/scala/org/specs2/matcher/FutureMatchers.scala
Expand Up @@ -5,6 +5,7 @@ import concurrent.duration._
import concurrent.{Await, Future}
import concurrent.ExecutionContext.Implicits.global
import java.util.concurrent.TimeoutException
import execute.{AsResult, Failure, Result}

/**
* This trait is for transforming matchers of values to matchers of Futures
Expand All @@ -19,21 +20,31 @@ trait FutureMatchers extends Expectations {
def await(retries: Int = 0, timeout: FiniteDuration = 1.seconds): Matcher[Future[T]] = awaitFor(m)(retries, timeout)
}

/**
* when a Future contains a result, it can be awaited to return this result
*/
implicit class futureAsResult[T : AsResult](f: Future[T]) {
def await: Result = await()
def await(retries: Int = 0, timeout: FiniteDuration = 1.seconds): Result = {
def awaitFor(retries: Int, totalDuration: FiniteDuration = 0.seconds): Result = {
try Await.result(f.map(value => AsResult(value)), timeout)
catch {
case e: TimeoutException => if (retries <= 0) Failure("Timeout after "+totalDuration) else awaitFor(retries - 1, totalDuration + timeout)
case other: Throwable => throw other
}
}
awaitFor(retries)
}
}

def await[T](m: Matcher[T])(retries: Int = 0, timeout: FiniteDuration = 1.seconds): Matcher[Future[T]] = awaitFor(m)(retries, timeout)

private def awaitFor[T](m: Matcher[T])(retries: Int = 0, timeout: FiniteDuration = 1.seconds, totalDuration: FiniteDuration = 0.seconds): Matcher[Future[T]] = new Matcher[Future[T]] {
private def awaitFor[T](m: Matcher[T])(retries: Int = 0, timeout: FiniteDuration = 1.seconds): Matcher[Future[T]] = new Matcher[Future[T]] {
def apply[S <: Future[T]](a: Expectable[S]) = {
try {
val r = m(createExpectable(Await.result(a.value, atMost = timeout)))
result(r.isSuccess, r.message, r.message, a)
}
catch {
case e: TimeoutException => if (retries <= 0) result(false, "Timeout ok", "Timeout after "+totalDuration, a) else awaitFor(m)(retries - 1, timeout, totalDuration + timeout)(a)
case other: Throwable => throw other
}
val r = a.value.map(v => createExpectable(v).applyMatcher(m).toResult).await(retries, timeout)
result(r.isSuccess, r.message, r.message, a)
}
}

}

object FutureMatchers extends FutureMatchers
Expand Up @@ -17,6 +17,8 @@ class FutureMatchersSpec extends Specification with Groups with NoTimeConversion
${ future { Thread.sleep(100); 1 } must be_>(0).await(retries = 2, timeout = 100.millis) }
${ (future { Thread.sleep(500); 1 } must be_>(0).await(retries = 2, timeout = 100.millis)) returns "Timeout after 200 milliseconds" }

A `Future` returning a `Matcher[T]` can be transformed into a `Result`
${ future(1 === 1).await }
"""

}

0 comments on commit 7631010

Please sign in to comment.