Skip to content

Commit

Permalink
Add cljs tests for map, make reader cross-platform
Browse files Browse the repository at this point in the history
Change the map-test namespace to `cljc`, several sections are still marked as
clj-only, because they rely on eval, hashCode, compact, or transient-map. I made
some adjustments to the cljs implementation in the process to make it better
match the clj version.
  • Loading branch information
plexus committed Aug 26, 2021
1 parent 0aa94cd commit 5efbf11
Show file tree
Hide file tree
Showing 10 changed files with 168 additions and 113 deletions.
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,12 @@ pom.xml.asc
target
.lein*
docs
tests.edn
.cljs_node_repl
.cpcache
.dir-locals.el
.nrepl-port
node_modules
out
package-lock.json
package.json
5 changes: 5 additions & 0 deletions bin/kaocha
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash

[ -d node_modules/ws ] || npm install ws

clojure -A:test -M -m kaocha.runner "$@"
9 changes: 6 additions & 3 deletions deps.edn
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{:paths ["src"],
:deps {org.clojure/clojure {:mvn/version "1.10.3"}},
{:paths ["src" "resources"]
:deps {org.clojure/clojure {:mvn/version "1.10.3"}}
:aliases
{:test {:extra-paths ["test"]}}}
{:test {:extra-paths ["test"]
:extra-deps {lambdaisland/kaocha {:mvn/version "RELEASE"}
lambdaisland/kaocha-cljs {:mvn/version "RELEASE"}
org.clojure/clojurescript {:mvn/version "RELEASE"}}}}}
2 changes: 0 additions & 2 deletions src/data_readers.clj

This file was deleted.

2 changes: 2 additions & 0 deletions src/data_readers.cljc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{ordered/set flatland.ordered.set/into-ordered-set
ordered/map flatland.ordered.map/ordered-map-reader}
5 changes: 5 additions & 0 deletions src/flatland/ordered/map.clj
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,8 @@ assoc'ed for the first time. Supports transient."
(defmethod print-method OrderedMap [o ^java.io.Writer w]
(.write w "#ordered/map ")
(print-method (seq o) w))

