 
 (t/deftest build-test
   (let [drvs (drv/build (test-spec (atom 1)))]
-    (t/is (= 1 @(:base drvs)))
-    (t/is (= 2 @(:inc drvs)))
-    (t/is (= {:base 1 :after-inc 2} @(:as-map drvs)))
-    (t/is (= 3 @(:sum drvs)))))
+    (t/is (= 1 @(get drvs kbase)))
+    (t/is (= 2 @(get drvs kinc)))
+    (t/is (= {:base 1 :after-inc 2} @(get drvs kas-map)))
+    (t/is (= 3 @(get drvs ksum)))))
 
 (t/deftest sync-derivatives-test
-  (let [drvs (drv/sync-derivatives! (test-spec (atom 1)) (drv/prefix-id) {} [:base :inc])]
-    (t/is (= 1 @(:base drvs)))
-    (t/is (= 2 @(:inc drvs)))
-    (t/is (nil? (:as-map drvs)))
-    (t/is (nil? (:sum drvs)))
-    (t/is (= [:inc] @compute-log))))
+  (let [drvs (drv/sync-derivatives! (test-spec (atom 1)) (drv/prefix-id) {} [kbase kinc])]
+    (t/is (= 1 @(get drvs kbase)))
+    (t/is (= 2 @(get drvs kinc)))
+    (t/is (nil? (get drvs kas-map)))
+    (t/is (nil? (get drvs ksum)))
+    (t/is (= [kinc] @compute-log))))
 
 (t/deftest sync-derivatives-test
   (let [spec (test-spec (atom 1))
         id   (drv/prefix-id)
-        drvs (drv/sync-derivatives! spec id {} [:base :inc])]
-    (t/is (= 1 @(:base drvs)))
-    (t/is (= 2 @(:inc drvs)))
-    (t/is (nil? (:as-map drvs)))
-    (t/is (nil? (:sum drvs)))
-    (t/is (= [:inc] @compute-log))
+        drvs (drv/sync-derivatives! spec id {} [kbase kinc])]
+    (t/is (= 1 @(get drvs kbase)))
+    (t/is (= 2 @(get drvs kinc)))
+    (t/is (nil? (get drvs kas-map)))
+    (t/is (nil? (get drvs ksum)))
+    (t/is (= [kinc] @compute-log))
     ;; sync with more required keys
-    (let [drvs-updated (drv/sync-derivatives! spec id drvs [:base :inc :as-map])]
-      (t/is (= [:inc :as-map] @compute-log)))))
+    (let [drvs-updated (drv/sync-derivatives! spec id drvs [kbase kinc kas-map])]
+      (t/is (= [kinc kas-map] @compute-log)))))
 
 ;; This test will fail if watches are not properly removed from source refs
 (t/deftest watches-disposed-test
   (let [base  (atom 1)
         spec  (test-spec base)
         id    (drv/prefix-id)
-        drvs1 (drv/sync-derivatives! spec id {} [:base :inc])
-        drvs2 (drv/sync-derivatives! spec id drvs1 [:base])]
+        drvs1 (drv/sync-derivatives! spec id {} [kbase kinc])
+        drvs2 (drv/sync-derivatives! spec id drvs1 [kbase])]
     (swap! base inc)
-    (t/is (= [:inc] @compute-log))))
+    (t/is (= [kinc] @compute-log))))
 
 (t/deftest pool-test
   (let [base  (atom 1)
         spec  (test-spec base)
         {:keys [get! release!]} (drv/derivatives-pool spec)]
-    (t/is (= 1 @(get! :base :1-token)))
+    (t/is (= 1 @(get! kbase :1-token)))
     (t/is (= [] @compute-log))
-    (t/is (= 2 @(get! :inc :2-token)))
-    (t/is (= [:inc] @compute-log))
-    (t/is (= 3 @(get! :sum :3-token)))
-    (t/is (= [:inc :as-map :sum] @compute-log))
+    (t/is (= 2 @(get! kinc :2-token)))
+    (t/is (= [kinc] @compute-log))
+    (t/is (= 3 @(get! ksum :3-token)))
+    (t/is (= [kinc kas-map ksum] @compute-log))
     (swap! base inc)
-    (t/is (= [:inc :as-map :sum :inc :as-map :sum :as-map] @compute-log))
-    (t/is (= 5 @(get! :sum :3-token)))))
+    ;; Previously this test was used:
+    ;; (t/is (= [kinc kas-map ksum kinc kas-map ksum kas-map] @compute-log))
+    ;; We cannot guarantee an order in which the watches are triggered though
+    ;; and so all we can test is that a thing as been recomputed at least n times
+    (t/is (at-least-n-in kinc 2 @compute-log))
+    (t/is (at-least-n-in ksum 2 @compute-log))
+    (t/is (at-least-n-in kas-map 3 @compute-log))
+    (t/is (= 5 @(get! ksum :3-token)))))
 
 (t/deftest pool-throws-if-unknown-test
   (let [base  (atom 1)

(def compute-log (atom []))
(defn log! [id] (swap! compute-log conj id))

(t/use-fixtures :each (fn [f] (reset! compute-log []) (f)))

(defn test-spec [base]
  {:base   [[]           base]
   :inc    [[:base]      (fn [base] (log! :inc) (inc base))]
   :as-map [[:base :inc] (fn [base inc] (log! :as-map) {:base base :after-inc inc})]
   :sum    [[:as-map]    (fn [as-map] (log! :sum) (+ (:base as-map) (:after-inc as-map)))]})


(t/deftest not-required-test
  (t/is (= [2] (drv/not-required {:a 1 :b 2} #{:a})))

        pool-state (atom {})
        dm (drv/->DerivativesPool spec (drv/prefix-id) (drv/spec->graph spec) pool-state)]

    (drv/get! dm ksum :1-token)
    (t/is (= {ksum #{:1-token}} (:registry @pool-state)))
    (drv/get! dm kas-map :2-token)
    (t/is (= {ksum #{:1-token} kas-map #{:2-token}} (:registry @pool-state)))
    (t/is (= {:base 1 :after-inc 2} (-> @pool-state :derivatives (get kas-map) deref)))
    (t/is (= 3 (-> @pool-state :derivatives (get ksum) deref)))

    ;; :as-map still needs to be present because :sum requires it
    (drv/release! dm kas-map :2-token)
    (t/is (= {ksum #{:1-token}} (:registry @pool-state)))
    (t/is (= {:base 1 :after-inc 2} (-> @pool-state :derivatives (get kas-map) deref)))
    (t/is (= 3 (-> @pool-state :derivatives (get ksum) deref)))

    ;; After releasing all, pool and registry should be empty
    (drv/release! dm ksum :1-token)
    (t/is (= {} (:registry @pool-state)))
    (t/is (= {} (:derivatives @pool-state)))))

