|
1 | 1 | (ns provisdom.test.core |
2 | 2 | #?(:cljs (:require-macros |
3 | | - [provisdom.test.core] |
4 | | - [provisdom.test.assertions.cljs])) |
| 3 | + [provisdom.test.assertions.cljs] |
| 4 | + [provisdom.test.core])) |
5 | 5 | (:require |
6 | 6 | [clojure.pprint :as pprint] |
7 | 7 | [clojure.set :as set] |
8 | | - #?(:clj [clojure.test :as t] |
9 | | - :cljs [cljs.test :as t]) |
10 | | - [clojure.string :as str] |
11 | 8 | [clojure.spec.alpha :as s] |
12 | | - [clojure.spec.test.alpha :as st] |
13 | 9 | [clojure.spec.gen.alpha :as gen] |
14 | | - #?(:clj [provisdom.test.spec-check :as p.st]) |
15 | | - #?(:cljs [orchestra-cljs.spec.test]) |
16 | | - |
17 | | - ;; We require clojure.test for some CLJS code, in order for spec-check to run. |
| 10 | + #_{:clj-kondo/ignore [:unused-namespace]} |
| 11 | + [clojure.spec.test.alpha :as st] |
| 12 | + [clojure.string :as str] |
| 13 | + #?(:clj [clojure.test :as t] |
| 14 | + :cljs [cljs.test :as t]) |
18 | 15 | #?(:cljs [clojure.test.check]) |
19 | | - #?(:cljs [clojure.test.check.properties])) |
| 16 | + [clojure.test.check.properties :as prop] |
| 17 | + #?(:cljs [orchestra-cljs.spec.test]) |
| 18 | + #?(:clj [provisdom.test.spec-check :as p.st])) |
20 | 19 | #?(:clj (:import (clojure.lang ExceptionInfo) |
21 | 20 | (java.io FileNotFoundException Closeable)))) |
22 | 21 |
|
|
64 | 63 | CLJS Note: Always returns `true` on ClojureScript because the internal instrumented-vars atom is |
65 | 64 | private and inaccessible. This means [[with-instrument]] won't unstrument functions on CLJS - |
66 | 65 | they remain instrumented. This is usually acceptable for test code." |
67 | | - [sym] |
| 66 | + [_sym] |
68 | 67 | #?(:clj (let [instrumented-vars @(var-get #'st/instrumented-vars)] |
69 | | - (contains? instrumented-vars (resolve sym))) |
| 68 | + (contains? instrumented-vars (resolve _sym))) |
70 | 69 | :cljs true)) |
71 | 70 |
|
72 | 71 | (defn collectionize |
|
96 | 95 | (defmacro with-instrument* |
97 | 96 | [instrument-args & body] |
98 | 97 | `(with-instrument-impl |
99 | | - {:sym-or-syms ~(first instrument-args) |
100 | | - :f (fn [] ~@body) |
101 | | - :opts ~(second instrument-args)})) |
| 98 | + {:f (fn [] ~@body) |
| 99 | + :opts ~(second instrument-args) |
| 100 | + :sym-or-syms ~(first instrument-args)})) |
102 | 101 |
|
103 | 102 | (defmacro with-instrument |
104 | 103 | "Enables instrumentation for `sym-or-syms` while executing `body`. Once `body` has completed, |
|
154 | 153 | (when-not valid? |
155 | 154 | (swap! invalid-store assoc og-spec x)) |
156 | 155 | valid?)) |
157 | | - {:spec spec |
| 156 | + {:form form |
158 | 157 | :overrides overrides |
159 | 158 | :path path |
160 | | - :form form}) g 100) |
| 159 | + :spec spec}) g 100) |
161 | 160 | (let [abbr (s/abbrev form)] |
162 | 161 | (throw (ex-info (str "Unable to construct gen at: " path " for: " abbr) |
163 | | - {::path path ::form form ::failure :no-gen}))))))) |
| 162 | + {::failure :no-gen, ::form form, ::path path}))))))) |
164 | 163 |
|
165 | 164 | (defmacro deftest |
166 | 165 | "Re-export `clojure.test/deftest` for convenience when using `[provisdom.test.core :as t]`." |
|
222 | 221 | (let [do-report-sym (if (cljs-env? &env) 'cljs.test/do-report 'clojure.test/do-report)] |
223 | 222 | `(try |
224 | 223 | ~@body |
225 | | - (~do-report-sym {:type :fail |
226 | | - :message "Expected exception to be thrown" |
| 224 | + (~do-report-sym {:actual nil |
227 | 225 | :expected '~expected-data |
228 | | - :actual nil}) |
| 226 | + :message "Expected exception to be thrown" |
| 227 | + :type :fail}) |
229 | 228 | (catch ~(if (cljs-env? &env) :default 'ExceptionInfo) e# |
230 | 229 | (let [actual-data# (ex-data e#) |
231 | 230 | matches?# (every? (fn [[k# v#]] |
232 | 231 | (= v# (get actual-data# k#))) |
233 | 232 | ~expected-data)] |
234 | 233 | (if matches?# |
235 | | - (~do-report-sym {:type :pass |
| 234 | + (~do-report-sym {:actual actual-data# |
| 235 | + :expected '~expected-data |
236 | 236 | :message nil |
| 237 | + :type :pass}) |
| 238 | + (~do-report-sym {:actual actual-data# |
237 | 239 | :expected '~expected-data |
238 | | - :actual actual-data#}) |
239 | | - (~do-report-sym {:type :fail |
240 | 240 | :message "Exception data did not match expected" |
241 | | - :expected '~expected-data |
242 | | - :actual actual-data#})) |
| 241 | + :type :fail})) |
243 | 242 | matches?#))))) |
244 | 243 |
|
245 | 244 | (defn midje-just |
|
353 | 352 | (= a ::missing) false |
354 | 353 | (number? e) (apply approx= e a (mapcat identity approx=-opts)) |
355 | 354 | :else (= e a))] |
356 | | - {:path path :expected e :actual a :equal? equal?}))) |
| 355 | + {:actual a |
| 356 | + :equal? equal? |
| 357 | + :expected e |
| 358 | + :path path}))) |
357 | 359 | (remove :equal?))))) |
358 | 360 |
|
359 | 361 | (defmacro is-data-approx= |
|
374 | 376 | actual# ~x2 |
375 | 377 | result# (~data-approx=-sym expected# actual# ~@opts)] |
376 | 378 | (if result# |
377 | | - (~do-report-sym {:type :pass :message nil :expected expected# :actual actual#}) |
| 379 | + (~do-report-sym {:actual actual# |
| 380 | + :expected expected# |
| 381 | + :message nil |
| 382 | + :type :pass}) |
378 | 383 | (let [diffs# (data-diff expected# actual# ~@opts) |
379 | 384 | diff-str# (with-out-str |
380 | 385 | (doseq [{:keys [~'path ~'expected ~'actual]} diffs#] |
381 | 386 | (println " Path:" ~'path) |
382 | 387 | (println " expected:" ~'expected) |
383 | 388 | (println " actual: " ~'actual)))] |
384 | | - (~do-report-sym {:type :fail |
| 389 | + (~do-report-sym {:actual actual# |
| 390 | + :expected expected# |
385 | 391 | :message (str "Data structures differ at " (count diffs#) |
386 | 392 | " path(s):\n" diff-str#) |
387 | | - :expected expected# |
388 | | - :actual actual#}))) |
| 393 | + :type :fail}))) |
389 | 394 | result#)))) |
390 | 395 |
|
391 | 396 | #?(:clj (defmethod t/assert-expr 'just |
|
394 | 399 | actual# ~(nth form 2) |
395 | 400 | result# (midje-just expected# actual#)] |
396 | 401 | (if result# |
397 | | - (t/do-report {:type :pass |
398 | | - :message ~msg |
| 402 | + (t/do-report {:actual actual# |
399 | 403 | :expected '~form |
400 | | - :actual actual#}) |
401 | | - (t/do-report {:type :fail |
402 | 404 | :message ~msg |
| 405 | + :type :pass}) |
| 406 | + (t/do-report {:actual actual# |
403 | 407 | :expected '~(nth form 1) |
404 | | - :actual actual#})) |
| 408 | + :message ~msg |
| 409 | + :type :fail})) |
405 | 410 | result#))) |
406 | 411 |
|
407 | 412 | (def ^:dynamic *default-spec-check-opts* {}) |
|
414 | 419 | (def quick-check-stc-keys |
415 | 420 | [:num-tests :seed :max-size :reporter-fn]) |
416 | 421 |
|
| 422 | +#_{:clj-kondo/ignore [:unused-private-var]} |
417 | 423 | (defn- normalize-spec-test-opts |
418 | 424 | [opts] |
419 | 425 | (let [base-opts (merge *default-spec-check-opts* opts)] |
|
477 | 483 | shrunk-args (first (:smallest shrunk)) |
478 | 484 | failing-args (or shrunk-args original-args) |
479 | 485 | spec-error (-> (:result-data test-check-ret) |
480 | | - :clojure.test.check.properties/error) |
| 486 | + ::prop/error) |
481 | 487 | timeout? (and (instance? ExceptionInfo (:result test-check-ret)) |
482 | 488 | (contains? (ex-data (:result test-check-ret)) |
483 | | - :provisdom.test.spec-check/timeout)) |
| 489 | + #?(:clj ::p.st/timeout |
| 490 | + :cljs :provisdom.test.spec-check/timeout))) |
484 | 491 | spec-error? (fn [ex] |
485 | 492 | (and (instance? ExceptionInfo ex) |
486 | 493 | (::s/failure (ex-data ex)))) |
|
493 | 500 | spec-error-map (fn [ex] |
494 | 501 | (let [spec-error-message (ex-message ex) |
495 | 502 | explain-data (ex-data ex)] |
496 | | - {:type :fail |
| 503 | + {:actual "n/a" |
497 | 504 | :expected "n/a" |
498 | | - :actual "n/a" |
499 | 505 | :message (str spec-error-message " (seed: " seed ")\n" |
500 | 506 | pass-info |
501 | 507 | shrink-info |
502 | 508 | "\n" |
503 | 509 | (with-out-str (s/explain-out explain-data)) "\n" |
504 | 510 | "Args:\n\n" |
505 | 511 | (with-out-str (pprint/pprint failing-args)) |
506 | | - "---------------")}))] |
| 512 | + "---------------") |
| 513 | + :type :fail}))] |
507 | 514 | (swap! failed-args-store assoc fn-sym failing-args) |
508 | 515 | (cond |
509 | 516 | ;; Timeout |
510 | 517 | timeout? |
511 | | - {:type :fail |
| 518 | + {:actual (:result test-check-ret) |
512 | 519 | :expected "" |
513 | | - :actual (:result test-check-ret) |
514 | | - :message (str "Spec check timed out.\n" pass-info)} |
| 520 | + :message (str "Spec check timed out.\n" pass-info) |
| 521 | + :type :fail} |
515 | 522 |
|
516 | 523 | (spec-error? spec-error) |
517 | 524 | (spec-error-map spec-error) |
|
522 | 529 |
|
523 | 530 | ;; Generator threw an exception |
524 | 531 | (instance? #?(:clj Throwable :cljs js/Error) (:failure first-failure)) |
525 | | - {:type :fail |
| 532 | + {:actual (:failure first-failure) |
526 | 533 | :expected "" |
527 | | - :actual (:failure first-failure) |
528 | | - :message (str "A generator threw an exception.\n" pass-info)} |
| 534 | + :message (str "A generator threw an exception.\n" pass-info) |
| 535 | + :type :fail} |
529 | 536 |
|
530 | | - :else {:type :fail |
| 537 | + :else {:actual spec-error |
531 | 538 | :expected "" |
532 | | - :actual spec-error |
533 | | - :message (str fn-sym " threw an exception.\n" pass-info)})) |
| 539 | + :message (str fn-sym " threw an exception.\n" pass-info) |
| 540 | + :type :fail})) |
534 | 541 | ;; all checks passed |
535 | 542 | (do |
536 | 543 | (swap! failed-args-store (fn [failed] |
537 | 544 | (apply dissoc failed (map :sym check-results)))) |
538 | | - {:type :pass |
539 | | - :message (str "Generative tests pass for " |
540 | | - (str/join ", " (map :sym check-results)))})))) |
| 545 | + {:message (str "Generative tests pass for " |
| 546 | + (str/join ", " (map :sym check-results))) |
| 547 | + :type :pass})))) |
541 | 548 |
|
542 | 549 | #?(:clj |
543 | 550 | (defn- fully-qualified-namespace |
|
0 commit comments