Skip to content

Commit be8d402

Browse files
committed
Add pretty printer tests
1 parent 1bf099d commit be8d402

11 files changed

+695
-1
lines changed

project.clj

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
(def dev-test-common-profile
77
{:dependencies '[[nubank/matcher-combinators "3.9.1"
8-
:exclusions [org.clojure/clojure]]]
8+
:exclusions [org.clojure/clojure]]
9+
[org.clojure/test.check "1.1.1"]]
910
:source-paths ["test-resources/java"]
1011
:resource-paths ["test-resources"]
1112
:test-paths ["test"]

test/orchard/pp/pp_array_test.clj

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
(ns orchard.pp.pp-array-test
2+
(:require [clojure.test :refer [deftest is]]
3+
[orchard.pp.test :refer [pp]]))
4+
5+
(deftest pprint-array
6+
(is (= "[true false]\n" (pp (boolean-array [true false]))))
7+
(is (= "[97 98]\n" (pp (byte-array [(int \a) (int \b)]))))
8+
(is (= "[\\a \\b]\n" (pp (char-array [\a \b]))))
9+
(is (= "[1.0 2.0]\n" (pp (double-array [1.0 2.0]))))
10+
(is (= "[3.0 4.0]\n" (pp (float-array [3.0 4.0]))))
11+
(is (= "[1 2 3]\n" (pp (int-array [1 2 3]))))
12+
(is (= "[4 5 6]\n" (pp (into-array [4 5 6]))))
13+
(is (= "[7 8 9]\n" (pp (long-array [7 8 9]))))
14+
(is (= "[{:a 1} {:b 2}]\n" (pp (object-array [{:a 1} {:b 2}]))))
15+
(is (= "[10 11 22]\n" (pp (short-array [10 11 22]))))
16+
(is (= "[[1 2 3] [4 5 6]]\n" (pp (to-array-2d [[1 2 3] [4 5 6]])))))

test/orchard/pp/pp_cpp_test.clj

