Skip to content

Commit

Permalink
Address review comments, support expanded defn specs
Browse files Browse the repository at this point in the history
  • Loading branch information
thumbnail committed Jun 6, 2019
1 parent ee0cb6a commit e290250
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 42 deletions.
27 changes: 1 addition & 26 deletions src/nedap/utils/spec/api.cljc
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
(ns nedap.utils.spec.api
(:require
#?(:clj [clojure.spec.alpha :as spec] :cljs [cljs.spec.alpha :as spec])
#?(:clj [clojure.test :as test])
[nedap.utils.spec.impl.check]
[spec-coerce.core :as coerce])
#?(:cljs (:require-macros [nedap.utils.spec.api :refer [check!]]))
#?(:clj (:import (clojure.lang ExceptionInfo))))
#?(:cljs (:require-macros [nedap.utils.spec.api :refer [check!]])))

#?(:clj
(defmacro check!
Expand All @@ -29,26 +27,3 @@
(let [m (coerce/coerce spec m)]
(cond-> m
(not (spec/valid? spec m)) (assoc ::invalid? true))))

#?(:clj
(defmethod test/assert-expr 'spec-assertion-thrown? [msg form]
;; (is (check-violated? s expr))
;; Asserts that evaluating expr throws an ExceptionInfo related to spec-symbol s.
;; Returns the exception thrown.
(let [spec-sym (second form)
body (nthnext form 2)]
`(try
(with-out-str ; silence output
~@body)
(test/do-report {:type :fail, :message ~msg
:expected '~spec-sym, :actual nil})
(catch ExceptionInfo e#
(if-let [spec# (:spec (ex-data e#))]
(do
(if (= spec# ~spec-sym)
(test/do-report {:type :pass, :message ~msg
:expected '~spec-sym, :actual nil})
(test/do-report {:type :fail, :message ~msg
:expected '~spec-sym, :actual spec#}))
e#)
(throw e#)))))))
24 changes: 24 additions & 0 deletions src/nedap/utils/spec/impl/spec_assertion.cljc
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
(ns nedap.utils.spec.impl.spec-assertion
(:require
#?(:cljs [cljs.core :refer [ExceptionInfo]])
[clojure.test :as test]
[nedap.utils.spec.impl.parsing :refer [infer-spec-from-symbol]])
#?(:clj (:import (clojure.lang ExceptionInfo))))

(defn assert-spec-failure [msg spec-sym body]
`(try
(with-out-str ~@body) ; silently execute body
(test/do-report {:type :fail, :message ~msg :expected '~spec-sym, :actual nil})
(catch ExceptionInfo e#
(let [spec# (:spec (ex-data e#))
inferred-spec# (:spec (infer-spec-from-symbol true ~spec-sym))]

;; rethrow if no spec failure is found
(when-not [spec#]
(throw e#))

(if (or (= ~spec-sym spec#)
(= inferred-spec# spec#))
(test/do-report {:type :pass, :message ~msg :expected '~spec-sym, :actual nil})
(test/do-report {:type :fail, :message ~msg :expected '~spec-sym, :actual spec#}))
e#))))
14 changes: 13 additions & 1 deletion src/nedap/utils/speced.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
[nedap.utils.spec.impl.def-with-doc]
[nedap.utils.spec.impl.defn :as impl.defn]
[nedap.utils.spec.impl.defprotocol :as impl.defprotocol]
[nedap.utils.spec.impl.satisfies])
[nedap.utils.spec.impl.satisfies]
[nedap.utils.spec.impl.spec-assertion :as spec-assertion]
#?(:clj [clojure.test :as test]))
#?(:cljs (:require-macros [nedap.utils.speced :refer [def-with-doc]])))

#?(:clj
Expand Down Expand Up @@ -77,3 +79,13 @@
"Can be summed to an existing spec (also passed as metadata),
for indicating that that spec is nilable."
any?)


#?(:clj
(defmethod test/assert-expr 'spec-assertion-thrown? [msg form]
;; (is (spec-assertion-thrown? s expr))
;; Asserts that evaluating expr throws an ExceptionInfo related to spec-symbol s.
;; Returns the exception thrown.
(let [spec-sym (second form)
body (nthnext form 2)]
(spec-assertion/assert-spec-failure msg spec-sym body))))
15 changes: 0 additions & 15 deletions test/unit/nedap/utils/api/spec_assertion.clj

This file was deleted.

19 changes: 19 additions & 0 deletions test/unit/nedap/utils/speced/spec_assertion.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
(ns unit.nedap.utils.speced.spec-assertion
(:require [clojure.test :refer :all]
[nedap.utils.spec.api :as sut]
[nedap.utils.speced :as speced]
[clojure.spec.alpha :as s]))

(s/def ::age number?)
(speced/defn accepts-age [^::age x] x)
(speced/defn ^::age returns-age [x] x)
(speced/defn accepts-number [^number? x] x)
(speced/defn ^number? returns-number [x] x)

(deftest spec-assertion-thrown?-defmethod
(is (spec-assertion-thrown? 'string? (sut/check! string? 123)))
(is (spec-assertion-thrown? 'number? (sut/check! number? "123")))
(is (spec-assertion-thrown? ::age (accepts-age "1234")))
(is (spec-assertion-thrown? ::age (returns-age "1234")))
(is (spec-assertion-thrown? 'number? (accepts-number "1234")))
(is (spec-assertion-thrown? 'number? (returns-number "1234"))))

0 comments on commit e290250

Please sign in to comment.