Skip to content

Commit

Permalink
Replace reader macros with functions
Browse files Browse the repository at this point in the history
  • Loading branch information
calherries committed Nov 10, 2023
1 parent 93495c6 commit 4928715
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 76 deletions.
51 changes: 26 additions & 25 deletions src/mb/hawk/assert_exprs/approximately_equal.clj
Original file line number Diff line number Diff line change
Expand Up @@ -107,50 +107,51 @@

(deftype Exactly [expected])

(defn read-exactly
"Data reader for `#hawk/exactly`."
[expected-form]
`(->Exactly ~expected-form))
(defn exactly
"Used inside a =? expression. Results have to be exactly equal as if by =. Use this to get around the normal way =?
would compare things. This works inside collections as well."
[expected]
(->Exactly expected))

(defmethod print-method Exactly
[this writer]
((get-method print-dup Exactly) this writer))

(defmethod print-dup Exactly
[^Exactly this ^java.io.Writer writer]
(.write writer (format "#hawk/exactly %s" (pr-str (.expected this)))))
(.write writer (format "(exactly %s)" (pr-str (.expected this)))))

(defmethod pprint/simple-dispatch Exactly
[^Exactly this]
(pprint/pprint-logical-block
:prefix "#hawk/exactly " :suffix nil
:prefix "(exactly " :suffix ")"
(pprint/write-out (.expected this))))

(methodical/defmethod =?-diff [Exactly :default]
[^Exactly this actual]
(let [expected (.expected this)]
(when-not (= expected actual)
(list 'not (list '= (symbol "#hawk/exactly") expected actual)))))
(list 'not (list '= (list 'exactly expected) actual)))))

(deftype Schema [schema])

(defn read-schema
"Data reader for `#hawk/schema`."
[schema-form]
`(->Schema ~schema-form))
(defn schema
"Used inside a =? expression. Compares things to a schema.core schema."
[schema]
(->Schema schema))

(defmethod print-method Schema
[this writer]
((get-method print-dup Schema) this writer))

(defmethod print-dup Schema
[^Schema this ^java.io.Writer writer]
(.write writer (format "#hawk/schema %s" (pr-str (.schema this)))))
(.write writer (format "(schema %s)" (pr-str (.schema this)))))

(defmethod pprint/simple-dispatch Schema
[^Schema this]
(pprint/pprint-logical-block
:prefix "#hawk/schema " :suffix nil
:prefix "(malli " :suffix ")"
(pprint/write-out (.schema this))))

(methodical/defmethod =?-diff [Schema :default]
Expand All @@ -159,14 +160,14 @@

(deftype Malli [schema])

(defn read-malli
"Data reader for `#hawk/malli`."
[schema-form]
`(->Malli ~schema-form))
(defn malli
"Used inside a =? expression. Compares things to a malli schema."
[schema]
(->Malli schema))

(defmethod print-dup Malli
[^Malli this ^java.io.Writer writer]
(.write writer (format "#hawk/malli %s" (pr-str (.schema this)))))
(.write writer (format "(malli %s)" (pr-str (.schema this)))))

(defmethod print-method Malli
[this writer]
Expand All @@ -175,7 +176,7 @@
(defmethod pprint/simple-dispatch Malli
[^Malli this]
(pprint/pprint-logical-block
:prefix "#hawk/malli " :suffix nil
:prefix "(malli " :suffix ")"
(pprint/write-out (.schema this))))

(methodical/defmethod =?-diff [Malli :default]
Expand All @@ -184,11 +185,11 @@

(deftype Approx [expected epsilon])

(defn read-approx
"Data reader for `#hawk/approx`."
(defn approx
"Used inside a =? expression. Compares whether two numbers are approximately equal."
[form]
(let [form (eval form)
_ (assert (sequential? form) "Expected #hawk/approx [expected epsilon]")
_ (assert (sequential? form) "Expected (approx [expected epsilon])")
[expected epsilon] form]
(assert (number? expected))
(assert (number? epsilon))
Expand All @@ -200,17 +201,17 @@

(defmethod print-dup Approx
[^Approx this ^java.io.Writer writer]
(.write writer (format "#hawk/approx %s" (pr-str [(.expected this) (.epsilon this)]))))
(.write writer (format "(approx %s)" (pr-str [(.expected this) (.epsilon this)]))))

(defmethod pprint/simple-dispatch Approx
[^Approx this]
(pprint/pprint-logical-block
:prefix "#hawk/approx " :suffix nil
:prefix "(approx " :suffix ")"
(pprint/write-out [(.expected this) (.epsilon this)])))

(methodical/defmethod =?-diff [Approx Number]
[^Approx this actual]
(let [expected (.expected this)
epsilon (.epsilon this)]
(when-not (algo.generic.math/approx= expected actual epsilon)
(list 'not (list 'approx= expected actual (symbol "#_epsilon") epsilon)))))
(list 'not (list 'approx expected actual (symbol "#_epsilon") epsilon)))))
102 changes: 51 additions & 51 deletions test/mb/hawk/assert_exprs/approximately_equal_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[clojure.test :refer :all]
[malli.core :as m]
[mb.hawk.assert-exprs :as test-runner.assert-exprs]
[mb.hawk.assert-exprs.approximately-equal :as approximately-equal]
[mb.hawk.assert-exprs.approximately-equal :as =?]
[schema.core :as s]))