+164
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
(ns orchard.pp.pp-cpp-test
2+
(:require [clojure.test :refer [deftest]]
3+
[orchard.pp.test :refer [$]]))
4+
5+
;; This namespace is a holdover from the time when pp only supported Clojure.
6+
;; It compares the output of clojure.pprint/pprint to the output of
7+
;; orchard.pp.pp/pprint.
8+
;;
9+
;; It is largely superseded by orchard.pp.pp-test, and is probably OK to
10+
;; remove.
11+
12+
(comment ($ {:a 1}))
13+
14+
(deftest pprint
15+
;; Basic
16+
($ {})
17+
($ [nil nil])
18+
($ {:a 1})
19+
($ '(1 nil))
20+
($ {:a 1 :b 2 :c 3 :d 4} :max-width 24)
21+
($ {:args [{:op :var :assignable? true}]} :max-width 24)
22+
($ {:a 1 :b 2 :c 3 :d 4 :e 5} :max-width 24)
23+
($ {:a 1 :b 2 :c 3 :d 4} :max-width 0)
24+
($ {:a 1 :b 2 :c 3 :d 4 :e {:f 6}} :max-width 24)
25+
($ {:a 1
26+
:b 2
27+
:c 3
28+
:d 4
29+
:e {:a 1 :b 2 :c 3 :d 4 :e {:f 6 :g 7 :h 8 :i 9 :j 10}}}
30+
:max-width 24)
31+
32+
;; Queues
33+
($ clojure.lang.PersistentQueue/EMPTY)
34+
($ (conj clojure.lang.PersistentQueue/EMPTY 1))
35+
($ (conj clojure.lang.PersistentQueue/EMPTY 1 2 3) :print-length 1)
36+
($ (conj clojure.lang.PersistentQueue/EMPTY 1 2 3) :print-level 1)
37+
($ (conj clojure.lang.PersistentQueue/EMPTY 1 2 3) :print-length 1 :print-level 1)
38+
($ (conj clojure.lang.PersistentQueue/EMPTY 1 2 3) :max-width 6)
39+
40+
;; Max width
41+
($ {:a 1 :b 2 :c 3 :d 4} :max-width 0)
42+
43+
;; Meta
44+
($ (with-meta {:a 1} {:b 2}) :print-meta true)
45+
($ (with-meta {:a 1} {:b 2}) :print-meta true :max-width 2)
46+
47+
;; Print level
48+
($ {} :print-level 0)
49+
($ {:a 1} :print-level 0)
50+
($ {:a {:b 2}} :print-level 1)
51+
($ {:a {:b 2}} :print-level 2)
52+
($ {:a {:b 2}} :print-level 3)
53+
($ {{:a 1} :b} :print-level 1)
54+
($ {{:a 1} :b} :print-level 2)
55+
($ {{:a 1} :b} :print-level 3)
56+
($ '(:a (:b (:c (:d)))) :print-level 0)
57+
($ '(:a (:b (:c (:d)))) :print-level 1)
58+
($ '(:a (:b (:c (:d)))) :print-level 2)
59+
($ '(:a (:b (:c (:d)))) :print-level 3)
60+
($ '(:a (:b (:c (:d)))) :print-level 4)
61+
($ '(:a (:b (:c (:d)))) :print-level 5)
62+
63+
;; Print length
64+
($ '() :print-length 0)
65+
($ [] :print-length 0)
66+
($ #{} :print-length 0)
67+
($ {} :print-length 0)
68+
($ (range) :print-length 0)
69+
($ (range) :print-length 1)
70+
($ '(1 2 3) :print-length 0)
71+
($ '(1 2 3) :print-length 1)
72+
($ '(1 2 3) :print-length 2)
73+
($ '(1 2 3) :print-length 3)
74+
($ '(1 2 3) :print-length 4)
75+
76+
;; Print level and print length
77+
($ {} :print-level 0 :print-length 0)
78+
($ {} :print-level 1 :print-length 0)
79+
($ {} :print-level 0 :print-length 1)
80+
($ {} :print-level 1 :print-length 1)
81+
82+
($ {:a 1 :b 2} :print-level 0 :print-length 0)
83+
($ {:a 1 :b 2} :print-level 1 :print-length 0)
84+
($ {:a 1 :b 2} :print-level 0 :print-length 1)
85+
($ {:a 1 :b 2} :print-level 1 :print-length 1)
86+
87+
;; Width
88+
($ {[]
89+
[-1000000000000000000000000000000000000000000000000000000000000000N]}
90+
:max-width 72)
91+
92+
;; Reader macros
93+
($ #'map)
94+
($ '(#'map))
95+
($ '#{#'map #'mapcat})
96+
($ '{:arglists (quote ([xform* coll])) :added "1.7"})
97+
($ '@(foo))
98+
($ ''foo)
99+
($ '~foo)
100+
($ '('#{boolean char floats}) :max-width 23)
101+
($ '('#{boolean char floats}) :max-width 23 :print-level 0)
102+
($ '('#{boolean char floats}) :max-width 23 :print-length 0)
103+
($ '('#{boolean char floats}) :max-width 23 :print-length 3)
104+
105+
;; Namespace maps
106+
($ {:a/b 1} :print-namespace-maps true)
107+
($ {:a/b 1 :a/c 2} :print-namespace-maps true)
108+
($ {:a/b 1 :c/d 2} :print-namespace-maps true)
109+
($ {:a/b {:a/b 1}} :print-namespace-maps true)
110+
($ {'a/b 1} :print-namespace-maps true)
111+
($ {'a/b 1 'a/c 3} :print-namespace-maps true)
112+
($ {'a/b 1 'c/d 2} :print-namespace-maps true)
113+
($ {'a/b {'a/b 1}} :print-namespace-maps true)
114+
($ {:a/b 1} :print-namespace-maps false)
115+
($ {:a/b 1 :a/c 2} :print-namespace-maps false)
116+
($ {:a/b 1 :c/d 2} :print-namespace-maps false)
117+
($ {:a/b {:a/b 1}} :print-namespace-maps false)
118+
($ {'a/b 1} :print-namespace-maps false)
119+
($ {'a/b 1 'a/c 3} :print-namespace-maps false)
120+
($ {'a/b 1 'c/d 2} :print-namespace-maps false)
121+
($ {'a/b {'a/b 1}} :print-namespace-maps false)
122+
($ #:a{:b 1 :c 2} :max-width 14 :print-namespace-maps true)
123+
($ #{'a/b 1 'a/c 2} :max-width 14 :print-namespace-maps true)
124+
125+
;; Custom tagged literals
126+
;; ($ #time/date "2023-10-02")
127+
128+
;; Sorted maps
129+
($ (sorted-map))
130+
($ (sorted-map :a 1 :b 2))
131+
($ (sorted-map :a 1 :b 2) :print-length 1)
132+
($ (sorted-map :a 1 :b 2) :max-width 7)
133+
134+
;; Sorted sets
135+
($ (sorted-set))
136+
($ (sorted-set 1 2 3))
137+
($ (sorted-set 1 2 3) :print-length 1)
138+
($ (sorted-set 1 2 3) :max-width 3)
139+
140+
;; Symbolic
141+
($ ##Inf)
142+
($ ##-Inf)
143+
($ ##NaN)
144+
145+
;; Map entries
146+
($ (find {:a 1} :a))
147+
($ (find {[:a 1] [:b 2]} [:a 1]))
148+
($ (find {:a 1} :a) :print-level 0)
149+
($ (find {:a 1} :a) :print-level 1)
150+
($ (find {:a 1} :a) :print-length 0)
151+
($ (find {:a 1} :a) :print-length 1)
152+
($ (find {:a 1} :a) :print-level 0 :print-length 0)
153+
($ (find {:a 1} :a) :print-level 0 :print-length 1)
154+
($ (find {:a 1} :a) :print-level 1 :print-length 0)
155+
($ (find {:a 1} :a) :print-level 1 :print-length 1)
156+
($ (find {[:a 1] 1} [:a 1]) :print-level 1 :print-length 0)
157+
($ (find {[:a 1] 1} [:a 1]) :print-level 1 :print-length 1)
158+
($ (find {0 1} 0) :max-width 2)
159+
($ [(find {:a 1} :a)])
160+
($ #{(find {:a 1} :a)})
161+
($ '((find {:a 1} :a))))
162+
163+
(deftest pprint-struct
164+
($ (struct (create-struct :q/a :q/b :q/c) 1 2 3)))
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
(ns orchard.pp.pp-custom-type-test
2+
(:require [clojure.pprint :as cpp]
3+
[clojure.test :refer [deftest is]]
4+
[orchard.pp.test :refer [pp]]))
5+
6+
(deftype T
7+
[^clojure.lang.Associative xs]
8+
clojure.lang.Associative
9+
(assoc [_ k v]
10+
(T. (.assoc xs k v))))
11+
12+
(def obj-re
13+
#"#object\[orchard.pp.pp_custom_type_test.T 0[xX][0-9a-fA-F]+ \"orchard.pp.pp_custom_type_test.T@[0-9a-fA-F]+\"\]\n")
14+
15+
(deftest pprint-custom-type
16+
(is (re-matches obj-re (with-out-str (prn (T. {:a 1})))))
17+
(is (re-matches obj-re (with-out-str (cpp/pprint (T. {:a 1})))))
18+
(is (re-matches obj-re (pp (T. {:a 1}))))
19+
20+
(binding [*print-level* 0]
21+
(is (re-matches obj-re (pp (T. {:a 1}))))))

test/orchard/pp/pp_flush_test.clj

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
(ns orchard.pp.pp-flush-test
2+
(:require [clojure.test :refer [deftest is testing]]
3+
[orchard.pp :as sut])
4+
(:import (java.io BufferedWriter StringWriter)))
5+
6+
(defn ^:private flush-observing-writer
7+
[writer]
8+
(let [a (atom 0)]
9+
[(proxy [BufferedWriter] [writer]
10+
(flush []
11+
(proxy-super flush)
12+
(swap! a inc)))
13+
a]))
14+
15+
(deftest flush-on-newline
16+
(testing "Does not flush after printing if *flush-on-newline* is nil"
17+
(let [sw (StringWriter.)
18+
[writer a] (flush-observing-writer sw)]
19+
(binding [*flush-on-newline* nil]
20+
(sut/pprint writer {:a 1 :b 2 :c 3 :d 4} {:max-width 10}))
21+
(is (= [0 ""] [@a (str sw)]))))
22+
23+
(testing "Only flushes after printing the entire top-level form if *flush-on-newline* is true"
24+
;; So when pretty-printing, *flush-on-newline* doesn't actually flush on
25+
;; newline, only after printing the entire top-level form.
26+
(let [sw (StringWriter.)
27+
[writer a] (flush-observing-writer sw)]
28+
(binding [*flush-on-newline* true]
29+
(sut/pprint writer {:a 1 :b 2 :c 3 :d 4} {:max-width 10})
30+
(sut/pprint writer {:a 1 :b 2 :c 3 :d 4} {:max-width 10}))
31+
(is (= [2 "{:a 1,\n :b 2,\n :c 3,\n :d 4}\n{:a 1,\n :b 2,\n :c 3,\n :d 4}\n"]
32+
[@a (str sw)])))))

test/orchard/pp/pp_gen_test.clj

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
(ns orchard.pp.pp-gen-test
2+
(:require [clojure.test.check.clojure-test :refer [defspec]]
3+
[clojure.test.check.generators :as gen]
4+
[clojure.test.check.properties :refer [for-all]]
5+
[orchard.pp.test :refer [pp cpp]]))
6+
7+
(defspec roundtrip 10000
8+
(for-all [x gen/any-printable-equatable
9+
print-dup gen/boolean
10+
map-entry-separator (gen/elements #{"," ""})]
11+
(= x
12+
(read-string
13+
(binding [*print-length* nil
14+
*print-level* nil
15+
*print-dup* print-dup]
16+
(pp x :map-entry-separator map-entry-separator))))))
17+
18+
(defspec level-zero-map-entry 10000
19+
(for-all [max-width gen/nat
20+
print-level gen/nat
21+
print-length gen/nat
22+
{:keys [me v]}
23+
(gen/fmap (fn [[k v]] {:me (clojure.lang.MapEntry. k v) :v [k v]})
24+
(gen/tuple
25+
gen/any-printable-equatable
26+
gen/any-printable-equatable))]
27+
(= (pp me :max-width max-width :print-level print-level :print-length print-length)
28+
(pp v :max-width max-width :print-level print-level :print-length print-length))))
29+
30+
(defspec non-map-coll-map-entry 250
31+
(let [me-g (gen/fmap (fn [[k v]] (clojure.lang.MapEntry. k v))
32+
(gen/tuple
33+
gen/any-printable-equatable
34+
gen/any-printable-equatable))]
35+
(for-all [print-level gen/nat
36+
print-length gen/nat
37+
xs (gen/one-of [(gen/vector me-g 1 8)
38+
(gen/fmap seq (gen/vector me-g 1 8))
39+
(gen/set me-g {:min-elements 1 :max-elements 8})])]
40+
(= (pp xs :max-width ##Inf :print-level print-level :print-length print-length)
41+
(cpp xs :max-width ##Inf :print-level print-level :print-length print-length)))))
42+
43+
(defspec pp-vs-cpp-map-entry 1000
44+
(for-all [print-level gen/nat
45+
print-length gen/nat
46+
x (gen/fmap (fn [[k v]] (clojure.lang.MapEntry. k v))
47+
(gen/tuple
48+
gen/any-printable-equatable
49+
gen/any-printable-equatable))]
50+
(= (pp x :max-width ##Inf :print-level print-level :print-length print-length)
51+
(cpp x :max-width ##Inf :print-level print-level :print-length print-length))))
52+
53+
(defspec pp-vs-cpp-vec 1000
54+
(for-all [print-level gen/nat
55+
print-length gen/nat
56+
x (gen/vector gen/small-integer)]
57+
(= (pp x :print-level print-level :print-length print-length)
58+
(cpp x :print-level print-level :print-length print-length))))
59+
60+
(defspec pp-vs-cpp-map-1 1000
61+
(for-all [print-length gen/nat
62+
print-level gen/nat
63+
x (gen/map gen/keyword gen/small-integer)]
64+
(= (pp x :print-level print-level :print-length print-length)
65+
(cpp x :print-level print-level :print-length print-length))))
66+
67+
(defspec pp-vs-cpp-map-2 1000
68+
(for-all [print-length gen/nat
69+
print-level gen/nat
70+
print-namespace-maps gen/boolean
71+
x (gen/map gen/keyword-ns gen/small-integer)]
72+
(= (pp x :print-level print-level :print-length print-length :print-namespace-maps print-namespace-maps)
73+
(cpp x :print-level print-level :print-length print-length :print-namespace-maps print-namespace-maps))))
74+
75+
(defspec pp-vs-cpp-map-3 75
76+
(for-all [print-namespace-maps gen/boolean
77+
x (gen/map (gen/one-of [gen/keyword-ns gen/symbol-ns])
78+
gen/any-printable-equatable)]
79+
(= (pp x :max-width ##Inf :print-namespace-maps print-namespace-maps)
80+
(cpp x :max-width ##Inf :print-namespace-maps print-namespace-maps))))
81+
82+
;; A generative test that checks that pp/print and cpp/print print any
83+
;; gen/any-printable-equatable the same way would be great, but
84+
;; cpp/print sometimes prints things in miser mode even when there's
85+
;; enough space to use linear mode, and I don't want to have my impl do
86+
;; that.
87+
;;
88+
;; With infinite max width, however, orchard.pp.pp prints everything
89+
;; the same way as clojure.pprint.
90+
(defspec pp-vs-cpp-inf-max-width 1000
91+
(for-all [x gen/any-printable-equatable]
92+
(= (pp x :max-width ##Inf) (cpp x :max-width ##Inf))))
93+
94+
(def ^:private print-readably-edge-case
95+
"clojure.pprint prints this differently than prn or pp/pprint, causing the
96+
generative test below to fail sometimes"
97+
(apply str (map char [9 133])))
98+
99+
(defspec print-readably 1000
100+
(for-all [x (gen/one-of [gen/string (gen/vector gen/char)])
101+
print-readably gen/boolean]
102+
(or (= x print-readably-edge-case)
103+
(= (pp x :print-readably print-readably) (cpp x :print-readably print-readably)))))
104+
105+
(defspec print-arrays 1000
106+
(for-all [booleans (gen/vector gen/boolean)
107+
bytes (gen/vector gen/byte)
108+
chars (gen/vector gen/char)
109+
doubles (gen/vector gen/double)
110+
ints (gen/vector gen/small-integer)
111+
longs (gen/vector gen/large-integer)
112+
objects (gen/vector gen/any-printable-equatable 1 10)]
113+
;; pp prints Java arrays the same way as vectors.
114+
(and
115+
(= (pp booleans) (pp (boolean-array booleans)))
116+
(= (pp bytes) (pp (byte-array bytes)))
117+
(= (pp chars) (pp (char-array chars)))
118+
(= (pp doubles) (pp (double-array doubles)))
119+
(= (pp ints) (pp (int-array ints)))
120+
(= (pp longs) (pp (long-array longs)))
121+
(= (pp objects) (pp (object-array objects))))))

test/orchard/pp/pp_print_dup_test.clj

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
(ns orchard.pp.pp-print-dup-test
2+
(:require [clojure.test :refer [deftest is]]
3+
[orchard.pp.test :refer [pp]]))
4+
5+
(defrecord R [x])
6+
7+
(deftest pprint-dup
8+
(binding [*print-dup* true]
9+
(doseq [x [1
10+
1.0
11+
1N
12+
1M
13+
"foo"
14+
{:a 1}
15+
[:a :b :c]
16+
#{:a :b :c}
17+
(java.math.BigInteger. "1")
18+
#'map
19+
#(inc 1)
20+
(doto (java.util.HashMap.) (.put :a 1))
21+
\a
22+
1/2
23+
#"[a-z]"
24+
(find-ns 'user)
25+
(java.util.Date.)
26+
(java.util.UUID/randomUUID)
27+
(->R 1)]]
28+
(is (= (str (print-str x) \newline) (pp x))))))

0 commit comments

Comments
 (0)