Ticker does not work with free Futures
veysiertekin opened this issue · 1 comments
veysiertekin commented
Hi,
I am trying to implement a fakeTicker for testing purposes but somehow ticker works with constant Futures (like Future.successful) but does not work with ongoing Futures that use ExecutorContext.
Ticker: FakeTicker.scala
import com.github.benmanes.caffeine.cache.Ticker
import java.util.concurrent.atomic.AtomicLong
import scala.concurrent.duration.Duration
class FakeTicker extends Ticker {
private val nanos = new AtomicLong()
private val autoIncrementStepNanos = new AtomicLong()
override def read(): Long =
nanos.getAndAdd(autoIncrementStepNanos.get())
def advance(duration: Duration): FakeTicker = {
advance(duration.toNanos)
this
}
def advance(nanoseconds: Long): FakeTicker = {
nanos.addAndGet(nanoseconds)
this
}
def setAutoIncrement(duration: Duration): Unit = {
this.autoIncrementStepNanos.set(duration.toNanos)
}
}
Working example:
import com.github.blemale.scaffeine.{ AsyncLoadingCache, Scaffeine }
import org.scalatest.freespec.AsyncFreeSpec
import scala.concurrent.Future
import scala.concurrent.duration._
import scala.util.Random
class CacheSpec extends AsyncFreeSpec {
val fakeTicker = new FakeTicker
case class Expired(ttl: Long, data: String)
val cache: AsyncLoadingCache[String, Expired] = Scaffeine()
.executor(scala.concurrent.ExecutionContext.global)
.ticker(fakeTicker)
.refreshAfterWrite(1.hour)
.expireAfter(
create = (_: String, response: Expired) => response.ttl.hours,
update = (_: String, response: Expired, _: FiniteDuration) => response.ttl.hours,
read = (_: String, _: Expired, duration: FiniteDuration) => duration
)
.buildAsyncFuture[String, Expired](load(_))
def load(s: String): Future[Expired] = {
Future.successful(Expired(4, Random.nextString(10)))
}
"get" - {
"should pass" in {
for {
first <- cache.get("test")
_ = fakeTicker.advance(5.hours)
second <- cache.get("test")
} yield {
assert(first != second)
succeed
}
}
}
}
Does not work on free Futures:
import com.github.blemale.scaffeine.{ AsyncLoadingCache, Scaffeine }
import org.scalatest.freespec.AsyncFreeSpec
import scala.concurrent.Future
import scala.concurrent.duration._
import scala.util.Random
class CacheSpec extends AsyncFreeSpec {
val fakeTicker = new FakeTicker
case class Expired(ttl: Long, data: String)
val cache: AsyncLoadingCache[String, Expired] = Scaffeine()
.executor(scala.concurrent.ExecutionContext.global)
.ticker(fakeTicker)
.refreshAfterWrite(1.hour)
.expireAfter(
create = (_: String, response: Expired) => response.ttl.hours,
update = (_: String, response: Expired, _: FiniteDuration) => response.ttl.hours,
read = (_: String, _: Expired, duration: FiniteDuration) => duration
)
.buildAsyncFuture[String, Expired](load(_))
def load(s: String): Future[Expired] = {
// Changed from `Future.successful` to `Future`
Future(Expired(4, Random.nextString(10)))
}
"get" - {
"should pass" in {
for {
first <- cache.get("test")
_ = fakeTicker.advance(5.hours)
second <- cache.get("test")
} yield {
assert(first != second)
succeed
}
}
}
}
veysiertekin commented
Sorry, I think I was wrong.
I had to set Test Suite's executor context to the same. My bad.
Working example:
import com.github.blemale.scaffeine.{ AsyncLoadingCache, Scaffeine }
import org.scalatest.freespec.AsyncFreeSpec
import scala.concurrent.Future
import scala.concurrent.duration._
import scala.util.Random
class CacheSpec extends AsyncFreeSpec {
val fakeTicker = new FakeTicker
implicit override val executionContext: ExecutionContextExecutor = scala.concurrent.ExecutionContext.global
case class Expired(ttl: Long, data: String)
val cache: AsyncLoadingCache[String, Expired] = Scaffeine()
.executor(executionContext)
.ticker(fakeTicker)
.refreshAfterWrite(1.hour)
.expireAfter(
create = (_: String, response: Expired) => response.ttl.hours,
update = (_: String, response: Expired, _: FiniteDuration) => response.ttl.hours,
read = (_: String, _: Expired, duration: FiniteDuration) => duration
)
.buildAsyncFuture[String, Expired](load(_))
def load(s: String): Future[Expired] = {
Future(Expired(4, Random.nextString(10)))
}
"get" - {
"should pass" in {
for {
first <- cache.get("test")
_ = fakeTicker.advance(5.hours)
second <- cache.get("test")
} yield {
assert(first != second)
succeed
}
}
}
}