From 02db8912c3f551f85b09868a20b68019e1adb448 Mon Sep 17 00:00:00 2001
From: Ben Spencer <dangerous.ben@gmail.com>
Date: Tue, 1 Jun 2021 13:55:41 +0100
Subject: [PATCH] CE3: Use twitter-util Time and Stopwatch for Clock
 implementation.

Allows using the twitter-util testing facilities.
---
 .../util/effect/RerunnableInstances.scala     |  7 ++-
 .../util/effect/RerunnableClockSuite.scala    | 48 -------------------
 .../catbird/util/effect/RerunnableSuite.scala | 20 +++++++-
 3 files changed, 21 insertions(+), 54 deletions(-)
 delete mode 100644 effect3/src/test/scala/io/catbird/util/effect/RerunnableClockSuite.scala

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")