From cb0dc261cf31d41f69465bacb082f03f2834c006 Mon Sep 17 00:00:00 2001 From: Alex Engelberg Date: Sun, 5 Jun 2016 21:38:14 -0700 Subject: [PATCH] Add tests for transients, fix transient navigators based on test failures --- src/clj/com/rpl/specter/impl.cljx | 23 +++++++++--------- src/clj/com/rpl/specter/transient.cljx | 23 +++++++++++++++--- test/com/rpl/specter/core_test.cljx | 32 ++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 14 deletions(-) diff --git a/src/clj/com/rpl/specter/impl.cljx b/src/clj/com/rpl/specter/impl.cljx index 21f7b1f..26cbf4f 100644 --- a/src/clj/com/rpl/specter/impl.cljx +++ b/src/clj/com/rpl/specter/impl.cljx @@ -466,6 +466,14 @@ (defn vec-count [v] (count v)) +#+clj +(defn transient-vec-count [^clojure.lang.ITransientVector v] + (.count v)) + +#+cljs +(defn transient-vec-count [v] + (count v)) + (extend-protocol UpdateExtremes #+clj clojure.lang.PersistentVector #+cljs cljs.core/PersistentVector (update-first [v afn] @@ -505,6 +513,9 @@ #+clj clojure.lang.IPersistentVector #+cljs cljs.core/PersistentVector (fast-empty? [v] (= 0 (vec-count v))) + #+clj clojure.lang.ITransientVector #+cljs cljs.core/TransientVector + (fast-empty? [v] + (= 0 (transient-vec-count v))) #+clj Object #+cljs default (fast-empty? [s] (empty? s)) @@ -762,21 +773,11 @@ (extend-protocol p/Navigator TransientEndNavigator (select* [this structure next-fn] - []) + (next-fn [])) (transform* [this structure next-fn] (let [res (next-fn [])] (reduce conj! structure res)))) -(deftype TransientLastNavigator []) - -(extend-protocol p/Navigator - TransientLastNavigator - (select* [this structure next-fn] - (next-fn (nth structure (dec (count structure))))) - (transform* [this structure next-fn] - (let [i (dec (count structure))] - (assoc! structure i (next-fn (nth structure i)))))) - #+clj (defn transient-all-select [structure next-fn] diff --git a/src/clj/com/rpl/specter/transient.cljx b/src/clj/com/rpl/specter/transient.cljx index 6cde71f..304db07 100644 --- a/src/clj/com/rpl/specter/transient.cljx +++ b/src/clj/com/rpl/specter/transient.cljx @@ -25,13 +25,30 @@ "Navigates to an empty (persistent) vector at the end of a transient vector." (i/comp-paths* [(i/->TransientEndNavigator)])) +(defn- t-get-first + [tv] + (nth tv 0)) + +(defn- t-get-last + [tv] + (nth tv (dec (i/transient-vec-count tv)))) + +(defn- t-update-first + [tv next-fn] + (assoc! tv 0 (next-fn (nth tv 0)))) + +(defn- t-update-last + [tv next-fn] + (let [i (dec (i/transient-vec-count tv))] + (assoc! tv i (next-fn (nth tv i))))) + (def FIRST! "Navigates to the first element of a transient vector." - (keypath! 0)) + (i/->PosNavigator t-get-first t-update-first)) (def LAST! "Navigates to the last element of a transient vector." - (i/comp-paths* [(i/->TransientLastNavigator)])) + (i/->PosNavigator t-get-last t-update-last)) (defn- select-keys-from-transient-map "Selects keys from transient map, because built-in select-keys uses @@ -54,7 +71,7 @@ submap! [m-keys] (select* [this structure next-fn] - (select-keys-from-transient-map structure m-keys)) + (next-fn (select-keys-from-transient-map structure m-keys))) (transform* [this structure next-fn] (let [selected (select-keys-from-transient-map structure m-keys) res (next-fn selected)] diff --git a/test/com/rpl/specter/core_test.cljx b/test/com/rpl/specter/core_test.cljx index cf0fbb1..5e2d72b 100644 --- a/test/com/rpl/specter/core_test.cljx +++ b/test/com/rpl/specter/core_test.cljx @@ -25,6 +25,7 @@ #+cljs [cljs.test.check.generators :as gen] #+cljs [cljs.test.check.properties :as prop :include-macros true] [com.rpl.specter :as s] + [com.rpl.specter.transient :as t] [clojure.set :as set])) ;;TODO: @@ -1036,3 +1037,34 @@ (is (= 2 (key e))) (is (= 4 (val e))) )) + +(defspec transient-vector-test + (for-all+ + [v (gen/vector (limit-size 5 gen/int))] + (every? identity + (for [[path transient-path f] + [[s/FIRST t/FIRST! (fnil inc 0)] ;; fnil in case vector is empty + [s/LAST t/LAST! (fnil inc 0)] + [(s/keypath 0) (t/keypath! 0) (fnil inc 0)] + [s/END t/END! #(conj % 1 2 3)]]] + (and (= (s/transform* path f v) + (persistent! (s/transform* transient-path f (transient v)))) + (= (s/select* path v) + (s/select* transient-path (transient v)))))))) + +(defspec transient-map-test + (for-all+ + [m (gen/not-empty (gen/map gen/keyword gen/int)) + new-key gen/keyword] + (let [existing-key (first (keys m))] + (every? identity + (for [[path transient-path f] + [[(s/keypath existing-key) (t/keypath! existing-key) inc] + [(s/keypath new-key) (t/keypath! new-key) (constantly 3)] + [(s/submap [existing-key new-key]) + (t/submap! [existing-key new-key]) + (constantly {new-key 1234})]]] + (and (= (s/transform* path f m) + (persistent! (s/transform* transient-path f (transient m)))) + (= (s/select* path m) + (s/select* transient-path (transient m)))))))))