babashka/test-resources/lib_tests/expound/printer_test.cljc
Gabriel Horner 665ae4dd97
Finish up library tests (#1120)
* Add tests for markdown-clj and tools.namespace

See comment for why only one markdown test could be run.
Closes #1069 and #1064

* Convert 10 test libs using add-libtest

Also improved add-libtest to only require maven artifact
and rely on clojars for getting git-url most of the time

* Convert 8 more test libs using add-libtest

Also updated table and added comment for newline test

* Fix doric test

* Disable tools.namespace test that fails on windows

* Added dozen manual test libs and converted 2 test libs

add-libtest.clj supports manually-added and test-directories options

* Converts last tests to test namespaces and write libraries.csv

* Add a number of library tests from projects.md

Also add more docs around adding test libs and tweak add script

* Use :sha for gitlib and older clojure cli

* Revert "Use :sha for gitlib and older clojure cli"

This reverts commit c663ab8368.

* Fix and disable failing tests

Disabled tests that fail consistently and fixed windows one
2021-12-29 16:35:14 +01:00

428 lines
14 KiB
Clojure

(ns expound.printer-test
(:require [clojure.spec.alpha :as s]
[clojure.test :as ct :refer [is deftest use-fixtures testing]]
[expound.printer :as printer]
[clojure.string :as string]
[com.gfredericks.test.chuck.clojure-test :refer [checking]]
[expound.test-utils :as test-utils :refer [contains-nan?]]
[expound.spec-gen :as sg]
[expound.problems :as problems]))
(def num-tests 5)
(use-fixtures :once
test-utils/check-spec-assertions
test-utils/instrument-all)
(defn example-fn [])
(defn get-args [& args] args)
(deftest pprint-fn
(is (= "string?"
(printer/pprint-fn (::s/spec (s/explain-data string? 1)))))
(is (= "expound.printer-test/example-fn"
(printer/pprint-fn example-fn)))
(is (= "<anonymous function>"
(printer/pprint-fn #(inc (inc %)))))
(is (= "<anonymous function>"
(printer/pprint-fn (constantly true))))
(is (= "<anonymous function>"
(printer/pprint-fn (comp vec str))))
(is (= "expound.test-utils/instrument-all"
(printer/pprint-fn test-utils/instrument-all)))
(is (= "expound.test-utils/contains-nan?"
(printer/pprint-fn contains-nan?))))
(s/def :print-spec-keys/field1 string?)
(s/def :print-spec-keys/field2 (s/coll-of :print-spec-keys/field1))
(s/def :print-spec-keys/field3 int?)
(s/def :print-spec-keys/field4 string?)
(s/def :print-spec-keys/field5 string?)
(s/def :print-spec-keys/key-spec (s/keys
:req [:print-spec-keys/field1]
:req-un [:print-spec-keys/field2]))
(s/def :print-spec-keys/key-spec2 (s/keys
:req-un [(and
:print-spec-keys/field1
(or
:print-spec-keys/field2
:print-spec-keys/field3))]))
(s/def :print-spec-keys/key-spec3 (s/keys
:req-un [:print-spec-keys/field1
:print-spec-keys/field4
:print-spec-keys/field5]))
(s/def :print-spec-keys/set-spec (s/coll-of :print-spec-keys/field1
:kind set?))
(s/def :print-spec-keys/vector-spec (s/coll-of :print-spec-keys/field1
:kind vector?))
(s/def :print-spec-keys/key-spec4 (s/keys
:req-un [:print-spec-keys/set-spec
:print-spec-keys/vector-spec
:print-spec-keys/key-spec3]))
(defn copy-key [m k1 k2]
(assoc m k2 (get m k1)))
(deftest print-spec-keys*
(is (=
[{"key" :field2, "spec" "(coll-of :print-spec-keys/field1)"}
{"key" :print-spec-keys/field1, "spec" "string?"}]
(printer/print-spec-keys*
(map #(copy-key % :via :expound/via)
(::s/problems
(s/explain-data
:print-spec-keys/key-spec
{}))))))
(is (nil?
(printer/print-spec-keys*
(map #(copy-key % :via :expound/via)
(::s/problems
(s/explain-data
(s/keys
:req [:print-spec-keys/field1]
:req-un [:print-spec-keys/field2])
{}))))))
(is (=
[{"key" :print-spec-keys/field1, "spec" "string?"}]
(printer/print-spec-keys*
(map #(copy-key % :via :expound/via)
(::s/problems
(s/explain-data
(s/keys
:req [:print-spec-keys/field1]
:req-un [:print-spec-keys/field2])
{:field2 [""]}))))))
(is (=
[{"key" :print-spec-keys/field1, "spec" "string?"}
{"key" :print-spec-keys/field2,
"spec" "(coll-of :print-spec-keys/field1)"}]
(printer/print-spec-keys*
(map #(copy-key % :via :expound/via)
(::s/problems
(s/explain-data
(s/keys
:req [:print-spec-keys/field1
:print-spec-keys/field2])
{}))))))
(is (=
[{"key" :field1, "spec" "string?"}
{"key" :field2, "spec" "(coll-of :print-spec-keys/field1)"}
{"key" :field3, "spec" "int?"}]
(printer/print-spec-keys*
(map #(copy-key % :via :expound/via)
(::s/problems
(s/explain-data
:print-spec-keys/key-spec2
{}))))))
(is (=
[{"key" :key-spec3,
"spec" #?(:clj
"(keys\n :req-un\n [:print-spec-keys/field1\n :print-spec-keys/field4\n :print-spec-keys/field5])"
:cljs
"(keys\n :req-un\n [:print-spec-keys/field1\n :print-spec-keys/field4 \n :print-spec-keys/field5])")}
{"key" :set-spec, "spec" #?(:clj
"(coll-of\n :print-spec-keys/field1\n :kind\n set?)"
:cljs
"(coll-of :print-spec-keys/field1 :kind set?)")}
{"key" :vector-spec, "spec" #?(:clj "(coll-of\n :print-spec-keys/field1\n :kind\n vector?)"
:cljs "(coll-of\n :print-spec-keys/field1 \n :kind \n vector?)")}]
(printer/print-spec-keys*
(map #(copy-key % :via :expound/via)
(::s/problems
(s/explain-data
:print-spec-keys/key-spec4
{})))))))
(deftest print-table
(is (=
"
| :key | :spec |
|======+=======|
| abc | a |
| | b |
|------+-------|
| def | d |
| | e |
"
(printer/print-table [{:key "abc" :spec "a\nb"}
{:key "def" :spec "d\ne"}])))
;; can select ordering of keys
(is (=
"
| :b | :c |
|====+====|
| 2 | 3 |
|----+----|
| {} | () |
"
(printer/print-table
[:b :c]
[{:a 1 :b 2 :c 3}
{:a [] :b {} :c '()}])))
;; ordering is deterministic, not based on hashmap
;; semantics
(is (=
"
| :k | :a | :b | :c | :d | :e | :f | :g | :h | :i | :j |
|====+====+====+====+====+====+====+====+====+====+====|
| k | a | b | c | d | e | f | g | h | i | j |
|----+----+----+----+----+----+----+----+----+----+----|
| k | a | b | c | d | e | f | g | h | i | j |
"
(printer/print-table
[:k :a :b :c :d :e :f :g :h :i :j]
[{:a "a" :b "b" :c "c" :d "d" :e "e" :f "f" :g "g" :h "h" :i "i" :j "j" :k "k" :l "l"}
{:l "l" :k "k" :j "j" :i "i" :h "h" :g "g" :f "f" :e "e" :d "d" :c "c" :b "b" :a "a"}]))))
(deftest print-table-gen
(checking
"any table with have constant width"
num-tests
[col-count (s/gen pos-int?)
keys (s/gen (s/coll-of keyword? :min-count 1))
row-count (s/gen pos-int?)
vals (s/gen (s/coll-of
(s/coll-of string? :count col-count)
:count row-count))
:let [rows (mapv
#(zipmap keys (get vals %))
(range 0 row-count))
table (printer/print-table rows)
srows (rest (string/split table #"\n"))]]
(is (apply = (map count srows))))
(checking
"any table will contain a sub-table of all rows but the last"
num-tests
[col-count (s/gen pos-int?)
keys (s/gen (s/coll-of keyword? :min-count 1))
row-count (s/gen (s/int-in 2 10))
vals (s/gen (s/coll-of
(s/coll-of string? :count col-count)
:count row-count))
:let [rows (mapv
#(zipmap keys (get vals %))
(range 0 row-count))
sub-rows (butlast rows)
table (printer/print-table rows)
sub-table (printer/print-table sub-rows)
sub-table-last-row (last (string/split sub-table #"\n"))
table-last-row (last (string/split table #"\n"))]]
;; If the line we delete shrinks the width of the table
;; (because it was the widest value)
;; then the property will not apply
(when (= (count sub-table-last-row) (count table-last-row))
(is (string/includes? table sub-table))))
#?(:clj
(checking
"for any known registered spec, table has max width"
num-tests
[spec sg/spec-gen
:let [rows [{:key spec
:spec (printer/expand-spec spec)}]
table (printer/print-table rows)
srows (rest (string/split table #"\n"))]]
(is (< (count (last srows)) 200)))
:cljs
;; Noop, just to make clj-kondo happy
(sg/topo-sort [])))
(deftest highlighted-value
(testing "atomic value"
(is (= "\"Fred\"\n^^^^^^"
(printer/highlighted-value
{}
{:expound/form "Fred"
:expound/in []}))))
(testing "value in vector"
(is (= "[... :b ...]\n ^^"
(printer/highlighted-value
{}
{:expound/form [:a :b :c]
:expound/in [1]}))))
(testing "long, composite values are pretty-printed"
(is (= (str "{:letters {:a \"aaaaaaaa\",
:b \"bbbbbbbb\",
:c \"cccccccd\",
:d \"dddddddd\",
:e \"eeeeeeee\"}}"
#?(:clj "\n ^^^^^^^^^^^^^^^"
:cljs "\n ^^^^^^^^^^^^^^^^"))
;; ^- the above works in clojure - maybe not CLJS?
(printer/highlighted-value
{}
{:expound/form
{:letters
{:a "aaaaaaaa"
:b "bbbbbbbb"
:c "cccccccd"
:d "dddddddd"
:e "eeeeeeee"}}
:expound/in [:letters]}))))
(testing "args to function"
(is (= "(1 ... ...)\n ^"
(printer/highlighted-value
{}
{:expound/form (get-args 1 2 3)
:expound/in [0]}))))
(testing "show all values"
(is (= "(1 2 3)\n ^"
(printer/highlighted-value
{:show-valid-values? true}
{:expound/form (get-args 1 2 3)
:expound/in [0]}))))
(testing "special replacement chars are not used"
(is (= "\"$ $$ $1 $& $` $'\"\n^^^^^^^^^^^^^^^^^^"
(printer/highlighted-value
{}
(first
(:expound/problems
(problems/annotate
(s/explain-data keyword? "$ $$ $1 $& $` $'"))))))))
(testing "nested map-of specs"
(is (= "{:a {:b 1}}\n ^"
(printer/highlighted-value
{}
(first
(:expound/problems
(problems/annotate
(s/explain-data :highlighted-value/nested-map-of {:a {:b 1}})))))))
(is (= "{:a {\"a\" ...}}\n ^^^"
(printer/highlighted-value
{}
(first
(:expound/problems
(problems/annotate
(s/explain-data :highlighted-value/nested-map-of {:a {"a" :b}})))))))
(is (= "{1 ...}\n ^"
(printer/highlighted-value
{}
(first
(:expound/problems
(problems/annotate
(s/explain-data :highlighted-value/nested-map-of {1 {:a :b}}))))))))
(testing "nested keys specs"
(is (= "{:address {:city 1}}\n ^"
(printer/highlighted-value
{}
(first
(:expound/problems
(problems/annotate
(s/explain-data :highlighted-value/house {:address {:city 1}})))))))
(is (= "{:address {\"city\" \"Denver\"}}\n ^^^^^^^^^^^^^^^^^"
(printer/highlighted-value
{}
(first
(:expound/problems
(problems/annotate
(s/explain-data :highlighted-value/house {:address {"city" "Denver"}})))))))
(is (= "{\"address\" {:city \"Denver\"}}\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
(printer/highlighted-value
{}
(first
(:expound/problems
(problems/annotate
(s/explain-data :highlighted-value/house {"address" {:city "Denver"}})))))))))
(deftest highlighted-value-on-alt
(is (= "[... 0]\n ^"
(printer/highlighted-value
{}
(first
(:expound/problems
(problems/annotate
(s/explain-data
(clojure.spec.alpha/alt :a int?
:b (clojure.spec.alpha/spec (clojure.spec.alpha/cat :c int?)))
[1 0]))))))))
(deftest highlighted-value-on-coll-of
;; sets
(is (= "#{1 3 2 :a}\n ^^"
(printer/highlighted-value
{}
(first
(:expound/problems
(problems/annotate
(s/explain-data
(s/coll-of integer?)
#{1 :a 2 3})))))))
(is (= "#{:a}\n ^^"
(printer/highlighted-value
{}
(first
(:expound/problems
(problems/annotate
(s/explain-data
(s/coll-of integer?)
#{:a})))))))
;; lists
(is (= "(... :a ... ...)\n ^^"
(printer/highlighted-value
{}
(first
(:expound/problems
(problems/annotate
(s/explain-data
(s/coll-of integer?)
'(1 :a 2 3))))))))
(is (= "(:a)\n ^^"
(printer/highlighted-value
{}
(first
(:expound/problems
(problems/annotate
(s/explain-data
(s/coll-of integer?)
'(:a))))))))
;; vectors
(is (= "[... :a ... ...]\n ^^"
(printer/highlighted-value
{}
(first
(:expound/problems
(problems/annotate
(s/explain-data
(s/coll-of integer?)
[1 :a 2 3])))))))
(is (= "[:a]\n ^^"
(printer/highlighted-value
{}
(first
(:expound/problems
(problems/annotate
(s/explain-data
(s/coll-of integer?)
[:a])))))))
;; maps
(is (= "[1 :a]\n^^^^^^"
(printer/highlighted-value
{}
(first
(:expound/problems
(problems/annotate
(s/explain-data
(s/coll-of integer?)
{1 :a 2 3})))))))
(is (= "[:a 1]\n^^^^^^"
(printer/highlighted-value
{}
(first
(:expound/problems
(problems/annotate
(s/explain-data
(s/coll-of integer?)
{:a 1}))))))))