diff --git a/effect3/src/main/scala/io/catbird/util/effect/RerunnableInstances.scala b/effect3/src/main/scala/io/catbird/util/effect/RerunnableInstances.scala index df6ff12e..ed7d9195 100644 --- a/effect3/src/main/scala/io/catbird/util/effect/RerunnableInstances.scala +++ b/effect3/src/main/scala/io/catbird/util/effect/RerunnableInstances.scala @@ -2,12 +2,11 @@ package io.catbird.util.effect import cats.effect.Clock import cats.effect.kernel.{ MonadCancel, Outcome, Sync } -import com.twitter.util.{ Future, Monitor } +import com.twitter.util.{ Future, Monitor, Stopwatch, Time } import io.catbird.util.{ Rerunnable, RerunnableMonadError } import java.lang.Throwable import java.util.concurrent.TimeUnit -import java.lang.System import scala.Unit import scala.concurrent.duration.FiniteDuration @@ -24,10 +23,10 @@ trait RerunnableInstances { Rerunnable(thunk) final override def realTime: Rerunnable[FiniteDuration] = - Rerunnable(FiniteDuration(System.currentTimeMillis(), TimeUnit.MILLISECONDS)) + Rerunnable(FiniteDuration(Time.nowNanoPrecision.inNanoseconds, TimeUnit.NANOSECONDS)) final override def monotonic: Rerunnable[FiniteDuration] = - Rerunnable(FiniteDuration(System.nanoTime(), TimeUnit.NANOSECONDS)) + Rerunnable(FiniteDuration(Stopwatch.timeNanos(), TimeUnit.NANOSECONDS)) final override def forceR[A, B](fa: Rerunnable[A])(fb: Rerunnable[B]): Rerunnable[B] = fa.liftToTry.flatMap { resultA => diff --git a/effect3/src/test/scala/io/catbird/util/effect/RerunnableClockSuite.scala b/effect3/src/test/scala/io/catbird/util/effect/RerunnableClockSuite.scala deleted file mode 100644 index c1011ac0..00000000 --- a/effect3/src/test/scala/io/catbird/util/effect/RerunnableClockSuite.scala +++ /dev/null @@ -1,48 +0,0 @@ -package io.catbird.util.effect - -import java.time.Instant -import java.util.concurrent.TimeUnit - -import cats.effect.Clock -import com.twitter.util.Await -import io.catbird.util.Rerunnable -import org.scalatest.{ Outcome } -import org.scalatest.concurrent.{ Eventually, IntegrationPatience } -import org.scalatest.funsuite.FixtureAnyFunSuite - -/** - * We'll use `eventually` and a reasonably big tolerance here to prevent CI from failing if it is a bit slow. - * - * Technically the implementation is just an extremely thin wrapper around `System.currentTimeMillis()` - * and `System.nanoTime()` so as long as the result is the same order of magnitude (and therefore the - * unit-conversion is correct) we should be fine. - */ -class RerunnableClockSuite extends FixtureAnyFunSuite with Eventually with IntegrationPatience { - - protected final class FixtureParam { - def now: Instant = Instant.now() - } - - test("Retrieval of real time") { f => - eventually { - val result = Await.result( - Clock[Rerunnable].realTime.map(duration => Instant.ofEpochMilli(duration.toMillis)).run - ) - - assert(java.time.Duration.between(result, f.now).abs().toMillis < 50) - } - } - - test("Retrieval of monotonic time") { f => - eventually { - val result = Await.result( - Clock[Rerunnable].monotonic.map(duration => duration.toNanos).run - ) - - val durationBetween = Math.abs(System.nanoTime() - result) - assert(TimeUnit.MILLISECONDS.convert(durationBetween, TimeUnit.NANOSECONDS) < 5) - } - } - - override protected def withFixture(test: OneArgTest): Outcome = withFixture(test.toNoArgTest(new FixtureParam)) -} diff --git a/effect3/src/test/scala/io/catbird/util/effect/RerunnableSuite.scala b/effect3/src/test/scala/io/catbird/util/effect/RerunnableSuite.scala index cf9ae80c..6cf31ecf 100644 --- a/effect3/src/test/scala/io/catbird/util/effect/RerunnableSuite.scala +++ b/effect3/src/test/scala/io/catbird/util/effect/RerunnableSuite.scala @@ -1,6 +1,6 @@ package io.catbird.util.effect -import cats.effect.MonadCancel +import cats.effect.kernel.{ Clock, MonadCancel, Outcome } import cats.effect.kernel.testkit.SyncTypeGenerators import cats.effect.laws.SyncTests import cats.instances.either._ @@ -8,7 +8,7 @@ import cats.instances.int._ import cats.instances.tuple._ import cats.instances.unit._ import cats.laws.discipline.arbitrary._ -import com.twitter.util.{ Await, Monitor, Throw } +import com.twitter.util.{ Await, Monitor, Throw, Time } import io.catbird.util.Rerunnable class RerunnableSuite @@ -19,6 +19,22 @@ class RerunnableSuite // This includes tests for Clock, MonadCancel, and MonadError checkAll("Rerunnable[Int]", SyncTests[Rerunnable].sync[Int, Int, Int]) + test("Retrieval of real time") { + val nanos = 123456789L + val result = Time.withTimeAt(Time.fromNanoseconds(nanos)) { _ => + unsafeRun(Clock[Rerunnable].realTime.map(_.toNanos)) + } + assert(result == Outcome.succeeded(Some(nanos))) + } + + test("Retrieval of monotonic time") { + val nanos = 123456789L + val result = Time.withTimeAt(Time.fromNanoseconds(nanos)) { _ => + unsafeRun(Clock[Rerunnable].monotonic.map(_.toNanos)) + } + assert(result == Outcome.succeeded(Some(nanos))) + } + test("Exceptions thrown by release are handled by Monitor") { val useException = new Exception("thrown by use") val releaseException = new Exception("thrown by release")