-
Notifications
You must be signed in to change notification settings - Fork 623
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
eventually fixed #4018
base: master
Are you sure you want to change the base?
eventually fixed #4018
Conversation
currently working on test code |
@@ -205,7 +211,7 @@ object NoopEventuallyListener : EventuallyListener { | |||
private class EventuallyControl(val config: EventuallyConfiguration) { | |||
|
|||
val start = timeInMillis() | |||
val end = start + config.duration.inWholeMilliseconds | |||
val end = addSafely() |
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.
Have you tried to run this? The local start
variable is not visible in addSafely()
, right?
[Edit: It is visible. My bad. Just needed to look at the whole file instead of the GitHub diff.]
What about expressing more clearly what happens, so that readers don't have to look up the implementation of addSafely()
? For example, mirroring the coerce...
functions in the stdilb, we could use:
val end = (start + config.duration.inWholeMilliseconds).orInfinityIfNegative()
with:
fun Long.orInfinityIfNegative() = if (this < 0) Duration.INFINITE.inWholeMilliseconds else this
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.
thanks to your advice I refactored code! thanks a lot!
fun hasAttemptsRemaining() = timeInMillis() < end && iterations < config.retries | ||
fun hasAttemptsRemaining() : Boolean { | ||
// If the duration is infinite, it is considered as default value, which means it will never stop | ||
val isInfinite = config.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.
Isn't this case already covered by isWithinDuration
?
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.
well
Isn't this case already covered by
isWithinDuration
?
isWithinDuration
only checks time depletion
isInfinte
checks if config.duration
if is set to default which is 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.
oh maybe since Duration.INFINITE
is almost same as INFINITE
I think I can rollback this expression
@@ -121,7 +121,7 @@ data class EventuallyConfiguration( | |||
} | |||
|
|||
object EventuallyConfigurationDefaults { | |||
var duration: Duration = Duration.INFINITE | |||
var duration: 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.
I am concerned over this approach in general. This thing is shared (because object
) and mutable (because var
). As such, there will be race conditions. We should refactor away the very need for shared mutable things. WDYT @OliverO2
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.
since this is default setting I think it is ok to change var
to val
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.
@AlexCue987 I agree.
All properties should really be val
(though I wonder how this would affect backward compatibility in this case). Global state is almost never a good idea and in particular with the option of running parallel tests and concurrent coroutines, any context-induced configuration should be coroutine-local. See #2791 for an example.
I also wonder why that object is part of the public API. It might better be private.
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.
I think its appropriate to make another pr and deal with this issue
on this pr I will only focus on eventually's function
I will raise another pr for this issue!
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.
Yes, that makes sense. To make this PR ready for review, we'd need some test code covering the proposed fixes. Would also be good to update the initial PR comment with references to issues resolved by this PR.
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.
I think its appropriate to make another pr and deal with this issue on this pr I will only focus on eventually's function I will raise another pr for this issue!
sure - this is a preexisting condition, not something introduced in this PR
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.
Yes, that makes sense. To make this PR ready for review, we'd need some test code covering the proposed fixes. Would also be good to update the initial PR comment with references to issues resolved by this PR.
done with test!
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.
- I commented some suggestions.
- Why not modify the comment of EventuallyConfigurationBuilder.duration that the constraint has been added?
return when { | ||
this in 0..INFINITE.inWholeMilliseconds -> this | ||
else -> INFINITE.inWholeMilliseconds.also { | ||
println("[WARN] end value $this is out of the valid range (0 to ${INFINITE.inWholeMilliseconds}), value is fixed to Duration.INFINITE.inWholeMilliseconds") |
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.
println("[WARN] end value $this is out of the valid range (0 to ${INFINITE.inWholeMilliseconds}), value is fixed to Duration.INFINITE.inWholeMilliseconds") | |
println("[WARN] end value $this is out of the valid range (0 to $it), value is fixed to Duration.INFINITE.inWholeMilliseconds") |
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.
Should we not just error out and throw if the duration is invalid?
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.
think throwing error is more clear then changing value since people will not set duration close to inifnite
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.
And no one is going to spot the warn in the logs unless they look closely.
Failing the test makes more sense if its a bug, which having an incorrect duration is.
...st-assertions-core/src/commonMain/kotlin/io/kotest/assertions/nondeterministic/eventually.kt
Outdated
Show resolved
Hide resolved
Can someone help me with test case to trigger Duration overflow? |
} | ||
|
||
test("when duration is set to default it cannot end test until iteration is done") { | ||
val finalCount = 10000 |
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.
This would also work with a count of 10, and faster, right?
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.
yes
@@ -205,7 +211,7 @@ object NoopEventuallyListener : EventuallyListener { | |||
private class EventuallyControl(val config: EventuallyConfiguration) { | |||
|
|||
val start = timeInMillis() | |||
val end = start + config.duration.inWholeMilliseconds | |||
val end = (start.milliseconds + config.duration).inWholeMilliseconds |
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.
here's how it works
Duration's single value cannot be Bigger than 2 ^ 63 - 1
if we add two Duration and if it's value's result is bigger than 2 ^ 63 -1 then we set Duration to Duration.INFINITE
so
this
val end = (start.milliseconds + config.duration).inWholeMilliseconds
is 100% safe!
I've come a long way round :(
b94febd
to
946651d
Compare
will resolve #3988
this is draft for
eventually
I made some senario for this new eventually
positve
-> works wellminus
-> error (this pr)minus
-> error (this pr)and there are some other feature I introduced in this pr
Duration.INFINITE
and prints out waringby the way can we change retries toLong
? I think it doesn't matter to changeInt
toLong
sorry found out reason why retries cannot be
Long
inExponentialIntervalFn.kt