Comments (13)
I agree, that would be a great feature to have. One problem that I'm currently not sure is possible to solve right now is detecting when time can be "pushed forward". This should be done only when all threads of execution have suspended (either on delay
or some condition - e.g. reading from a blocking queue). In cats or ZIO, that's possible as you are in control of the runtime, so you know when all of your fibers are suspended, and none can be further executed unless the time progresses. With Loom, you'd have to somehow inspect the state of all currently running virtual threads. I don't think there's an API for that currently, and even it if where, there would also be the problem of scoping the checks: we only really want to wait until the virtual threads created by some parent scope are suspended, not necessarily all in the whole JVM.
from ox.
A project with similar goals (though no longer developed / maintained) is https://github.com/devexperts/time-test, but probably doesn't work with Loom
from ox.
IMHO this can only work if getting current time, and waiting for somehting comes together, e.g.
class Clock {
// Returns simulated time
def currentTime: Instant
// Wait for specific amount of time
def wait(duration: FiniteDuration): Unit
// Wait until a time is met.
def waitUntil(i: Instant): Unit
}
In my opinion, it shouldn't be necessary to track all other threads suspensions state, as long as the clock is monotonically increasing, because other threads can't make more assumptions. And if something crashes because the time advanced, well, that's exactly what a test should figure out.
On the other hand, all (testable) access to the time/wait must then use this clock, maybe in some scoped value?
from ox.
Yes, we can make this available via a scoped value, but what would the wait
method do then? It wouldn't know if it's fine to advance the clock (& "pretend" that e.g. 10 seconds has passed), as other threads might be runnable and have something to do in the "current" time. So such a test would behave very differently from one ran using wall-clock time.
from ox.
Idea is to write tests assuming execution of non-blocking code takes zero time. Which is true in most practical cases involving wait(duration)
calls
from ox.
I think it depends on the test scope. But if wait
immediately returns and stores advanced time, it should work for some reasons. For "smaller tests" this could already be useful, e.g.
class RetryPolicy {
def retry3Times(f: => T): T = { .. }
}
// The test
val retryPolicy = RetryPolicy (/* Configure*/)
intercept[CouldNotRetryAnymore]{
retryPolicy.retry3Times { throw RuntimeException() }
}
clock.waitedTime: FiniteDuration shouldBe 3 * retryPolicy.retryTime
from ox.
Ah, good to share some examples :) Something that I had in mind:
val i = AtomicInteger(0)
fork {
i.getAndAdd(1)
}
sleep(10.seconds)
assert(i.get() == 1)
In (almost ;) ) every "normal" execution that test would pass. But if sleeps don't sleep, it would fail.
from ox.
Isn't that good rather than bad? :-)
This is data race 'solved' by sleep call instead of joining forked thread. Both linearizations are valid and test one just uncovers illegal synchronization. Therefore my point is: emulated sleeps do not introduce new linearizations but shift their probabilities.
from ox.
Yes, you're right in principle, but then ... what are the usual cases for having sleep
s in your code in the first place?
One non-artificial example that comes to my mind is sending pings over a web socket:
fork {
forever {
ws.send(Ping())
sleep(1.second)
}
}
ws.send(msgsToSendChannel.receive())
with the sleep-doesn't-sleep, the test might end up sending an infinite stream of pings, and no messages altogether. Or vice versa.
from ox.
I see two alternatives in your example
- We wait a minimal time (like 5ms), so that a parallel thread can check exit-assumptions, but that wouldn't be very good unit tests as we need statistical results
- The clock has some upper boundery for simulated time, e.g and we stop the children thread if this time is over. Then no real waiting has to be done.
val clock = SimualtedClock(simulatedTime = 1.minute)
clock.run {
// Adams Example
}
pingReceived.count shouldBe 60
In the end it looks like, multiple clock implementations can be discussed, depending on the way of testing methods.
from ox.
Cases I've encountered (and written tests for!) are:
- retries
- rate control (call remote service with at most 10 rps)
- scheduling
Additionally, from my experience of writing emulated time tests (for Future though, it is much easier):
All waits are coordinated by the test. When code under test calls wait() function, it block indefinitely until:
a) test calls tick()
funciton which releases single wait call with closest timeline
b) test calls waitUntil(time)
function which releases all waits before given timestamp
edit: here is some inspiration: https://gist.github.com/scf37/4071839f25e197e38e4b070cfbed977b
from ox.
@scf37 this sounds more or less like mocking of the clock for me, so that the test itself is capable of releasing waits.
This is a perfect use case for complicated things, which involves multiple threads.
In my small unit test thinking I would like to get rid of threads all together to get a deterministic behaviour. We can run a function, which itself calls wait/waitFor and has some stopping kriteria (e.g. when simulated time is out or some or some explicit behavior).
Consequently this leads to multiple clock implementations.
from ox.
I would like to get rid of threads all together to get a deterministic behaviour
Provided Mock does exactly that - it performs linearization by adding chunks of code to single queue executed by test.
Unfortunately there is no API in Loom to access such "chunks" (AKA coroutines/continuations) therefore I don't know how to implement deterministic tests outside of Futures. Still, emulated delays are much better than nothing.
from ox.
Related Issues (20)
- Flaky RateLimiterTest
- Consider supporting Scala 2.13
- Add License HOT 1
- Support app errors HOT 1
- `tell` method in actor is not working with sleep HOT 4
- Naming things - unwrapping Eithers in boundary-break: .value or .? HOT 53
- Rate control primitives? HOT 1
- Integration with raise4s library
- Scope finalizers don't run on runtime shutdown
- Optimize Chunk
- Add Source.fromFile and source.toFile
- Add source.encode and decode for text HOT 3
- Using `ScheduledExecutorService` for timeouts
- Proposal: make forking safe in regard to boundary-based combinators
- Proposal: prevent either block nesting to avoid type/try-catch mismatches HOT 1
- Proposal: either.catching should be a boundary operator HOT 8
- Proposal: provide an `OxApp` trait as main entry point for app HOT 12
- Cancelling `CancellableFork` optimization
- Two exceptions thrown at the same time cause the program to hang HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from ox.