(defn ordered-map-reader [coll]
(if (some-> (resolve 'cljs.env/*compiler*) deref)
`(ordered-map ~(vec coll))
(ordered-map coll)))
23 changes: 18 additions & 5 deletions src/flatland/ordered/map.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
(pr-sequential-writer
writer
(fn [k w opts]
(-write w \[)
(-write w (pr-str k))
(-write w \space)
(-write w (pr-str (get kvs k))))
(-write w (pr-str (get kvs k)))
(-write w \]))
;; Printing with square brackets so that we can define a data_reader that
;; preserves order.
"[" ", " "]"
"(" " " ")"
opts
ks))

Expand Down Expand Up @@ -81,6 +83,11 @@
(when (seq ks)
(map #(-find kvs %) ks)))

IReversible
(-rseq [this]
(when (seq ks)
(map #(-find kvs %) (rseq ks))))

ICounted
(-count [this] (count kvs))

Expand Down Expand Up @@ -128,9 +135,15 @@
(.-kvs that)
that)))

(defn ordered-map [& kvs]
(OrderedMap. (apply hash-map kvs)
(mapv first (partition 2 kvs))))
(def ^:private empty-ordered-map (OrderedMap. {} []))

(defn ordered-map
([]
empty-ordered-map)
([coll]
(into empty-ordered-map coll))
([k v & kvs]
(apply assoc empty-ordered-map k v kvs)))

(comment
(ordered-map :foo 123 :bar 456)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,36 @@
(ns flatland.ordered.map-test
(:use clojure.test
[flatland.ordered.map :only [ordered-map]]
[flatland.ordered.common :only [compact]])
(:import flatland.ordered.map.OrderedMap))
(:require [clojure.test :refer [deftest testing is are]]
[flatland.ordered.map :refer [#?(:cljs OrderedMap) ordered-map]]
#?(:clj [flatland.ordered.common :refer [compact]]
:cljs [cljs.reader :as reader]))
#?(:clj (:import flatland.ordered.map.OrderedMap)))

#_(read-string "#ordered/map ([1 9] [3 4] [5 6] [7 8])")

#?(:cljs
(defn read-string [s]
(reader/read-string {:readers {'ordered/map ordered-map}} s)))

(deftest implementations
(let [basic (ordered-map)]
(testing "Interfaces marked as implemented"
(are [class] (instance? class basic)
clojure.lang.IPersistentMap
clojure.lang.IPersistentCollection
clojure.lang.Counted
clojure.lang.Associative
java.util.Map))
#?(:clj
(testing "Interfaces marked as implemented"
(are [class] (instance? class basic)
clojure.lang.IPersistentMap
clojure.lang.IPersistentCollection
clojure.lang.Counted
clojure.lang.Associative
java.util.Map)))
(testing "Behavior smoke testing"
(testing "Most operations don't change type"
(are [object] (= (class object) (class basic))
(conj basic [1 2])
(assoc basic 1 2)
(into basic {1 2})))
(are [object] (= (type object) (type basic))
(conj basic [1 2])
(assoc basic 1 2)
(into basic {1 2})))
(testing "Seq-oriented operations return nil when empty"
(are [object] (nil? object)
(seq basic)
(rseq basic)))
(seq basic)
(rseq basic)))
(testing "Metadata"
(is (nil? (seq (meta basic))))
(is (= 10 (-> basic
Expand Down Expand Up @@ -124,58 +132,60 @@
(is (= {:a 1 :b 2 :c 3 :d 4} (conj m {:d 4}))))
(testing "(conj m nil) returns m"
(are [x] (= m x)
(conj m nil)
(merge m ())
(into m ()))))
(conj m nil)
(merge m ())
(into m ()))))
(let [m (ordered-map :a '("spark") :b '("flare" "bolt"))]
(testing "assoc replaces values if not identical?"
(is (vector? (-> m
(update-in [:a] vec)
(get :a)))))))
(update-in [:a] vec)
(get :a)))))))

(deftest object-features
(let [m (ordered-map 'a 1 :b 2)]
(is (= "{a 1, :b 2}" (str m)))))
#?(:clj
(deftest object-features
(let [m (ordered-map 'a 1 :b 2)]
(is (= "{a 1, :b 2}" (str m))))))

(deftest transient-support
(let [m (ordered-map {1 2 7 8})]
(testing "Basic transient conj!"
(let [t (transient m)
t (conj! t [3 4])
t (conj! t [3 4])
p (persistent! t)]
(is (= p (conj m [3 4])))))
(testing "Transients still keep order"
(let [t (transient m)
t (assoc! t 0 1)
p (persistent! t)]
(is (= (concat (seq m) '([0 1]))
(seq p)))))
(testing "Transients can overwrite existing entries"
(let [t (transient m)
t (assoc! t 1 5)
p (persistent! t)]
(is (= p (assoc m 1 5)))))
(testing "Transients can dissoc!"
(let [k (key (first m))
t (transient m)
t (dissoc! t k)]
(is (= (persistent! t)
(dissoc m k)))))
(testing "Can't edit transient after calling persistent!"
(let [more [[:a 1] [:b 2]]
t (transient m)
t (reduce conj! t more)
p (persistent! t)]
(is (thrown? Throwable (assoc! t :c 3)))
(is (= (into m more) p))))
(testing "Transients are never equal to other objects"
(let [[t1 t2 :as ts] (repeatedly 2 #(transient m))
holder (apply hash-set ts)]
(is (not= t1 t2))
(is (= (count ts) (count holder)))
(are [t] (= t (holder t))
t1 t2)))))
#?(:clj
(deftest transient-support
(let [m (ordered-map {1 2 7 8})]
(testing "Basic transient conj!"
(let [t (transient m)
t (conj! t [3 4])
t (conj! t [3 4])
p (persistent! t)]
(is (= p (conj m [3 4])))))
(testing "Transients still keep order"
(let [t (transient m)
t (assoc! t 0 1)
p (persistent! t)]
(is (= (concat (seq m) '([0 1]))
(seq p)))))
(testing "Transients can overwrite existing entries"
(let [t (transient m)
t (assoc! t 1 5)
p (persistent! t)]
(is (= p (assoc m 1 5)))))
(testing "Transients can dissoc!"
(let [k (key (first m))
t (transient m)
t (dissoc! t k)]
(is (= (persistent! t)
(dissoc m k)))))
(testing "Can't edit transient after calling persistent!"
(let [more [[:a 1] [:b 2]]
t (transient m)
t (reduce conj! t more)
p (persistent! t)]
(is (thrown? Throwable (assoc! t :c 3)))
(is (= (into m more) p))))
(testing "Transients are never equal to other objects"
(let [[t1 t2 :as ts] (repeatedly 2 #(transient m))
holder (apply hash-set ts)]
(is (not= t1 t2))
(is (= (count ts) (count holder)))
(are [t] (= t (holder t))
t1 t2))))))

(deftest print-and-read-ordered
(let [s (ordered-map 1 2, 3 4, 5 6, 1 9, 7 8)]
Expand All @@ -186,34 +196,38 @@
(is (= '([1 9] [3 4] [5 6] [7 8])
(seq o))))))

(deftest print-read-eval-ordered
(is (= (pr-str (eval (read-string "#ordered/map[[:a 1] [:b 2]]")))
"#ordered/map ([:a 1] [:b 2])"))
(is (= (pr-str (eval (read-string "#ordered/map[[1 2] [3 4] [5 6] [1 9] [7 8]]")))
"#ordered/map ([1 9] [3 4] [5 6] [7 8])")))
#?(:clj
(deftest print-read-eval-ordered
(is (= (pr-str (eval (read-string "#ordered/map[[:a 1] [:b 2]]")))
"#ordered/map ([:a 1] [:b 2])"))
(is (= (pr-str (eval (read-string "#ordered/map[[1 2] [3 4] [5 6] [1 9] [7 8]]")))
"#ordered/map ([1 9] [3 4] [5 6] [7 8])"))))

(deftest compacting
(let [m1 (ordered-map :a 1 :b 2 :c 3)
m2 (dissoc m1 :b)
m3 (compact m2)
m4 (dissoc m3 :c)]
(is (= m2 (ordered-map :a 1 :c 3)))
(is (= m3 m2))
(is (= m4 (ordered-map :a 1)))))
#?(:clj
(deftest compacting
(let [m1 (ordered-map :a 1 :b 2 :c 3)
m2 (dissoc m1 :b)
m3 (compact m2)
m4 (dissoc m3 :c)]
(is (= m2 (ordered-map :a 1 :c 3)))
(is (= m3 m2))
(is (= m4 (ordered-map :a 1))))))

(deftest same-hash
(let [m1 (ordered-map :a 1 :b 2 :c 3)
m2 (hash-map :a 1 :b 2 :c 3)
m3 (array-map :a 1 :b 2 :c 3)]
(is (= (hash m1) (hash m2) (hash m3)))
(is (= (.hashCode m1) (.hashCode m2) (.hashCode m3)))
(is (= (hash (ordered-map)) (hash (hash-map)) (hash (array-map))))
(is (= (.hashCode (ordered-map)) (.hashCode (hash-map)) (.hashCode (array-map))))))
#?(:clj
(deftest same-hash
(let [m1 (ordered-map :a 1 :b 2 :c 3)
m2 (hash-map :a 1 :b 2 :c 3)
m3 (array-map :a 1 :b 2 :c 3)]
(is (= (hash m1) (hash m2) (hash m3)))
(is (= (.hashCode m1) (.hashCode m2) (.hashCode m3)))
(is (= (hash (ordered-map)) (hash (hash-map)) (hash (array-map))))
(is (= (.hashCode (ordered-map)) (.hashCode (hash-map)) (.hashCode (array-map)))))))

(deftest nil-hash-code-npe
;; No assertions here; just check that it doesn't NPE
;; See: https://github.com/amalloy/ordered/issues/27
(are [contents] (.hashCode (ordered-map contents))
[[nil :a]]
[[:a nil]]
[[nil nil]]))
#?(:clj
(deftest nil-hash-code-npe
;; No assertions here; just check that it doesn't NPE
;; See: https://github.com/amalloy/ordered/issues/27
(are [contents] (.hashCode (ordered-map contents))
[[nil :a]]
[[:a nil]]
[[nil nil]])))
22 changes: 11 additions & 11 deletions test/flatland/ordered/performance_test.clj
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
(ns flatland.ordered.performance-test
(:use clojure.test))

(deftest reflection
#_ (binding [*warn-on-reflection* true]
(are [ns-sym] (= ""
(with-out-str
(binding [*err* *out*]
(require :reload ns-sym))))
;; Order of the below is IMPORTANT. set depends on map, and if you
;; reload map *after* reloading set, then set refers to classes that
;; don't exist anymore, and all kinds of bad stuff happens
;; (in this test and others)
'ordered.map 'ordered.set)))
(deftest ^:kaocha/pending reflection
#_(binding [*warn-on-reflection* true]
(are [ns-sym] (= ""
(with-out-str
(binding [*err* *out*]
(require :reload ns-sym))))
;; Order of the below is IMPORTANT. set depends on map, and if you
;; reload map *after* reloading set, then set refers to classes that
;; don't exist anymore, and all kinds of bad stuff happens
;; (in this test and others)
'ordered.map 'ordered.set)))
6 changes: 6 additions & 0 deletions tests.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#kaocha/v1
{:tests [{:id :clj}
{:id :cljs
:type :kaocha.type/cljs}]
:plugins [:notifier :print-invocations :hooks]
:kaocha.hooks/pre-load [(fn [t] (require (quote flatland.ordered.map)) t)]}

0 comments on commit 5efbf11

Please sign in to comment.