Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import java.util.concurrent.TimeUnit
import com.typesafe.tools.mima.core._
import com.github.sbt.git.SbtGit.GitKeys._
import org.openqa.selenium.chrome.ChromeOptions
import org.openqa.selenium.firefox.FirefoxOptions
import org.openqa.selenium.firefox.{FirefoxOptions, FirefoxProfile}
import org.scalajs.jsenv.nodejs.NodeJSEnv
import org.scalajs.jsenv.selenium.SeleniumJSEnv
import sbtcrossproject.CrossProject
Expand Down Expand Up @@ -224,7 +224,10 @@ ThisBuild / jsEnv := {
useJSEnv.value match {
case NodeJS => new NodeJSEnv(NodeJSEnv.Config().withSourceMap(true))
case Firefox =>
val profile = new FirefoxProfile()
profile.setPreference("privacy.reduceTimerPrecision", false)
val options = new FirefoxOptions()
options.setProfile(profile)
options.setHeadless(true)
new SeleniumJSEnv(options)
case Chrome =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
package cats.effect.unsafe

import scala.concurrent.duration._
import scala.scalajs.js
import scala.scalajs.js.timers
import scala.util.Try

private[unsafe] abstract class SchedulerCompanionPlatform { this: Scheduler.type =>
private[this] val maxTimeout = Int.MaxValue.millis
Expand All @@ -38,8 +40,37 @@ private[unsafe] abstract class SchedulerCompanionPlatform { this: Scheduler.type

def nowMillis() = System.currentTimeMillis()
def monotonicNanos() = System.nanoTime()
override def nowMicros(): Long = nowMillis() * 1000
override def nowMicros(): Long = nowMicrosImpl()
},
() => ())

private[this] val nowMicrosImpl: () => Long = {
def test(performance: Performance) = {
// take it for a spin
!(performance.timeOrigin + performance.now()).isNaN
}

def browsers =
Try(js.Dynamic.global.performance.asInstanceOf[js.UndefOr[Performance]])
.toOption
.flatMap(_.toOption)
.filter(test)
def nodeJS =
Try(
js.Dynamic
.global
.require("perf_hooks")
.performance
.asInstanceOf[js.UndefOr[Performance]]).toOption.flatMap(_.toOption).filter(test)

browsers.orElse(nodeJS).map { performance => () =>
((performance.timeOrigin + performance.now()) * 1000).toLong
} getOrElse { () => System.currentTimeMillis() * 1000 }
}
}

@js.native
private trait Performance extends js.Any {
def timeOrigin: Double = js.native
def now(): Double = js.native
}
16 changes: 12 additions & 4 deletions project/CI.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,12 @@ object CI {
command = "ciFirefox",
rootProject = "rootJS",
jsEnv = Some(JSEnv.Firefox),
testCommands =
List("testOnly *tracing*", "testOnly *.RandomSpec", "testOnly *.ConsoleJSSpec"),
testCommands = List(
"testOnly *tracing*",
"testOnly *.ConsoleJSSpec",
"testOnly *.RandomSpec",
"testOnly *.SchedulerSpec"
),
mimaReport = false,
suffixCommands = List())

Expand All @@ -79,8 +83,12 @@ object CI {
command = "ciChrome",
rootProject = "rootJS",
jsEnv = Some(JSEnv.Chrome),
testCommands =
List("testOnly *tracing*", "testOnly *.RandomSpec", "testOnly *.ConsoleJSSpec"),
testCommands = List(
"testOnly *tracing*",
"testOnly *.ConsoleJSSpec",
"testOnly *.RandomSpec",
"testOnly *.SchedulerSpec"
),
mimaReport = false,
suffixCommands = List())

Expand Down
13 changes: 12 additions & 1 deletion tests/js/src/test/scala/cats/effect/unsafe/SchedulerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,20 @@ class SchedulerSpec extends BaseSpec {
// When the provided timeout in milliseconds overflows a signed 32-bit int, the implementation defaults to 1 millisecond
IO.sleep(Long.MaxValue.nanos).race(IO.sleep(100.millis)) mustEqual Right(())
}
"is using the correct max timeout" in real {
"use the correct max timeout" in real {
IO.sleep(Int.MaxValue.millis).race(IO.sleep(100.millis)) mustEqual Right(())
}
"use high-precision time" in real {
IO.realTime.product(IO.cede *> IO.realTime).map {
case (x, y) => (y - x).toMicros % 1000 should not(be_==(0L))
}
}
"correctly calculate real time" in real {
IO.realTime.product(IO(System.currentTimeMillis())).map {
case (realTime, currentTime) =>
(realTime.toMillis - currentTime) should be_<=(1L)
}
}
}

}