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))