(comment test-runner.assert-exprs/keep-me)
Expand Down Expand Up @@ -43,21 +43,21 @@
[:a 100]))
(testing "Should enforce that sequences are of the same length"
(is (= [nil nil (list 'not= nil? nil)]
(approximately-equal/=?-diff [int? string? nil?]
[1 "two"])))
(=?/=?-diff [int? string? nil?]
[1 "two"])))
(is (= [nil nil (list 'not= nil? nil) (list 'not= nil "cans")]
(approximately-equal/=?-diff [int? string? nil?]
[1 "two" nil "cans"])))
(=?/=?-diff [int? string? nil?]
[1 "two" nil "cans"])))
(testing "Differentiate between [1 2 nil] and [1 2]"
;; these are the same answers [[clojure.data/diff]] would give in these situations. The output is a little more
;; obvious in failing tests because you can see the difference between expected and actual in addition to this
;; diff.
(is (= [nil nil nil]
(approximately-equal/=?-diff [1 2 nil]
[1 2])))
(=?/=?-diff [1 2 nil]
[1 2])))
(is (= [nil nil nil]
(approximately-equal/=?-diff [1 2]
[1 2 nil]))))))
(=?/=?-diff [1 2]
[1 2 nil]))))))

(deftest ^:parallel custom-approximately-equal-methods
(is (=? {[String java.time.LocalDate]
Expand All @@ -76,99 +76,99 @@
{:a "abc", :b 100})))

(deftest ^:parallel exactly-test
(testing "#hawk/exactly"
(testing "=?/exactly"
(is (=? {:a 1}
{:a 1, :b 2}))
(testing "Fail when things are not exactly the same, as if by `=`"
;; these serialize the results to strings because it makes it easier to see what the output will look like when
;; printed out which is what we actually care about.
(is (= "(not (= #hawk/exactly {:a 1} {:a 1, :b 2}))"
(pr-str (approximately-equal/=?-diff #hawk/exactly {:a 1} {:a 1, :b 2}))))
(is (= "(not (= (exactly {:a 1}) {:a 1, :b 2}))"
(pr-str (=?/=?-diff (=?/exactly {:a 1}) {:a 1, :b 2}))))
(testing "Inside a map"
(is (= "{:b (not (= #hawk/exactly {:a 1} {:a 1, :b 2}))}"
(pr-str (approximately-equal/=?-diff {:a 1, :b #hawk/exactly {:a 1}}
{:a 1, :b {:a 1, :b 2}}))))))
(is (= "{:b (not (= (exactly {:a 1}) {:a 1, :b 2}))}"
(pr-str (=?/=?-diff {:a 1, :b (=?/exactly {:a 1})}
{:a 1, :b {:a 1, :b 2}}))))))
(testing "Should pass when things are exactly the same as if by `=`"
(is (nil? (approximately-equal/=?-diff #hawk/exactly 2 2)))
(is (=? #hawk/exactly 2
(is (nil? (=?/=?-diff (=?/exactly 2) 2)))
(is (=? (=?/exactly 2)
2))
(testing "should evaluate args"
(is (=? #hawk/exactly (+ 1 1)
(is (=? (=?/exactly (+ 1 1))
2)))
(testing "Inside a map"
(is (=? {:a 1, :b #hawk/exactly 2}
(is (=? {:a 1, :b (=?/exactly 2)}
{:a 1, :b 2}))))))

(deftest ^:parallel schema-test
(testing "#hawk/schema"
(is (=? #hawk/schema {:a s/Int}
(testing "=?/schema"
(is (=? (=?/schema {:a s/Int})
{:a 1}))
(testing "Nested inside a collection"
(is (=? {:a 1, :b #hawk/schema {s/Keyword s/Int}}
(is (=? {:a 1, :b (=?/schema {s/Keyword s/Int})}
{:a 1, :b {}}))
(is (=? {:a 1, :b #hawk/schema {s/Keyword s/Int}}
(is (=? {:a 1, :b (=?/schema {s/Keyword s/Int})}
{:a 1, :b {:c 2}}))
(is (=? {:a 1, :b #hawk/schema {s/Keyword s/Int}}
(is (=? {:a 1, :b (=?/schema {s/Keyword s/Int})}
{:a 1, :b {:c 2, :d 3}})))
(testing "failures"
;; serialize these to strings and read them back out because Schema actually returns weird classes like
;; ValidationError or whatever that aren't equal to their printed output
(is (= '{:a (not (integer? 1.0))}
(read-string (pr-str (approximately-equal/=?-diff #hawk/schema {:a s/Int} {:a 1.0})))))
(read-string (pr-str (=?/=?-diff (=?/schema {:a s/Int}) {:a 1.0})))))
(testing "Inside a collection"
(is (= '{:b {:c (not (integer? 2.0))}}
(read-string (pr-str (approximately-equal/=?-diff {:a 1, :b #hawk/schema {:c s/Int}}
{:a 1, :b {:c 2.0}})))))))))
(read-string (pr-str (=?/=?-diff {:a 1, :b (=?/schema {:c s/Int})}
{:a 1, :b {:c 2.0}})))))))))

(deftest ^:parallel malli-test
(testing "#hawk/malli"
(is (=? #hawk/malli [:map [:a :int]]
(testing "=?/malli"
(is (=? (=?/malli [:map [:a :int]])
{:a 1}))
(testing "Nested inside a collection"
(is (=? {:a 1, :b #hawk/malli [:map-of :keyword :int]}
(is (=? {:a 1, :b (=?/malli [:map-of :keyword :int])}
{:a 1, :b {}}))
(is (=? {:a 1, :b #hawk/malli [:map-of :keyword :int]}
(is (=? {:a 1, :b (=?/malli [:map-of :keyword :int])}
{:a 1, :b {:c 2}}))
(is (=? {:a 1, :b #hawk/malli [:map-of :keyword :int]}
(is (=? {:a 1, :b (=?/malli [:map-of :keyword :int])}
{:a 1, :b {:c 2, :d 3}})))
(testing "failures"
(is (= '{:a ["should be an integer"]}
(read-string (pr-str (approximately-equal/=?-diff #hawk/malli [:map [:a :int]] {:a 1.0})))))
(read-string (pr-str (=?/=?-diff (=?/malli [:map [:a :int]]) {:a 1.0})))))
(testing "Inside a collection"
(is (= '{:b {:c ["should be an integer"]}}
(read-string (pr-str (approximately-equal/=?-diff {:a 1, :b #hawk/malli [:map [:c :int]]}
{:a 1, :b {:c 2.0}})))))))
(read-string (pr-str (=?/=?-diff {:a 1, :b (=?/malli [:map [:c :int]])}
{:a 1, :b {:c 2.0}})))))))
(testing "Doesn't double evaluate functions (#12)"
(let [schema #hawk/malli [:map [:k map?]]]
(let [schema (=?/malli [:map [:k map?]])]
(is (identical? map? (-> (.schema schema) last last))
"Got a double compiled function in schema")
(is (m/validate (.schema schema) {:k {}}))))))


(deftest ^:parallel approx-test
(testing "#hawk/approx"
(is (=? #hawk/approx [1.5 0.1]
(testing "=?/approx"
(is (=? (=?/approx [1.5 0.1])
1.51))
(testing "Nested inside a collection"
(is (=? {:a 1, :b #hawk/approx [1.5 0.1]}
(is (=? {:a 1, :b (=?/approx [1.5 0.1])}
{:a 1, :b 1.51})))
;; failures below render stuff to strings so we can see it the way it will look in test failures with its nice
;; comment and whatnot
(testing "failures"
(is (= "(not (approx= 1.5 1.6 #_epsilon 0.1))"
(pr-str (approximately-equal/=?-diff #hawk/approx [1.5 0.1] 1.6))))
(is (= "(not (approx 1.5 1.6 #_epsilon 0.1))"
(pr-str (=?/=?-diff (=?/approx [1.5 0.1]) 1.6))))
(testing "Inside a collection"
(is (= "{:b (not (approx= 1.5 1.6 #_epsilon 0.1))}"
(pr-str (approximately-equal/=?-diff {:a 1, :b #hawk/approx [1.5 0.1]}
{:a 1, :b 1.6}))))))
(is (= "{:b (not (approx 1.5 1.6 #_epsilon 0.1))}"
(pr-str (=?/=?-diff {:a 1, :b (=?/approx [1.5 0.1])}
{:a 1, :b 1.6}))))))
(testing "Eval the args"
(is (=? #hawk/approx [(+ 1.0 0.5) (- 1.0 0.9)]
(is (=? (=?/approx [(+ 1.0 0.5) (- 1.0 0.9)])
1.51)))
(testing "A large epsilon"
(is (=? #hawk/approx [1 10.0]
(is (=? (=?/approx [1 10.0])
9.0))
(is (= "(not (approx= 1 20.0 #_epsilon 10.0))"
(pr-str (approximately-equal/=?-diff #hawk/approx [1 10.0] 20.0)))))
(testing "nil should not match the #hawk/approx method -- fall back to the :default"
(is (= "(not= #hawk/approx [1 0.1] nil)"
(pr-str (approximately-equal/=?-diff #hawk/approx [1 0.1] nil)))))))
(is (= "(not (approx 1 20.0 #_epsilon 10.0))"
(pr-str (=?/=?-diff (=?/approx [1 10.0]) 20.0)))))
(testing "nil should not match the =?/approx method -- fall back to the :default"
(is (= "(not= (approx [1 0.1]) nil)"
(pr-str (=?/=?-diff (=?/approx [1 0.1]) nil)))))))

0 comments on commit 4928715

Please sign in to comment.