-
Notifications
You must be signed in to change notification settings - Fork 58
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
Fake time, using a time/capsule
#141
Comments
In case it's not clear from the main description, |
Another thing I haven't made clear probably is that my proposal would be the replacement of |
I also forgot to mention the While |
Nice problem statement! I agree that Is the intent for the capsule to be referred to explicitly by any code that needs FYI for another reference point... and something will make it into tick eventually, is https://tc39.es/proposal-temporal/docs/index.html#Temporal-now - which serves the same function as java.time Clock. In https://github.com/henryw374/tempo I'm looking at making an api for 'clocks' that works with Temporal.now and java.time.Clock |
fyi mutable clock in clojure https://gist.github.com/henryw374/2291e787087eeea513f9a8e5a5bd6f69 |
is there any significance of using |
good question. I don't think so.... I tried just now without and it seems to work ok. If I had a reason it has been forgotten as this was a few months ago. Also looking at this again, it would be good to be able to atomically set zone and instant. maybe it should just be a zoned-date-time. Circling back to the capsule idea... I am leaning towards preferring to create instances of java.time.Clock (or in js-land js/Temporal.Now) as these things work with the native APIs (just constructor fns afaik). I think 1-6 can still be addressed with that constraint. but pls let me know otherwise. and if so... I guess having a mutable clock in the lib would be handy. and anything more esoteric can be done in userspace. btw in Tempo there are no zero-args 'now' fns - you have to provide a clock. I've come to prefer working that way with tick - ie not using with-clock at all. |
Very exciting! In our app we have a We inject it into our Datomic components too, so even the db schema can be transacted in the past virtually, so we can transact theoretical past scenarios in our tests (because Datomic doesn't allow future transactions). The only thing we couldn't control the clock of is I haven't explored the +1 for using I'll try to review this thread in the coming ~2weeks, to give some feedback. |
another clock implementation https://gist.github.com/henryw374/38d61b20c62cd5450331f36ee9029a61 |
Problem
In automated tests, it's desirable to control time. It is achieved by something
typically called a fake clock.
The Use a fake system clock
article enumerated the expected behaviours of such a clock:
These lists completely matched my expectations and felt common sense to me.
To my surprise, only few of these use-cases were supported out of the box, by
java.time.Clock
or thisjuxt/tick
library. I don't see how can I achieveuse-case 4, 5 and 6.
java.time.Clock/fixed
solves use-case 1, 2 and 3.tick.core/AtomicClock
kinda solves use-case 4, but at the cost of introducing custom variants of
clojure.core/atom,{reset,swap}{,-vals}!,compare-and-set!
. The mentionedarticle demonstrates a solution to use-case 5, by a custom implementation of
j.t.Clock
.I've found a Java implementation of a
MutableClock
It solves use-case 4, but not 5. It's also introducing extra, custom API
(
set
,setInstant
) for changing the clock; typical parochialism. Itsimplementation is complected with concerns like serialization, and the way the
clock might be changed. Neither of these are very desirable in Clojure context.
This
MutableClock
is also a bit obscure, because I haven't found it mentionedin higher-level documentations, only in the auto-generated Java docs. I don't
feel confident using it and pulling in this extra library, just for this
simple concept, despite it's authored by the same @jodastephen, who is the
shepherd of
java.time
and JSR-310.Solution
To cater for use-case 3 and 4, while maintaining a convenient and idiomatic use
for use-case 7, I propose constructing a time-capsule concept. We can imagine
this capsule having a clock-stand, which holds a concrete clock. This clock
might be standing still at a time we specify, or ticking at the same, or
a different rate as the system clock. During tests, we can replace the clock on
this imaginary stand, with a different one, which might be derived from the one
already on the stand.
From an application's point of view, this capsule should just look like any
other
j.t.Clock
. Sincej.t.Clock
is just an abstract class, not a Javainterface, we cannot extend it conveniently from Clojure, without providing a
full-blown implementation for it and getting sucked into OO-land.
Instead, we can treat clocks as something
deref
able, just liket/AtomicClock
does, to conveniently take a reading of their current time. To adjust the clock
though, we need access to both the clock and the stand itself, to
swap!
it, so references to the capsule won't be affected.
Here is a possible implementation, for Clojure-only, for the sake of clarity:
I've also omitted
reset!
,reset-vals!
andswap-vals!
for the sake ofsimplifying discussion, by focusing on the core idea.
When I tried to print a
time/capsule
, I got an error about multi-methodambiguity, which I resolved with:
(prefer-method print-method java.util.Map clojure.lang.IDeref)
I don't have a lot of experience with hierarchies in Clojure, so I'm not sure
how to avoid this ambiguity, or what's better to prefer. I should probably just
provide a
(defmethod print-method Capsule)
(and itspprint
variant), butI'm not sure yet, what should the implementation look like.
Here is a demonstration of using a
time/capsule
:Pros:
j.t.Clock
implementingIDeref
, as opposed to an(atom (Clock/system.))
, which would require(t/instant @clk-holder)
to be read, or maybe@@clock-holder
somehow.Cons:
swap!
to its transformation function, is the clock,which is different from the value we get with
deref
, which is the time onthat clock.
IAtom
,IAtom2
and the ClojureScriptIReset
andISwap
protocols is quite a lot of boilerplatesuitable for our needs
The text was updated successfully, but these errors were encountered: