From b544b690580a99df35b72855181f56f1efefaa95 Mon Sep 17 00:00:00 2001 From: hfhbd Date: Wed, 15 Dec 2021 13:37:37 +0100 Subject: [PATCH 1/4] Add TimeSource toClock converter --- core/common/src/Clock.kt | 14 ++++++++++ core/common/test/ClockTest.kt | 51 +++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 core/common/test/ClockTest.kt diff --git a/core/common/src/Clock.kt b/core/common/src/Clock.kt index def6813d4..acad3a640 100644 --- a/core/common/src/Clock.kt +++ b/core/common/src/Clock.kt @@ -35,3 +35,17 @@ private class InstantTimeMark(private val instant: Instant, private val clock: C override fun minus(duration: Duration): TimeMark = InstantTimeMark(instant - duration, clock) } + +/** + * Returns the [Clock] by storing an initial [TimeMark] using [TimeSource.markNow] and [returns][Clock.now] the elapsed + * time using [TimeMark.elapsedNow] plus the provided [offset]. + * + * This clock stores the initial [TimeMark], so repeatedly creating [Clock]s from the same [TimeSource] results + * into different [Instant]s iff the time of the [TimeSource] was increased. To sync different [Clock]s, use the [offset] + * parameter. + */ +@ExperimentalTime +public fun TimeSource.toClock(offset: Instant = Instant.fromEpochSeconds(0)): Clock = object : Clock { + private val startMark: TimeMark = markNow() + override fun now() = offset + startMark.elapsedNow() +} diff --git a/core/common/test/ClockTest.kt b/core/common/test/ClockTest.kt new file mode 100644 index 000000000..c77fb77ab --- /dev/null +++ b/core/common/test/ClockTest.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2019-2021 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.datetime.test + +import kotlinx.datetime.* +import kotlin.test.* +import kotlin.time.* +import kotlin.time.Duration.Companion.seconds + +@ExperimentalTime +class ClockTest { + @Test + fun timeSourceToClock() { + val timeSource = TestTimeSource() + val clock = timeSource.toClock() + + assertEquals(Instant.fromEpochSeconds(0), clock.now()) + assertEquals(Instant.fromEpochSeconds(0), clock.now()) + + timeSource += 1.seconds + assertEquals(Instant.fromEpochSeconds(1), clock.now()) + assertEquals(Instant.fromEpochSeconds(1), clock.now()) + } + + @Test + fun syncMultipleClocksFromTimeSource() { + val timeSource = TestTimeSource() + val clock1 = timeSource.toClock() + + assertEquals(0, clock1.now().epochSeconds) + + timeSource += 1.seconds + assertEquals(1, clock1.now().epochSeconds) + + val clock2 = timeSource.toClock(offset = Instant.fromEpochSeconds(1)) + assertEquals(clock1.now(), clock2.now()) + + timeSource += 1.seconds + assertEquals(2, clock1.now().epochSeconds) + assertEquals(clock1.now(), clock2.now()) + + val clock3 = timeSource.toClock(offset = clock2.now()) + timeSource += 1.seconds + assertEquals(3, clock3.now().epochSeconds) + assertEquals(clock1.now(), clock2.now()) + assertEquals(clock1.now(), clock3.now()) + } +} From 717d964ba43c50fb066ac72d32b63ef11188779c Mon Sep 17 00:00:00 2001 From: hfhbd Date: Sat, 29 Apr 2023 09:51:18 +0200 Subject: [PATCH 2/4] Rename function to asClock --- core/common/src/Clock.kt | 2 +- core/common/test/ClockTest.kt | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/common/src/Clock.kt b/core/common/src/Clock.kt index acad3a640..0351e21b6 100644 --- a/core/common/src/Clock.kt +++ b/core/common/src/Clock.kt @@ -45,7 +45,7 @@ private class InstantTimeMark(private val instant: Instant, private val clock: C * parameter. */ @ExperimentalTime -public fun TimeSource.toClock(offset: Instant = Instant.fromEpochSeconds(0)): Clock = object : Clock { +public fun TimeSource.asClock(offset: Instant = Instant.fromEpochSeconds(0)): Clock = object : Clock { private val startMark: TimeMark = markNow() override fun now() = offset + startMark.elapsedNow() } diff --git a/core/common/test/ClockTest.kt b/core/common/test/ClockTest.kt index c77fb77ab..8ba56d950 100644 --- a/core/common/test/ClockTest.kt +++ b/core/common/test/ClockTest.kt @@ -13,9 +13,9 @@ import kotlin.time.Duration.Companion.seconds @ExperimentalTime class ClockTest { @Test - fun timeSourceToClock() { + fun timeSourceAsClock() { val timeSource = TestTimeSource() - val clock = timeSource.toClock() + val clock = timeSource.asClock() assertEquals(Instant.fromEpochSeconds(0), clock.now()) assertEquals(Instant.fromEpochSeconds(0), clock.now()) @@ -28,21 +28,21 @@ class ClockTest { @Test fun syncMultipleClocksFromTimeSource() { val timeSource = TestTimeSource() - val clock1 = timeSource.toClock() + val clock1 = timeSource.asClock() assertEquals(0, clock1.now().epochSeconds) timeSource += 1.seconds assertEquals(1, clock1.now().epochSeconds) - val clock2 = timeSource.toClock(offset = Instant.fromEpochSeconds(1)) + val clock2 = timeSource.asClock(offset = Instant.fromEpochSeconds(1)) assertEquals(clock1.now(), clock2.now()) timeSource += 1.seconds assertEquals(2, clock1.now().epochSeconds) assertEquals(clock1.now(), clock2.now()) - val clock3 = timeSource.toClock(offset = clock2.now()) + val clock3 = timeSource.asClock(offset = clock2.now()) timeSource += 1.seconds assertEquals(3, clock3.now().epochSeconds) assertEquals(clock1.now(), clock2.now()) From da6dfb929573942b64bc51f93b39a5333a014304 Mon Sep 17 00:00:00 2001 From: Philip Wedemann <22521688+hfhbd@users.noreply.github.com> Date: Fri, 18 Oct 2024 13:13:33 +0200 Subject: [PATCH 3/4] Apply suggestions from code review Co-authored-by: Dmitry Khalanskiy <52952525+dkhalanskyjb@users.noreply.github.com> --- core/common/src/Clock.kt | 9 ++++----- core/common/test/ClockTest.kt | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/core/common/src/Clock.kt b/core/common/src/Clock.kt index 0db84644a..2da510030 100644 --- a/core/common/src/Clock.kt +++ b/core/common/src/Clock.kt @@ -54,14 +54,13 @@ private class InstantTimeMark(private val instant: Instant, private val clock: C } /** - * Returns the [Clock] by storing an initial [TimeMark] using [TimeSource.markNow] and [returns][Clock.now] the elapsed - * time using [TimeMark.elapsedNow] plus the provided [offset]. + * Creates a [Clock] that uses the [time mark at the moment of creation][TimeMark.markNow] to determine how [far][TimeMark.elapsedNow] + * the [current moment][Clock.now] is from the [origin]. * - * This clock stores the initial [TimeMark], so repeatedly creating [Clock]s from the same [TimeSource] results - * into different [Instant]s iff the time of the [TimeSource] was increased. To sync different [Clock]s, use the [offset] + * This clock stores the [TimeMark] at the moment of creation, so repeatedly creating [Clock]s from the same [TimeSource] results + * in different [Instant]s iff the time of the [TimeSource] was increased. To sync different [Clock]s, use the [offset] * parameter. */ -@ExperimentalTime public fun TimeSource.asClock(offset: Instant = Instant.fromEpochSeconds(0)): Clock = object : Clock { private val startMark: TimeMark = markNow() override fun now() = offset + startMark.elapsedNow() diff --git a/core/common/test/ClockTest.kt b/core/common/test/ClockTest.kt index 8ba56d950..d663ee903 100644 --- a/core/common/test/ClockTest.kt +++ b/core/common/test/ClockTest.kt @@ -10,7 +10,6 @@ import kotlin.test.* import kotlin.time.* import kotlin.time.Duration.Companion.seconds -@ExperimentalTime class ClockTest { @Test fun timeSourceAsClock() { From b0606d2fa3b15e07f09e5040a7f532a4fdfaed18 Mon Sep 17 00:00:00 2001 From: hfhbd <22521688+hfhbd@users.noreply.github.com> Date: Fri, 18 Oct 2024 17:04:05 +0200 Subject: [PATCH 4/4] Add sample --- core/common/src/Clock.kt | 8 ++-- core/common/test/ClockTest.kt | 50 ------------------------ core/common/test/ClockTimeSourceTest.kt | 38 ++++++++++++++++++ core/common/test/samples/ClockSamples.kt | 17 ++++++++ 4 files changed, 60 insertions(+), 53 deletions(-) delete mode 100644 core/common/test/ClockTest.kt diff --git a/core/common/src/Clock.kt b/core/common/src/Clock.kt index 3eacdc285..3403af53c 100644 --- a/core/common/src/Clock.kt +++ b/core/common/src/Clock.kt @@ -129,12 +129,14 @@ private class InstantTimeMark(private val instant: Instant, private val clock: C * the [current moment][Clock.now] is from the [origin]. * * This clock stores the [TimeMark] at the moment of creation, so repeatedly creating [Clock]s from the same [TimeSource] results - * in different [Instant]s iff the time of the [TimeSource] was increased. To sync different [Clock]s, use the [offset] + * in different [Instant]s iff the time of the [TimeSource] was increased. To sync different [Clock]s, use the [origin] * parameter. + * + * @sample kotlinx.datetime.test.samples.ClockSamples.timeSourceAsClock */ -public fun TimeSource.asClock(offset: Instant = Instant.fromEpochSeconds(0)): Clock = object : Clock { +public fun TimeSource.asClock(origin: Instant): Clock = object : Clock { private val startMark: TimeMark = markNow() - override fun now() = offset + startMark.elapsedNow() + override fun now() = origin + startMark.elapsedNow() } @Deprecated("Use Clock.todayIn instead", ReplaceWith("this.todayIn(timeZone)"), DeprecationLevel.WARNING) diff --git a/core/common/test/ClockTest.kt b/core/common/test/ClockTest.kt deleted file mode 100644 index d663ee903..000000000 --- a/core/common/test/ClockTest.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2019-2021 JetBrains s.r.o. - * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. - */ - -package kotlinx.datetime.test - -import kotlinx.datetime.* -import kotlin.test.* -import kotlin.time.* -import kotlin.time.Duration.Companion.seconds - -class ClockTest { - @Test - fun timeSourceAsClock() { - val timeSource = TestTimeSource() - val clock = timeSource.asClock() - - assertEquals(Instant.fromEpochSeconds(0), clock.now()) - assertEquals(Instant.fromEpochSeconds(0), clock.now()) - - timeSource += 1.seconds - assertEquals(Instant.fromEpochSeconds(1), clock.now()) - assertEquals(Instant.fromEpochSeconds(1), clock.now()) - } - - @Test - fun syncMultipleClocksFromTimeSource() { - val timeSource = TestTimeSource() - val clock1 = timeSource.asClock() - - assertEquals(0, clock1.now().epochSeconds) - - timeSource += 1.seconds - assertEquals(1, clock1.now().epochSeconds) - - val clock2 = timeSource.asClock(offset = Instant.fromEpochSeconds(1)) - assertEquals(clock1.now(), clock2.now()) - - timeSource += 1.seconds - assertEquals(2, clock1.now().epochSeconds) - assertEquals(clock1.now(), clock2.now()) - - val clock3 = timeSource.asClock(offset = clock2.now()) - timeSource += 1.seconds - assertEquals(3, clock3.now().epochSeconds) - assertEquals(clock1.now(), clock2.now()) - assertEquals(clock1.now(), clock3.now()) - } -} diff --git a/core/common/test/ClockTimeSourceTest.kt b/core/common/test/ClockTimeSourceTest.kt index 561cd2221..571f6580e 100644 --- a/core/common/test/ClockTimeSourceTest.kt +++ b/core/common/test/ClockTimeSourceTest.kt @@ -10,6 +10,7 @@ import kotlin.test.* import kotlin.time.* import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.nanoseconds +import kotlin.time.Duration.Companion.seconds @OptIn(ExperimentalTime::class) class ClockTimeSourceTest { @@ -83,4 +84,41 @@ class ClockTimeSourceTest { assertFailsWith { markFuture - Duration.INFINITE } assertFailsWith { markPast + Duration.INFINITE } } + + @Test + fun timeSourceAsClock() { + val timeSource = TestTimeSource() + val clock = timeSource.asClock(origin = Instant.fromEpochSeconds(0)) + + assertEquals(Instant.fromEpochSeconds(0), clock.now()) + assertEquals(Instant.fromEpochSeconds(0), clock.now()) + + timeSource += 1.seconds + assertEquals(Instant.fromEpochSeconds(1), clock.now()) + assertEquals(Instant.fromEpochSeconds(1), clock.now()) + } + + @Test + fun syncMultipleClocksFromTimeSource() { + val timeSource = TestTimeSource() + val clock1 = timeSource.asClock(origin = Instant.fromEpochSeconds(0)) + + assertEquals(0, clock1.now().epochSeconds) + + timeSource += 1.seconds + assertEquals(1, clock1.now().epochSeconds) + + val clock2 = timeSource.asClock(origin = Instant.fromEpochSeconds(1)) + assertEquals(clock1.now(), clock2.now()) + + timeSource += 1.seconds + assertEquals(2, clock1.now().epochSeconds) + assertEquals(clock1.now(), clock2.now()) + + val clock3 = timeSource.asClock(origin = clock2.now()) + timeSource += 1.seconds + assertEquals(3, clock3.now().epochSeconds) + assertEquals(clock1.now(), clock2.now()) + assertEquals(clock1.now(), clock3.now()) + } } diff --git a/core/common/test/samples/ClockSamples.kt b/core/common/test/samples/ClockSamples.kt index 83a3a1563..70683efa5 100644 --- a/core/common/test/samples/ClockSamples.kt +++ b/core/common/test/samples/ClockSamples.kt @@ -7,6 +7,8 @@ package kotlinx.datetime.test.samples import kotlinx.datetime.* import kotlin.test.* +import kotlin.time.Duration.Companion.seconds +import kotlin.time.TestTimeSource class ClockSamples { @Test @@ -45,4 +47,19 @@ class ClockSamples { check(clock.todayIn(TimeZone.UTC) == LocalDate(2020, 1, 1)) check(clock.todayIn(TimeZone.of("America/New_York")) == LocalDate(2019, 12, 31)) } + + @Test + fun timeSourceAsClock() { + // Creating a TimeSource + // When testing a Clock in combination of kotlinx-coroutines-test, use the testTimeSource of the TestDispatcher. + val timeSource = TestTimeSource() + // Creating a clock by setting the origin, here the epoch start + val clock = timeSource.asClock(origin = Instant.fromEpochSeconds(0)) + + check(Instant.fromEpochSeconds(0) == clock.now()) + + // Increasing the timeSource + timeSource += 1.seconds + check(Instant.fromEpochSeconds(1) == clock.now()) + } }