Back
chime-at (clj)
(source)function
(chime-at times f & [{:keys [error-handler on-finished], :or {on-finished (fn* [] ())}, :as opts}])
Deprecated: use `chime.core/chime-at` instead - see the source of this fn for a migration.
Calls `f` with the current time at every time in the `times` list.
Examples
chime
(ns chime-test
(:require
[chime :refer :all]
[clojure.core.async :as a :refer [<! go-loop]]
[clojure.test :refer :all]
[chime.core :as chime]
[chime.joda-time]
[clj-time.core :as t]
[clj-time.periodic])
(:import (java.time Instant)
(java.time.temporal ChronoUnit)))
(deftest test-chime-at
(let [will-be-omitted (.minusSeconds (now) 2)
t1 (.plusSeconds (now) 2)
t2 (.plusSeconds (now) 3)
proof (atom [])]
(chime-at [will-be-omitted t1 t2]
(fn [t]
(swap! proof conj [t
(chime-test/now)])))
(while (not (= (list t1 t2)
(map first @proof))))
(is (= [t1 t2]
(mapv first @proof)))
(check-timeliness! proof)))
(deftest test-error-handler
(testing "continues the schedule"
(let [proof (atom [])
!latch (promise)
sched (chime-at [(.plusMillis (Instant/now) 500)
(.plusMillis (Instant/now) 1000)]
(fn [time]
(throw (ex-info "boom!" {:time time})))
{:error-handler (fn [e]
(swap! proof conj e)
nil)
:on-finished (fn [] (deliver !latch nil))})]
(is (not= ::timeout (deref !latch 1500 ::timeout)))
(is (= 2 (count @proof)))
(is (every? ex-data @proof))))
(testing "rethrowing the error stops the schedule"
(let [proof (atom [])
!latch (promise)
sched (chime-at [(.plusMillis (Instant/now) 500)
(.plusMillis (Instant/now) 1000)]
(fn [time]
(throw (ex-info "boom!" {:time time})))
{:error-handler (fn [e]
(swap! proof conj e)
(throw e))
:on-finished (fn [] (deliver !latch nil))})]
(is (not= ::timeout (deref !latch 1500 ::timeout)))
(is (= 1 (count @proof)))
(is (every? ex-data @proof)))))
(deftest test-on-finished
(let [proof (atom false)]
(chime-at [(.plusSeconds (now) 2) (.plusSeconds (now) 4)]
(deftest test-cancellation-works-even-in-the-face-of-overrun-past-tasks
(let [proof (atom [])
do-stuff (fn [now]
;; some overrunning task:
(swap! proof conj now)
(Thread/sleep 5000))
cancel-stuff! (chime-at (rest (chime/periodic-seq (chime-test/now)
(java.time.Duration/ofSeconds 1)))
do-stuff)]
(Thread/sleep 3000)
(cancel-stuff!)
(is (= 1
(count @proof)))))
(deftest test-empty-or-completely-past-sequences-are-acceptable
(let [proof (atom false)]
(chime-at (map #(.minusSeconds (now) (* 60 %)) [5 4 3 2])
identity
{:on-finished (fn []
(reset! proof true))})
(while (not @proof))
(is @proof))
(let [proof (atom false)]
(chime-at []
identity
{:on-finished (fn []
(reset! proof true))})
(while (not @proof))
(is @proof)))
chime
(ns chime.core-test
(:require [chime.core :as chime]
[clojure.test :as t])
(:import (java.time Instant Duration)
(java.time.temporal ChronoUnit)))
(t/deftest test-chime-at
(let [times [(.minusSeconds (Instant/now) 2)
(.plusSeconds (Instant/now) 1)
(.plusSeconds (Instant/now) 2)]
proof (atom [])]
(with-open [sched (chime/chime-at times
(fn [t]
(swap! proof conj [t (Instant/now)])))]
(Thread/sleep 2500))
(t/is (= times (mapv first @proof)))
(check-timeliness! (rest @proof))))
(t/deftest empty-times
(t/testing "Empty or completely past sequences are acceptable"
(let [proof (atom false)]
(chime/chime-at []
identity
{:on-finished (fn []
(reset! proof true))})
(t/deftest test-on-finished
(let [proof (atom false)]
(chime/chime-at [(.plusMillis (Instant/now) 500) (.plusMillis (Instant/now) 1000)]
(fn [time])
{:on-finished (fn []
(Thread/sleep 100)
(reset! proof true))})
(Thread/sleep 1200)
(t/is @proof)))
(t/deftest test-error-handler
(t/testing "returning true continues the schedule"
(let [proof (atom [])
sched (chime/chime-at [(.plusMillis (Instant/now) 500)
(.plusMillis (Instant/now) 1000)]
(fn [time]
(throw (ex-info "boom!" {:time time})))
{:error-handler (fn [e]
(swap! proof conj e)
true)})]
(t/is (not= ::timeout (deref sched 1500 ::timeout)))
(t/is (= 2 (count @proof)))
(t/is (every? ex-data @proof))))
(t/testing "returning false stops the schedule"
(let [proof (atom [])
sched (chime/chime-at [(.plusMillis (Instant/now) 500)
(.plusMillis (Instant/now) 1000)]
(fn [time]
(throw (ex-info "boom!" {:time time})))
{:error-handler (fn [e]
(swap! proof conj e)
false)})]
(t/is (not= ::timeout (deref sched 1500 ::timeout)))
(t/is (= 1 (count @proof)))
(t/is (every? ex-data @proof)))))
(t/deftest test-long-running-jobs
(let [proof (atom [])
!latch (promise)
now (Instant/now)
times (->> (chime/periodic-seq now (Duration/ofMillis 500))
(take 3))
sched (chime/chime-at times
(fn [time]
(swap! proof conj [time (Instant/now)])
(Thread/sleep 750)))]
(t/deftest test-cancelling-overrunning-task
(let [!proof (atom [])
!error (atom nil)
!latch (promise)]
(with-open [sched (chime/chime-at (chime/periodic-seq (Instant/now) (Duration/ofSeconds 1))
(fn [now]
(swap! !proof conj now)
(Thread/sleep 3000))
{:error-handler (fn [e]
(reset! !error e))
:on-finished (fn []
(deliver !latch nil))})]
(Thread/sleep 2000))
(t/deftest test-only-call-on-finished-once-36
(let [!count (atom 0)
now (Instant/now)]
(with-open [chiming (chime/chime-at [(.plusMillis now 500)
(.plusMillis now 500)]
(fn [time])
{:on-finished #(swap! !count inc)})]
(Thread/sleep 1000))