-
Notifications
You must be signed in to change notification settings - Fork 99
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Comparable time marks in a time source returned by Clock.asTimeSource() #271
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
/* | ||
* Copyright 2019-2023 JetBrains s.r.o. and contributors. | ||
* 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.days | ||
import kotlin.time.Duration.Companion.nanoseconds | ||
|
||
@OptIn(ExperimentalTime::class) | ||
class ClockTimeSourceTest { | ||
@Test | ||
fun arithmetic() { | ||
val timeSource = Clock.System.asTimeSource() | ||
val mark0 = timeSource.markNow() | ||
|
||
val markPast = mark0 - 1.days | ||
val markFuture = mark0 + 1.days | ||
|
||
assertTrue(markPast < mark0) | ||
assertTrue(markFuture > mark0) | ||
assertEquals(mark0, markPast + 1.days) | ||
assertEquals(2.days, markFuture - markPast) | ||
} | ||
|
||
@Test | ||
fun elapsed() { | ||
val clock = object : Clock { | ||
var instant = Clock.System.now() | ||
override fun now(): Instant = instant | ||
} | ||
val timeSource = clock.asTimeSource() | ||
val mark = timeSource.markNow() | ||
assertEquals(Duration.ZERO, mark.elapsedNow()) | ||
|
||
clock.instant += 1.days | ||
assertEquals(1.days, mark.elapsedNow()) | ||
|
||
clock.instant -= 2.days | ||
assertEquals(-1.days, mark.elapsedNow()) | ||
|
||
clock.instant = Instant.MAX | ||
assertEquals(Duration.INFINITE, mark.elapsedNow()) | ||
} | ||
|
||
@Test | ||
fun differentSources() { | ||
val mark1 = Clock.System.asTimeSource().markNow() | ||
val mark2 = object : Clock { | ||
override fun now(): Instant = Instant.DISTANT_FUTURE | ||
}.asTimeSource().markNow() | ||
assertNotEquals(mark1, mark2) | ||
assertFailsWith<IllegalArgumentException> { mark1 - mark2 } | ||
assertFailsWith<IllegalArgumentException> { mark1 compareTo mark2 } | ||
} | ||
|
||
@Test | ||
fun saturation() { | ||
val mark0 = Clock.System.asTimeSource().markNow() | ||
|
||
val markFuture = mark0 + Duration.INFINITE | ||
val markPast = mark0 - Duration.INFINITE | ||
|
||
for (delta in listOf(Duration.ZERO, 1.nanoseconds, 1.days)) { | ||
assertEquals(markFuture, markFuture - delta) | ||
assertEquals(markFuture, markFuture + delta) | ||
|
||
assertEquals(markPast, markPast - delta) | ||
assertEquals(markPast, markPast + delta) | ||
} | ||
val infinitePairs = listOf(markFuture to markPast, markFuture to mark0, mark0 to markPast) | ||
for ((later, earlier) in infinitePairs) { | ||
assertEquals(Duration.INFINITE, later - earlier) | ||
assertEquals(-Duration.INFINITE, earlier - later) | ||
} | ||
assertEquals(Duration.ZERO, markFuture - markFuture) | ||
assertEquals(Duration.ZERO, markPast - markPast) | ||
Comment on lines
+80
to
+81
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This highlights that we have a problem with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are not instants, but time marks, they have different saturation rules. Some are infinitely distant, but we defined the difference between them as zero. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Is there a reason why we can't consider Looks like the difference is just the following:
So, we have two models of infinity:
Personally, after thinking about it, I like the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah, me neither. What I see value in, is making sure There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It's expected that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
By the same logic, wouldn't we also expect the following to be true? assertEquals(-Duration.INFINITE, instant - (instant + Duration.INFINITE))
Sure, why not consider There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe this is out of scope of this PR |
||
|
||
assertFailsWith<IllegalArgumentException> { markFuture - Duration.INFINITE } | ||
assertFailsWith<IllegalArgumentException> { markPast + Duration.INFINITE } | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But we already have
Instant.plus(Duration)
. Its behavior is slightly different, and I even likesaturatingAdd
more, but shouldn'tsaturatingDiff
also throw on NaN if we keepsaturatingAdd
the way it is here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, the operation
minus
on time marks is defined to return zero when calculating difference between two equal infinitely distant time marks.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it internally inconsistent? This would make sense in the
Instant
infinity model of "all events in the far future are just one event," but in theMark
infinity model of "infinitely far events are fuzzily distributed along the timeline," this is surprising.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's consistent in time mark infinity model: all the distant events are the same infinitely distant event. It's not consistent with math infinity, but we're ok with that. See the Libraries DM at 2023-04-11.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, ok. This model is so unintuitive to me that I didn't even think about it. Well, since the behavior is already established and you implemented it precisely, I guess there's no need to discuss anything further, as my gripes are with the behavior, not the implementation.