Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace reader tags with functions #14

Merged
merged 4 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 28 additions & 22 deletions docs/approximately-equal.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,77 +53,83 @@ aren't. In this case, it returns something like

This is printed in the correct place by humanized test output and other things that can print diffs.

## Reader tags:
## Built-in functions for other `=?` behaviors:

### `#hawk/exactly`
Built-in functions for `=?` are defined in the `mb.hawk.assert-exprs.approximately-equal` namespace. You can create
an alias for this namespace like so:
```clj
(require '[mb.hawk.assert-exprs.approximately-equal :as =?])
```

### `exactly`

`#hawk/exactly` means results have to be exactly equal as if by `=`. Use this to get around the normal way `=?` would
`exactly` means 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.

```clj
(is (=? {:m #hawk/exactly {:a 1}}
(is (=? {:m (=?/exactly {:a 1})}
{:m {:a 1, :b 2}}))
;; =>
expected: {:m #hawk/exactly {:a 1}}
expected: {:m (exactly {:a 1})}

actual: {:m {:a 1, :b 2}}
diff: - {:m (not (= #hawk/exactly {:a 1} {:a 1, :b 2}))}
diff: - {:m (not (= (exactly {:a 1}) {:a 1, :b 2}))}
+ nil
```

### `#hawk/schema`
### `schema`

`#hawk/schema` compares things to a `schema.core` Schema:
`schema` compares things to a `schema.core` Schema:

```clj
(is (=? {:a 1, :b #hawk/schema {s/Keyword s/Int}}
(is (=? {:a 1, :b (=?/schema {s/Keyword s/Int})}
{:a 1, :b {:c 2}}))
=> ok

(is (=? {:a 1, :b #hawk/schema {s/Keyword s/Int}}
(is (=? {:a 1, :b (=?/schema {s/Keyword s/Int})}
{:a 1, :b {:c 2.0}}))
=>
expected: {:a 1, :b #hawk/schema {(pred keyword?) (pred integer?)}}
expected: {:a 1, :b (schema {(pred keyword?) (pred integer?)})}

actual: {:a 1, :b {:c 2.0}}
diff: - {:b {:c (not (integer? 2.0))}}
+ nil
```

### `#hawk/malli`
### `malli`

`#hawk/malli` compares things to a `malli` schema:
`malli` compares things to a `malli` schema:

```clj
(is (=? {:a 1, :b #hawk/malli [:map-of :keyword :int]}
(is (=? {:a 1, :b (=?/malli [:map-of :keyword :int])}
{:a 1, :b {:c 2}}))
=> ok

(is (=? {:a 1, :b #hawk/malli [:map-of :keyword :int]}
(is (=? {:a 1, :b (=?/malli [:map-of :keyword :int])}
{:a 1, :b {:c 2.0}}))
=>
expected: {:a 1, :b #hawk/malli [:map-of :keyword :int]}
expected: {:a 1, :b (malli [:map-of :keyword :int])}
actual: {:a 1, :b {:c 2.0}}
diff: - {:b {:c ["should be an integer"]}}

```

### `#hawk/approx`
### `approx`

`#hawk/approx` compares whether two numbers are approximately equal:
`approx` compares whether two numbers are approximately equal:

```clj
;; is the difference between actual and 1.5 less than ±0.1?
(is (=? #hawk/approx [1.5 0.1]
(is (=? (=?/approx [1.5 0.1])
1.51))
=> true

(is (=? #hawk/approx [1.5 0.1]
(is (=? (=?/approx [1.5 0.1])
1.6))
=>
expected: #hawk/approx [1.5 0.1]
expected: (approx [1.5 0.1])

actual: 1.6
diff: - (not (approx= 1.5 1.6 #_epsilon 0.1))
diff: - (not (approx 1.5 1.6 #_epsilon 0.1))
+ nil
```
4 changes: 0 additions & 4 deletions resources/data_readers.clj

This file was deleted.

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)))))
Loading