specter/test/com/rpl/specter/core_test.cljx
2016-05-08 23:39:14 +05:30

858 lines
26 KiB
Clojure

(ns com.rpl.specter.core-test
#+cljs (:require-macros
[cljs.test :refer [is deftest]]
[cljs.test.check.cljs-test :refer [defspec]]
[com.rpl.specter.cljs-test-helpers :refer [for-all+]]
[com.rpl.specter.macros
:refer [paramsfn defprotocolpath defpath extend-protocolpath
declarepath providepath]])
(:use
#+clj [clojure.test :only [deftest is]]
#+clj [clojure.test.check.clojure-test :only [defspec]]
#+clj [com.rpl.specter.test-helpers :only [for-all+]]
#+clj [com.rpl.specter.macros
:only [paramsfn defprotocolpath defpath extend-protocolpath
declarepath providepath]]
)
(:require #+clj [clojure.test.check.generators :as gen]
#+clj [clojure.test.check.properties :as prop]
#+cljs [cljs.test.check :as tc]
#+cljs [cljs.test.check.generators :as gen]
#+cljs [cljs.test.check.properties :as prop :include-macros true]
[com.rpl.specter :as s]
[clojure.set :as set]))
;;TODO:
;; test walk, codewalk
(defn limit-size [n {gen :gen}]
(gen/->Generator
(fn [rnd _size]
(gen rnd (if (< _size n) _size n)))))
(defn gen-map-with-keys [key-gen val-gen & keys]
(gen/bind (gen/map key-gen val-gen)
(fn [m]
(gen/bind
(apply gen/hash-map (mapcat (fn [k] [k val-gen]) keys))
(fn [m2]
(gen/return (merge m m2)))))))
(defspec select-all-keyword-filter
(for-all+
[kw gen/keyword
v (gen/vector (limit-size 5
(gen-map-with-keys gen/keyword gen/int kw)))
pred (gen/elements [odd? even?])]
(= (s/select [s/ALL kw pred] v)
(->> v (map kw) (filter pred))
)))
(defspec select-pos-extreme-pred
(for-all+
[v (gen/vector gen/int)
pred (gen/elements [odd? even?])
pos (gen/elements [[s/FIRST first] [s/LAST last]])]
(= (s/select-one [(s/filterer pred) (first pos)] v)
(->> v (filter pred) ((last pos)))
)))
(defspec select-all-on-map
(for-all+
[m (limit-size 5 (gen/map gen/keyword gen/int))]
(= (s/select [s/ALL s/LAST] m)
(for [[k v] m] v))
))
(deftest select-one-test
(is (thrown? #+clj Exception #+cljs js/Error (s/select-one [s/ALL even?] [1 2 3 4])))
(is (= 1 (s/select-one [s/ALL odd?] [2 4 1 6])))
)
(deftest select-first-test
(is (= 7 (s/select-first [(s/filterer odd?) s/ALL #(> % 4)] [3 4 2 3 7 5 9 8])))
(is (nil? (s/select-first [s/ALL even?] [1 3 5 9])))
)
(defspec transform-all-on-map
(for-all+
[m (limit-size 5 (gen/map gen/keyword gen/int))]
(= (s/transform [s/ALL s/LAST] inc m)
(into {} (for [[k v] m] [k (inc v)]))
)))
(defspec transform-all
(for-all+
[v (gen/vector gen/int)]
(let [v2 (s/transform [s/ALL] inc v)]
(and (vector? v2) (= v2 (map inc v)))
)))
(defspec transform-all-list
(for-all+
[v (gen/list gen/int)]
(let [v2 (s/transform [s/ALL] inc v)]
(and (seq? v2) (= v2 (map inc v)))
)))
(defspec transform-all-filter
(for-all+
[v (gen/vector gen/int)
pred (gen/elements [odd? even?])
action (gen/elements [inc dec])]
(let [v2 (s/transform [s/ALL pred] action v)]
(= v2 (map (fn [v] (if (pred v) (action v) v)) v))
)))
(defspec transform-last
(for-all+
[v (gen/not-empty (gen/vector gen/int))
pred (gen/elements [inc dec])]
(let [v2 (s/transform [s/LAST] pred v)]
(= v2 (concat (butlast v) [(pred (last v))]))
)))
(defspec transform-first
(for-all+
[v (gen/not-empty (gen/vector gen/int))
pred (gen/elements [inc dec])]
(let [v2 (s/transform [s/FIRST] pred v)]
(= v2 (concat [(pred (first v))] (rest v) ))
)))
(defspec transform-filterer-all-equivalency
(prop/for-all
[s (gen/vector gen/int)
target-type (gen/elements ['() []])
pred (gen/elements [even? odd?])
updater (gen/elements [inc dec])]
(let [v (into target-type s)
v2 (s/transform [(s/filterer pred) s/ALL] updater v)
v3 (s/transform [s/ALL pred] updater v)]
(and (= v2 v3) (= (type v2) (type v3)))
)))
(defspec transform-with-context
(for-all+
[kw1 gen/keyword
kw2 gen/keyword
m (limit-size 10 (gen-map-with-keys gen/keyword gen/int kw1 kw2))
pred (gen/elements [odd? even?])]
(= (s/transform [(s/collect-one kw2) kw1 pred] + m)
(if (pred (kw1 m))
(assoc m kw1 (+ (kw1 m) (kw2 m)))
m
))))
(defn differing-elements [v1 v2]
(->> (map vector v1 v2)
(map-indexed (fn [i [e1 e2]]
(if (not= e1 e2)
i)))
(filter identity)))
(defspec transform-last-compound
(for-all+
[pred (gen/elements [odd? even?])
v (gen/such-that #(some pred %) (gen/vector gen/int))]
(let [v2 (s/transform [(s/filterer pred) s/LAST] inc v)
differing-elems (differing-elements v v2)]
(and (= (count v2) (count v))
(= (count differing-elems) 1)
(every? (complement pred) (drop (first differing-elems) v2))
))))
;; max sizes prevent too much data from being generated and keeps test from taking forever
(defspec transform-keyword
(for-all+
[k1 (limit-size 3 gen/keyword)
k2 (limit-size 3 gen/keyword)
m1 (limit-size 5
(gen-map-with-keys
gen/keyword
(gen-map-with-keys gen/keyword gen/int k2)
k1))
pred (gen/elements [inc dec])]
(let [m2 (s/transform [k1 k2] pred m1)]
(and (= (assoc-in m1 [k1 k2] nil) (assoc-in m2 [k1 k2] nil))
(= (pred (get-in m1 [k1 k2])) (get-in m2 [k1 k2])))
)))
(defspec replace-in-test
(for-all+
[v (gen/vector gen/int)]
(let [res (->> v (map (fn [v] (if (even? v) (inc v) v))))
user-ret (->> v
(filter even?)
(map (fn [v] [v v]))
(apply concat))
user-ret (if (empty? user-ret) nil user-ret)]
(= (s/replace-in [s/ALL even?] (fn [v] [(inc v) [v v]]) v)
[res user-ret]
))))
(defspec replace-in-custom-merge
(for-all+
[v (gen/vector gen/int)]
(let [res (->> v (map (fn [v] (if (even? v) (inc v) v))))
last-even (->> v (filter even?) last)
user-ret (if last-even {:a last-even})]
(= (s/replace-in [s/ALL even?] (fn [v] [(inc v) v]) v :merge-fn (fn [curr new]
(assoc curr :a new)))
[res user-ret]
))))
(defspec srange-extremes-test
(for-all+
[v (gen/vector gen/int)
v2 (gen/vector gen/int)]
(let [b (s/setval s/BEGINNING v2 v)
e (s/setval s/END v2 v)]
(and (= b (concat v2 v))
(= e (concat v v2)))
)))
(defspec srange-test
(for-all+
[v (gen/vector gen/int)
b (gen/elements (-> v count inc range))
e (gen/elements (range b (-> v count inc)))
]
(let [sv (subvec v b e)
predcount (fn [pred v] (->> v (filter pred) count))
even-count (partial predcount even?)
odd-count (partial predcount odd?)
b (s/transform (s/srange b e) (fn [r] (filter odd? r)) v)]
(and (= (odd-count v) (odd-count b))
(= (+ (even-count b) (even-count sv))
(even-count v)))
)))
(deftest structure-path-directly-test
(is (= 3 (s/select-one :b {:a 1 :b 3})))
(is (= 5 (s/select-one (s/comp-paths :a :b) {:a {:b 5}})))
)
(deftest atom-test
(let [v (s/transform s/ATOM inc (atom 1))]
(is (instance? clojure.lang.Atom v))
(is (= 2 (s/select-one s/ATOM v) @v))))
(defspec view-test
(for-all+
[i gen/int
afn (gen/elements [inc dec])]
(= (first (s/select (s/view afn) i))
(afn i)
(s/transform (s/view afn) identity i)
)))
(defspec must-test
(for-all+
[k1 gen/int
k2 (gen/such-that #(not= k1 %) gen/int)
m (gen-map-with-keys gen/int gen/int k1)
op (gen/elements [inc dec])
]
(let [m (dissoc m k2)]
(and (= (s/transform (s/must k1) op m)
(s/transform (s/keypath k1) op m))
(= (s/transform (s/must k2) op m) m)
(= (s/select (s/must k1) m) (s/select (s/keypath k1) m))
(empty? (s/select (s/must k2) m))
))))
(defspec parser-test
(for-all+
[i gen/int
afn (gen/elements [inc dec #(* % 2)])
bfn (gen/elements [inc dec #(* % 2)])
cfn (gen/elements [inc dec #(* % 2)])]
(and (= (s/select-one! (s/parser afn bfn) i)
(afn i))
(= (s/transform (s/parser afn bfn) cfn i)
(-> i afn cfn bfn))
)))
(deftest selected?-test
(is (= [[1 3 5] [2 :a] [7 11 4 2 :a] [10 1 :a] []]
(s/setval [s/ALL (s/selected? s/ALL even?) s/END]
[:a]
[[1 3 5] [2] [7 11 4 2] [10 1] []]
))))
(defspec identity-test
(for-all+
[i gen/int
afn (gen/elements [inc dec])]
(and (= [i] (s/select nil i))
(= (afn i) (s/transform nil afn i)))))
(deftest nil-comp-test
(is (= [5] (s/select (com.rpl.specter.impl/comp-paths* nil) 5))))
(defspec putval-test
(for-all+
[kw gen/keyword
m (limit-size 10 (gen-map-with-keys gen/keyword gen/int kw))
c gen/int]
(= (s/transform [(s/putval c) kw] + m)
(s/transform [kw (s/putval c)] + m)
(assoc m kw (+ c (get m kw)))
)))
(defspec empty-selector-test
(for-all+
[v (gen/vector gen/int)]
(= [v]
(s/select [] v)
(s/select nil v)
(s/select (s/comp-paths) v)
(s/select (s/comp-paths nil) v)
(s/select [nil nil nil] v)
)))
(defspec empty-selector-transform-test
(for-all+
[kw gen/keyword
m (limit-size 10 (gen-map-with-keys gen/keyword gen/int kw))]
(and (= m
(s/transform nil identity m)
(s/transform [] identity m)
(s/transform (s/comp-paths []) identity m)
(s/transform (s/comp-paths nil nil) identity m)
)
(= (s/transform kw inc m)
(s/transform [nil kw] inc m)
(s/transform (s/comp-paths kw nil) inc m)
(s/transform (s/comp-paths nil kw nil) inc m)
))))
(deftest compose-empty-comp-path-test
(let [m {:a 1}]
(is (= [1]
(s/select [:a (s/comp-paths)] m)
(s/select [(s/comp-paths) :a] m)
))))
(defspec mixed-selector-test
(for-all+
[k1 (limit-size 3 gen/keyword)
k2 (limit-size 3 gen/keyword)
m (limit-size 5
(gen-map-with-keys
gen/keyword
(gen-map-with-keys gen/keyword gen/int k2)
k1))]
(= [(-> m k1 k2)]
(s/select [k1 (s/comp-paths k2)] m)
(s/select [(s/comp-paths k1) k2] m)
(s/select [(s/comp-paths k1 k2) nil] m)
(s/select [(s/comp-paths) k1 k2] m)
(s/select [k1 (s/comp-paths) k2] m)
)))
(deftest cond-path-test
(is (= [4 2 6 8 10]
(s/select [s/ALL (s/cond-path even? [(s/view inc) (s/view inc)]
#(= 3 %) (s/view dec))]
[1 2 3 4 5 6 7 8])))
(is (empty? (s/select (s/if-path odd? (s/view inc)) 2)))
(is (= [6 2 10 6 14]
(s/transform [(s/putval 2)
s/ALL
(s/if-path odd? [(s/view inc) (s/view inc)] (s/view dec))]
*
[1 2 3 4 5]
)))
(is (= 2
(s/transform [(s/putval 2)
(s/if-path odd? (s/view inc))]
*
2)))
)
(defspec cond-path-selector-test
(for-all+
[k1 (limit-size 3 gen/keyword)
k2 (limit-size 3 gen/keyword)
k3 (limit-size 3 gen/keyword)
m (limit-size 5
(gen-map-with-keys
gen/keyword
gen/int
k1
k2
k3))
pred (gen/elements [odd? even?])
]
(let [v1 (get m k1)
k (if (pred v1) k2 k3)]
(and
(= (s/transform (s/if-path [k1 pred] k2 k3) inc m)
(s/transform k inc m))
(= (s/select (s/if-path [k1 pred] k2 k3) m)
(s/select k m))
))))
(defspec multi-path-test
(for-all+
[k1 (limit-size 3 gen/keyword)
k2 (limit-size 3 gen/keyword)
m (limit-size 5
(gen-map-with-keys
gen/keyword
gen/int
k1
k2))
]
(= (s/transform (s/multi-path k1 k2) inc m)
(->> m
(s/transform k1 inc)
(s/transform k2 inc)))
))
(deftest empty-pos-transform
(is (empty? (s/select s/FIRST [])))
(is (empty? (s/select s/LAST [])))
(is (= [] (s/transform s/FIRST inc [])))
(is (= [] (s/transform s/LAST inc [])))
)
(defspec set-filter-test
(for-all+
[k1 gen/keyword
k2 (gen/such-that #(not= k1 %) gen/keyword)
k3 (gen/such-that (complement #{k1 k2}) gen/keyword)
v (gen/vector (gen/elements [k1 k2 k3]))]
(= (filter #{k1 k2} v) (s/select [s/ALL #{k1 k2}] v))
))
(deftest nil-select-one-test
(is (= nil (s/select-one! s/ALL [nil])))
(is (thrown? #+clj Exception #+cljs js/Error (s/select-one! s/ALL [])))
)
(defspec transformed-test
(for-all+
[v (gen/vector gen/int)
pred (gen/elements [even? odd?])
op (gen/elements [inc dec])]
(= (s/select-one (s/transformed [s/ALL pred] op) v)
(s/transform [s/ALL pred] op v))
))
(defspec basic-parameterized-composition-test
(for-all+
[k1 (limit-size 3 gen/keyword)
k2 (limit-size 3 gen/keyword)
m1 (limit-size 5
(gen-map-with-keys
gen/keyword
(gen-map-with-keys gen/keyword gen/int k2)
k1))
pred (gen/elements [inc dec])]
(let [p (s/comp-paths s/keypath s/keypath)]
(and
(= (s/compiled-select (p k1 k2) m1) (s/select [k1 k2] m1))
(= (s/compiled-transform (p k1 k2) pred m1) (s/transform [k1 k2] pred m1))
))))
(defspec various-orders-comp-test
(for-all+
[k1 (limit-size 3 gen/keyword)
k2 (limit-size 3 gen/keyword)
k3 (limit-size 3 gen/keyword)
m1 (limit-size 5
(gen-map-with-keys
gen/keyword
(gen-map-with-keys
gen/keyword
(gen-map-with-keys
gen/keyword
gen/int
k3
)
k2)
k1))
pred (gen/elements [inc dec])]
(let [paths [((s/comp-paths s/keypath s/keypath k3) k1 k2)
(s/comp-paths k1 k2 k3)
((s/comp-paths s/keypath k2 s/keypath) k1 k3)
((s/comp-paths k1 s/keypath k3) k2)
(s/comp-paths k1 (s/keypath k2) k3)
((s/comp-paths (s/keypath k1) s/keypath (s/keypath k3)) k2)
((s/comp-paths s/keypath (s/keypath k2) s/keypath) k1 k3)
]
]
(and
(apply = (for [p paths] (s/compiled-select p m1)))
(apply = (for [p paths] (s/compiled-transform p pred m1)))
))))
(defspec filterer-param-test
(for-all+
[k gen/keyword
k2 gen/keyword
v (gen/vector
(limit-size 5
(gen-map-with-keys
gen/keyword
gen/int
k
k2
)))
pred (gen/elements [odd? even?])
updater (gen/elements [inc dec])]
(and
(= (s/compiled-select ((s/filterer s/keypath pred) k) v)
(s/compiled-select (s/filterer k pred) v))
(= (s/compiled-transform ((s/comp-paths (s/filterer s/keypath pred) s/ALL k2) k)
updater
v)
(s/compiled-transform (s/comp-paths (s/filterer k pred) s/ALL k2)
updater
v))
)))
(deftest nested-param-paths
(let [p (s/filterer s/keypath (s/selected? s/ALL s/keypath (s/filterer s/keypath even?) s/ALL))
p2 (p :a :b :c)
p3 (s/filterer :a (s/selected? s/ALL :b (s/filterer :c even?) s/ALL))
data [{:a [{:b [{:c 4 :d 5}]}]}
{:a [{:c 3}]}
{:a [{:b [{:c 7}] :e [1]}]}]
]
(is (= (s/select p2 data)
(s/select p3 data)
[[{:a [{:b [{:c 4 :d 5}]}]}]]
))
))
(defspec subselect-nested-vectors
(for-all+
[v1 (gen/vector
(gen/vector gen/int))]
(let [path (s/comp-paths (s/subselect s/ALL s/ALL))
v2 (s/compiled-transform path reverse v1)]
(and
(= (s/compiled-select path v1) [(flatten v1)])
(= (flatten v1) (reverse (flatten v2)))
(= (map count v1) (map count v2))))))
(defspec subselect-param-test
(for-all+
[k gen/keyword
v (gen/vector
(limit-size 5
(gen-map-with-keys
gen/keyword
gen/int
k)))]
(and
(= (s/compiled-select ((s/subselect s/ALL s/keypath) k) v)
[(map k v)])
(let [v2 (s/compiled-transform ((s/comp-paths (s/subselect s/ALL s/keypath)) k)
reverse
v)]
(and (= (map k v) (reverse (map k v2)))
(= (map #(dissoc % k) v)
(map #(dissoc % k) v2))) ; only key k was touched in any of the maps
))))
(defspec param-multi-path-test
(for-all+
[k1 gen/keyword
k2 gen/keyword
k3 gen/keyword
m (limit-size 5
(gen-map-with-keys
gen/keyword
gen/int
k1
k2
k3
))
pred1 (gen/elements [odd? even?])
pred2 (gen/elements [odd? even?])
updater (gen/elements [inc dec])
]
(let [paths [((s/multi-path [s/keypath pred1] [s/keypath pred2] k3) k1 k2)
((s/multi-path [k1 pred1] [s/keypath pred2] s/keypath) k2 k3)
((s/multi-path [s/keypath pred1] [s/keypath pred2] s/keypath) k1 k2 k3)
(s/multi-path [k1 pred1] [k2 pred2] k3)
((s/multi-path [k1 pred1] [s/keypath pred2] k3) k2)
]]
(and
(apply =
(for [p paths]
(s/select p m)
))
(apply =
(for [p paths]
(s/transform p updater m)
))
))))
(defspec paramsfn-test
(for-all+
[v (gen/vector (gen/elements (range 10)))
val (gen/elements (range 10))
op (gen/elements [inc dec])
comparator (gen/elements [= > <])]
(let [path (s/comp-paths s/ALL (paramsfn [p] [v] (comparator v p)))]
(= (s/transform (path val) op v)
(s/transform [s/ALL #(comparator % val)] op v)))
))
(defspec subset-test
(for-all+
[s1 (gen/vector (limit-size 5 gen/keyword))
s2 (gen/vector (limit-size 5 gen/keyword))
s3 (gen/vector (limit-size 5 gen/int))
s4 (gen/vector (limit-size 5 gen/keyword))]
(let [s1 (set s1)
s2 (set s1)
s3 (set s1)
s4 (set s1)
combined (set/union s1 s2)
ss (set/union s2 s3)]
(and
(= (s/transform (s/subset s3) identity combined) combined)
(= (s/setval (s/subset s3) #{} combined) (set/difference combined s2))
(= (s/setval (s/subset s3) s4 combined) (-> combined (set/difference s2) (set/union s4)))
))))
(deftest submap-test
(is (= [{:foo 1}]
(s/select [(s/submap [:foo :baz])] {:foo 1 :bar 2})))
(is (= {:foo 1, :barry 1}
(s/setval [(s/submap [:bar])] {:barry 1} {:foo 1 :bar 2})))
(is (= {:bar 1, :foo 2}
(s/transform [(s/submap [:foo :baz]) s/ALL s/LAST] inc {:foo 1 :bar 1})))
(is (= {:a {:new 1}
:c {:new 1
:old 1}}
(s/setval [s/ALL s/LAST (s/submap [])] {:new 1} {:a nil, :c {:old 1}}))))
(deftest nil->val-test
(is (= {:a #{:b}}
(s/setval [:a s/NIL->SET (s/subset #{})] #{:b} nil)))
(is (= {:a #{:b :c :d}}
(s/setval [:a s/NIL->SET (s/subset #{})] #{:b} {:a #{:c :d}})))
(is (= {:a [:b]}
(s/setval [:a s/NIL->VECTOR s/END] [:b] nil)))
)
(defspec void-test
(for-all+
[s1 (gen/vector (limit-size 5 gen/int))]
(and
(empty? (s/select s/STOP s1))
(empty? (s/select [s/STOP s/ALL s/ALL s/ALL s/ALL] s1))
(= s1 (s/transform s/STOP inc s1))
(= s1 (s/transform [s/ALL s/STOP s/ALL] inc s1))
(= (s/transform [s/ALL (s/cond-path even? nil odd? s/STOP)] inc s1)
(s/transform [s/ALL even?] inc s1))
)))
(deftest stay-continue-tests
(is (= [[1 2 [:a :b]] [3 [:a :b]] [:a :b [:a :b]]]
(s/setval [(s/stay-then-continue s/ALL) s/END] [[:a :b]] [[1 2] [3]])))
(is (= [[1 2 [:a :b]] [3 [:a :b]] [:a :b]]
(s/setval [(s/continue-then-stay s/ALL) s/END] [[:a :b]] [[1 2] [3]])))
(is (= [[1 2 3] 1 3]
(s/select (s/stay-then-continue s/ALL odd?) [1 2 3])))
(is (= [1 3 [1 2 3]]
(s/select (s/continue-then-stay s/ALL odd?) [1 2 3])))
)
(declarepath MyWalker)
(providepath MyWalker
(s/if-path vector?
(s/if-path [s/FIRST #(= :abc %)]
(s/continue-then-stay s/ALL MyWalker)
[s/ALL MyWalker]
)))
(deftest recursive-path-test
(is (= [9 1 10 3 1]
(s/select [MyWalker s/ALL number?]
[:bb [:aa 34 [:abc 10 [:ccc 9 8 [:abc 9 1]]]] [:abc 1 [:abc 3]]])
))
(is (= [:bb [:aa 34 [:abc 11 [:ccc 9 8 [:abc 10 2]]]] [:abc 2 [:abc 4]]]
(s/transform [MyWalker s/ALL number?] inc
[:bb [:aa 34 [:abc 10 [:ccc 9 8 [:abc 9 1]]]] [:abc 1 [:abc 3]]])
))
)
(declarepath map-key-walker [akey])
(providepath map-key-walker
[s/ALL
(s/if-path [s/FIRST (paramsfn [akey] [curr] (= curr akey))]
s/LAST
[s/LAST (s/params-reset map-key-walker)])])
(deftest recursive-params-path-test
(is (= #{1 2 3} (set (s/select (map-key-walker :aaa)
{:a {:aaa 3 :b {:c {:aaa 2} :aaa 1}}}))))
(is (= {:a {:aaa 4 :b {:c {:aaa 3} :aaa 2}}}
(s/transform (map-key-walker :aaa) inc
{:a {:aaa 3 :b {:c {:aaa 2} :aaa 1}}})))
(is (= {:a {:c {:b "X"}}}
(s/setval (map-key-walker :b) "X" {:a {:c {:b {:d 1}}}})))
)
(deftest recursive-params-composable-path-test
(let [p (s/comp-paths s/keypath map-key-walker)]
(is (= [1] (s/select (p 1 :a) [{:a 3} {:a 1} {:a 2}])))
))
(deftest all-map-test
(is (= {3 3} (s/transform [s/ALL s/FIRST] inc {2 3})))
(is (= {3 21 4 31} (s/transform [s/ALL s/ALL] inc {2 20 3 30})))
)
(declarepath NestedHigherOrderWalker [k])
(providepath NestedHigherOrderWalker
(s/if-path vector?
(s/if-path [s/FIRST (paramsfn [k] [e] (= k e))]
(s/continue-then-stay s/ALL (s/params-reset NestedHigherOrderWalker))
[s/ALL (s/params-reset NestedHigherOrderWalker)]
)))
(deftest nested-higher-order-walker-test
(is (= [:q [:abc :I 3] [:ccc [:abc :I] [:abc :I "a" [:abc :I [:abc :I [:d]]]]]]
(s/setval [(NestedHigherOrderWalker :abc) (s/srange 1 1)]
[:I]
[:q [:abc 3] [:ccc [:abc] [:abc "a" [:abc [:abc [:d]]]]]]
))))
#+clj
(deftest large-params-test
(let [path (apply s/comp-paths (repeat 25 s/keypath))
m (reduce
(fn [m k]
{k m})
:a
(reverse (range 25)))]
(is (= :a (s/select-one (apply path (range 25)) m)))
))
;;TODO: there's a bug in clojurescript that won't allow
;; non function implementations of IFn to have more than 20 arguments
#+clj
(do
(defprotocolpath AccountPath [])
(defrecord Account [funds])
(defrecord User [account])
(defrecord Family [accounts])
(extend-protocolpath AccountPath User :account Family [:accounts s/ALL])
)
#+clj
(deftest protocolpath-basic-test
(let [data [(->User (->Account 30))
(->User (->Account 50))
(->Family [(->Account 51) (->Account 52)])]]
(is (= [30 50 51 52]
(s/select [s/ALL AccountPath :funds] data)))
(is (= [(->User (->Account 31))
(->User (->Account 51))
(->Family [(->Account 52) (->Account 53)])]
(s/transform [s/ALL AccountPath :funds]
inc
data)))
))
#+clj
(do
(defprotocolpath LabeledAccountPath [label])
(defrecord LabeledUser [account])
(defrecord LabeledFamily [accounts])
(extend-protocolpath LabeledAccountPath
LabeledUser [:account s/keypath]
LabeledFamily [:accounts s/keypath s/ALL])
)
#+clj
(deftest protocolpath-params-test
(let [data [(->LabeledUser {:a (->Account 30)})
(->LabeledUser {:a (->Account 50)})
(->LabeledFamily {:a [(->Account 51) (->Account 52)]})]]
(is (= [30 50 51 52]
(s/select [s/ALL (LabeledAccountPath :a) :funds] data)))
(is (= [(->LabeledUser {:a (->Account 31)})
(->LabeledUser {:a (->Account 51)})
(->LabeledFamily {:a [(->Account 52) (->Account 53)]})]
(s/transform [s/ALL (LabeledAccountPath :a) :funds]
inc
data)))
))
#+clj
(do
(defprotocolpath CustomWalker [])
(extend-protocolpath CustomWalker
Object nil
clojure.lang.PersistentHashMap [(s/keypath :a) CustomWalker]
clojure.lang.PersistentArrayMap [(s/keypath :a) CustomWalker]
clojure.lang.PersistentVector [s/ALL CustomWalker]
)
)
#+clj
(deftest mixed-rich-regular-protocolpath
(is (= [1 2 3 11 21 22 25]
(s/select [CustomWalker number?] [{:a [1 2 :c [3]]} [[[[[[11]]] 21 [22 :c 25]]]]])))
(is (= [2 3 [[[4]] :b 0] {:a 4 :b 10}]
(s/transform [CustomWalker number?] inc [1 2 [[[3]] :b -1] {:a 3 :b 10}])))
)
#+cljs
(defn make-queue [coll]
(reduce
#(conj %1 %2)
#queue []
coll))
#+clj
(defn make-queue [coll]
(reduce
#(conj %1 %2)
clojure.lang.PersistentQueue/EMPTY
coll))
(defspec transform-idempotency 50
(for-all+
[v1 (gen/vector gen/int)
l1 (gen/list gen/int)
m1 (gen/map gen/keyword gen/int)]
(let [s1 (set v1)
q1 (make-queue v1)
v2 (s/transform s/ALL identity v1)
m2 (s/transform s/ALL identity m1)
s2 (s/transform s/ALL identity s1)
l2 (s/transform s/ALL identity l1)
q2 (s/transform s/ALL identity q1)]
(and
(= v1 v2)
(= (type v1) (type v2))
(= m1 m2)
(= (type m1) (type m2))
(= s1 s2)
(= (type s1) (type s2))
(= l1 l2)
(seq? l2) ; Transformed lists are only guaranteed to impelment ISeq
(= q1 q2)
(= (type q1) (type q2))))))