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
This commit is contained in:
Gabriel Horner 2021-12-23 15:26:01 -05:00
parent c0588be686
commit d5b3f0f4f6
26 changed files with 1482 additions and 141 deletions

View file

@ -69,16 +69,16 @@
henryw374/cljc.java-time
{:git/url "https://github.com/henryw374/cljc.java-time.git"
:sha "e3d184b78e933322b3fcaa6ca66cbb8f42a6b35c"}
camel-snake-kebab/camel-snake-kebab {:mvn/version "0.4.1"}
camel-snake-kebab/camel-snake-kebab {:mvn/version "0.4.2"}
aero/aero {:mvn/version "1.1.6"}
org.clojure/data.generators {:mvn/version "1.0.0"}
honeysql/honeysql {:mvn/version "1.0.444"}
com.github.seancorfield/honeysql {:mvn/version "2.0.0-rc2"}
minimallist/minimallist {:mvn/version "0.0.6"}
circleci/bond {:mvn/version "0.4.0"}
version-clj/version-clj {:mvn/version "2.0.1"}
minimallist/minimallist {:mvn/version "0.0.10"}
circleci/bond {:mvn/version "0.6.0"}
version-clj/version-clj {:mvn/version "2.0.2"}
gaka/gaka {:mvn/version "0.3.0"}
failjure/failjure {:mvn/version "2.1.1"}
failjure/failjure {:mvn/version "2.2.0"}
io.helins/binf {:mvn/version "1.1.0-beta0"}
rm-hull/jasentaa {:mvn/version "0.2.5"}
slingshot/slingshot {:mvn/version "0.12.2"}

View file

@ -6,6 +6,7 @@
(:require [babashka.deps :as deps]
[babashka.fs :as fs]
[babashka.tasks :refer [shell]]
[org.httpkit.client :as http]
[clojure.string :as str]
[clojure.java.io :as io]
[clojure.tools.cli :as cli]
@ -53,7 +54,7 @@
(let [nodes (-> "deps.edn" slurp r/parse-string)]
(spit "deps.edn"
(str (r/assoc-in nodes
[:aliases :lib-tests :extra-deps (symbol lib-name)]
[:aliases :lib-tests :extra-deps lib-name]
lib-coordinate)))))
(defn- copy-tests
@ -102,14 +103,46 @@
lib)))
namespaces))
(defn- add-libtest*
[args options]
(let [[deps-string git-url] args
deps-map (edn/read-string deps-string)
(defn- fetch-artifact
[artifact]
(let [url (str "https://clojars.org/api/artifacts/" artifact)
_ (println (str "GET " url "..."))
resp @(http/get url {:headers {"Accept" "application/edn"}})]
(if (= 200 (:status resp))
(-> resp :body slurp edn/read-string)
(error (str "Response failed and returned " (pr-str resp))))))
(defn- deps-to-lib-name-and-coordinate
[deps-string]
(let [deps-map (edn/read-string deps-string)
_ (when (not= 1 (count deps-map))
(error "Deps map must have one key"))
lib-name (ffirst deps-map)
lib-coordinate (deps-map lib-name)
lib-coordinate (deps-map lib-name)]
[lib-name lib-coordinate]))
(defn- get-lib-map
[deps-string options]
;; if deps-string is artifact name
(if (re-matches #"\S+/\S+" deps-string)
(let [artifact-edn (fetch-artifact deps-string)]
{:lib-name (symbol deps-string)
:lib-coordinate {:mvn/version (:latest_version artifact-edn)}
:git-url (or (:git-url options) (:homepage artifact-edn))})
(let [deps-map (edn/read-string deps-string)]
(when (not= 1 (count deps-map))
(error "Deps map must have one key"))
{:lib-name (ffirst deps-map)
:lib-coordinate (-> deps-map vals first)
:git-url (:git-url options)})))
(defn- add-libtest*
[args options]
(let [[artifact-or-deps-string] args
{:keys [lib-name lib-coordinate git-url]}
(get-lib-map artifact-or-deps-string options)
_ (when (nil? git-url)
(error "git-url is required. Please specify with --git-url"))
_ (add-lib-to-deps lib-name lib-coordinate)
dirs (copy-tests git-url lib-name options)
namespaces (add-lib-to-tested-libs lib-name git-url dirs options)]
@ -119,8 +152,8 @@
(defn add-libtest
[{:keys [arguments options summary]}]
(if (or (< (count arguments) 2) (:help options))
(print-summary "DEPS-MAP GIT-URL " summary)
(if (or (< (count arguments) 1) (:help options))
(print-summary "ARTIFACT-OR-DEPS-MAP " summary)
(add-libtest* arguments options)))
(def cli-options
@ -129,7 +162,8 @@
;; https://github.com/weavejester/environ/tree/master/environ used this option
["-d" "--directory DIRECTORY" "Directory where library is located"]
;; https://github.com/reifyhealth/specmonstah used this option
["-b" "--branch BRANCH" "Default branch for git url"]])
["-b" "--branch BRANCH" "Default branch for git url"]
["-g" "--git-url GITURL" "Git url for artifact. Defaults to homepage on clojars"]])
(when (= *file* (System/getProperty "babashka.file"))
(run-command add-libtest *command-line-args* cli-options))

View file

@ -13,7 +13,7 @@
goog.string.format
[cljs.tools.reader.reader-types
:refer [source-logging-push-back-reader]]]))
;; TODO:
;; BB-TEST-PATCH
#_#?(:clj (:import [aero.core Deferred])))
(defn env [s]
@ -38,6 +38,8 @@
(if (= value :favorite) :chocolate :vanilla))
(deftest basic-test
;; BB-TEST-PATCH: This and several other test files were changed to work with
;; our dir structure
(let [config (read-config "test-resources/lib_tests/aero/config.edn")]
(is (= "Hello World!" (:greeting config))))
(testing "Reading empty config returns nil"

View file

@ -0,0 +1,23 @@
(ns aero.lumo-test
(:require
aero.core-test
[cljs.test :refer-macros [deftest is testing run-tests]]))
(def resolve-p (atom nil))
(def p (new js/Promise (fn [resolve reject]
(reset! resolve-p resolve))))
(defmethod cljs.test/report [:cljs.test/default :end-run-tests]
[m]
(@resolve-p m))
(defn -main [& argv]
(println "Testing with lumo")
(run-tests 'aero.core-test)
(-> p
(.then (fn [m]
(.exit (js/require "process")
(if (cljs.test/successful? m)
0
1))))))

View file

@ -115,39 +115,10 @@
(test-doric-cyclic-dep-problem)
(test-namespaces 'doric.test.core))
;;;; cljc-java-time
(test-namespaces 'cljc.java-time-test)
;;;; camel-snake-kebab
(test-namespaces 'camel-snake-kebab.core-test)
;;;; aero
(test-namespaces 'aero.core-test)
;;;; clojure.data.generators
(test-namespaces 'clojure.data.generators-test)
;;;; honeysql
(test-namespaces 'honeysql.core-test 'honeysql.format-test)
;;;; minimallist
(test-namespaces 'minimallist.core-test)
;;;; bond
(test-namespaces 'bond.test.james)
;;;; version-clj
(test-namespaces 'version-clj.compare-test
'version-clj.core-test
'version-clj.split-test
'version-clj.via-use-test)
;;;; httpkit client
(test-namespaces 'httpkit.client-test)
@ -168,10 +139,6 @@
(test-namespaces 'test-check.smoke-test)
(test-namespaces 'gaka.core-test)
(test-namespaces 'failjure.test-core)
(test-namespaces 'rewrite-clj.parser-test
'rewrite-clj.node-test
'rewrite-clj.zip-test
@ -184,23 +151,10 @@
(test-namespaces 'selmer.core-test)
(test-namespaces 'selmer.our-test)
(test-namespaces 'jasentaa.position-test
'jasentaa.worked-example-1
'jasentaa.worked-example-2
'jasentaa.collections-test
'jasentaa.parser.basic-test
'jasentaa.parser.combinators-test)
(test-namespaces 'honey.sql-test
'honey.sql.helpers-test
'honey.sql.postgres-test)
(test-namespaces 'slingshot.slingshot-test
'slingshot.support-test
;; TODO:
;; 'slingshot.test-test
)
(test-namespaces 'omniconf.core-test)
(test-namespaces 'crispin.core-test)
@ -222,8 +176,6 @@
;; namespaces to see if they at least load correctly
(test-namespaces 'java-http-clj.smoke-test)
(test-namespaces 'component.component-test)
(test-namespaces 'clj-commons.digest-test)
(test-namespaces 'hato.client-test)

View file

@ -20,4 +20,18 @@
;; BB-TEST-PATCH: Removed markdown.md-file-test b/c tests hardcode path to test
;; files. Removed markdown.benchmark b/c it depends on criterium which isn't bb compatible
markdown-clj/markdown-clj {:git-sha "ac245d3049afa25a6d41fcb5ba5a268f52c610e4", :git-url "https://github.com/yogthos/markdown-clj", :test-namespaces (markdown.md-test)}
org.clojure/tools.namespace {:git-sha "a13b037215e21a2e71aa34b27e1dd52c801a2a7b", :git-url "https://github.com/babashka/tools.namespace", :test-namespaces (clojure.tools.namespace.test-helpers clojure.tools.namespace.dependency-test clojure.tools.namespace.find-test clojure.tools.namespace.move-test clojure.tools.namespace.parse-test clojure.tools.namespace.dir-test), :branch "babashka"}}
org.clojure/tools.namespace {:git-sha "a13b037215e21a2e71aa34b27e1dd52c801a2a7b", :git-url "https://github.com/babashka/tools.namespace", :test-namespaces (clojure.tools.namespace.test-helpers clojure.tools.namespace.dependency-test clojure.tools.namespace.find-test clojure.tools.namespace.move-test clojure.tools.namespace.parse-test clojure.tools.namespace.dir-test), :branch "babashka"}
com.stuartsierra/component {:git-sha "9f9653d1d95644e3c30beadf8c8811f86758ea23", :git-url "https://github.com/stuartsierra/component", :test-namespaces (com.stuartsierra.component-test)}
slingshot/slingshot {:git-sha "6961ab0593ab9633c15b7697ffd43823090720be", :git-url "https://github.com/scgilardi/slingshot", :test-namespaces (slingshot.slingshot-test slingshot.support-test slingshot.test-test)}
rm-hull/jasentaa {:git-sha "f52a0e75cbdf1d2b72d9604232db264ff6473f12", :git-url "https://github.com/rm-hull/jasentaa", :test-namespaces (jasentaa.position-test jasentaa.worked-example-2 jasentaa.collections-test jasentaa.parser.basic-test jasentaa.parser.combinators-test jasentaa.test-helpers jasentaa.worked-example-1)}
failjure/failjure {:git-sha "c6e528c1eda6ad5eaab0f1fb2a97e766bf41fdd5", :git-url "https://github.com/adambard/failjure", :test-namespaces (failjure.test-core)}
gaka/gaka {:git-sha "2f264758881d6dc586b948ca8757134675f542a7", :git-url "https://github.com/cdaddr/gaka", :test-namespaces (gaka.core-test)}
version-clj/version-clj {:git-sha "9d86cd870f7e435fd6d593cb689790a22d8040a6", :git-url "https://github.com/xsc/version-clj", :test-namespaces (version-clj.compare-test version-clj.split-test version-clj.core-test version-clj.via-use-test)}
circleci/bond {:git-sha "0d389cfb4628341824bddbe8bf102f15ad25ad0d", :git-url "https://github.com/circleci/bond", :test-namespaces (bond.assertions-test bond.james-test bond.target-data)}
;; BB-TEST-PATCH: minimallist.generator-test excluded because generator ns can't be required
minimallist/minimallist {:git-sha "f10ebbd3c2b93e7579295618a7ed1e870c489bc4", :git-url "https://github.com/green-coder/minimallist", :test-namespaces (minimallist.util-test minimallist.core-test), :branch "all-work-and-no-play"}
aero/aero {:git-sha "743e9bc495425b4a4a7c780f5e4b09f6680b4e7a", :git-url "http://github.com/juxt/aero", :test-namespaces (aero.core-test)}
org.clojure/data.generators {:git-sha "bf65f99aa9dcabed7de7c09b74d71db208cf61ee", :git-url "https://github.com/clojure/data.generators", :test-namespaces (clojure.data.generators-test)}
camel-snake-kebab/camel-snake-kebab {:git-sha "d072c7fd242ab0becd4bb265622ded415f2a4b68", :git-url "https://github.com/clj-commons/camel-snake-kebab", :test-namespaces (camel-snake-kebab.internals.string-separator-test camel-snake-kebab.extras-test camel-snake-kebab.core-test)}
;; BB-TEST-PATCH: Removed cljs-test-opts.edn
henryw374/cljc.java-time {:git-sha "b9da12ea25e80a0e284a5bffd88ebcbf18fc3bf7", :git-url "https://github.com/henryw374/cljc.java-time", :test-namespaces (cljc.java-time-test)}}

View file

@ -0,0 +1,121 @@
(ns bond.assertions-test
(:require [clojure.test :refer (deftest is testing)]
[bond.assertions :as assertions]
[bond.james :as bond :include-macros true]
[bond.target-data :as target]))
(deftest called?-works
(testing "a spy was called directly"
(bond/with-spy [target/foo]
(target/foo 1)
(is (assertions/called? target/foo))))
(testing "a spy was called indirectly"
(bond/with-spy [target/foo]
(target/foo-caller 1)
(is (assertions/called? target/foo))))
(testing "a spy was not called"
(bond/with-spy [target/foo]
(is (not (assertions/called? target/foo)))))
(testing "called? fails when its argument is not spied"
(is (thrown? IllegalArgumentException
(assertions/called? target/foo)))))
(deftest called-times?-works
(testing "the number of times a spy was called"
(bond/with-spy [target/foo]
(target/foo-caller 1)
(is (assertions/called-times? target/foo 1))
(target/foo 2)
(is (assertions/called-times? target/foo 2))))
(testing "the number of times a spy was not called"
(bond/with-spy [target/foo]
(target/foo-caller 1)
(is (not (assertions/called-times? target/foo 2)))
(target/foo-caller 2)
(is (not (assertions/called-times? target/foo 1)))))
(testing "called-times? fails when its argument is not spied"
(is (thrown? IllegalArgumentException
(assertions/called-times? target/foo 0)))))
(deftest called-with-args?-works
(testing "an assertion for calling a spy with args"
(bond/with-spy [target/foo
target/bar]
(target/foo-caller 1)
(is (assertions/called-with-args? target/foo [[1]]))
(is (not (assertions/called-with-args? target/foo [[2]])))
(is (not (assertions/called-with-args? target/bar [[1]])))
(is (not (assertions/called-with-args? target/foo [[1 2]])))))
(testing "an assertion for calling a spy multiple times with args"
(bond/with-spy [target/foo]
(target/foo-caller 1)
(target/foo-caller 2)
(is (assertions/called-with-args? target/foo [[1] [2]]))))
(testing "called-with-args? fails when its argument is not spied"
(is (thrown? IllegalArgumentException
(assertions/called-with-args? target/foo [])))))
(deftest called-once-with-args?-works
(testing "an assertion for calling a spy once with args"
(bond/with-spy [target/foo]
(target/foo 1)
(is (assertions/called-once-with-args? target/foo [1]))
(is (not (assertions/called-once-with-args? target/foo [2])))))
(testing "an assertion for calling a spy twice with args"
(bond/with-spy [target/foo]
(target/foo 1)
(target/foo 2)
(is (not (assertions/called-once-with-args? target/foo [1])))
(is (not (assertions/called-once-with-args? target/foo [2])))))
(testing "an assertion for calling a spy indirectly once with args"
(bond/with-spy [target/foo]
(target/foo-caller 1)
(is (assertions/called-once-with-args? target/foo [1]))
(is (not (assertions/called-once-with-args? target/foo [2])))))
(testing "an assertion for a spy that was not called"
(bond/with-spy [target/foo]
(is (not (assertions/called-once-with-args? target/foo [])))))
(testing "called-once-with-args? fails when its argument is not spied"
(is (thrown? IllegalArgumentException
(assertions/called-once-with-args? target/foo [])))))
(deftest called-at-least-once-with-args?-works
(testing "an assertion for calling a spy multiple times"
(bond/with-spy [target/foo]
(target/foo 1)
(target/foo 2)
(is (assertions/called-at-least-once-with-args? target/foo [1]))
(is (assertions/called-at-least-once-with-args? target/foo [2]))
(is (not (assertions/called-at-least-once-with-args? target/foo [3])))))
(testing "an assertion for calling a spy multiple times with the same value"
(bond/with-spy [target/foo]
(target/foo 1)
(target/foo 1)
(is (assertions/called-at-least-once-with-args? target/foo [1]))
(is (not (assertions/called-at-least-once-with-args? target/foo [2])))))
(testing "an assertion for calling a spy once"
(bond/with-spy [target/foo]
(target/foo 1)
(is (assertions/called-at-least-once-with-args? target/foo [1]))
(is (not (assertions/called-at-least-once-with-args? target/foo [2])))))
(testing "an assertion for a spy that was not called"
(bond/with-spy [target/foo]
(is (not (assertions/called-at-least-once-with-args? target/foo [])))))
(testing "called-at-least-once-with-args? fails when its argument is not spied"
(is (thrown? IllegalArgumentException
(assertions/called-at-least-once-with-args? target/foo [])))))

View file

@ -0,0 +1,109 @@
(ns bond.james-test
{:clj-kondo/config {:linters {:private-call {:level :off}
:invalid-arity {:level :off}}}}
(:require [clojure.test :refer (deftest is testing)]
[bond.james :as bond :include-macros true]
[bond.target-data :as target]))
(deftest spy-logs-args-and-results
(bond/with-spy [target/foo]
(is (= 2 (target/foo 1)))
(is (= 4 (target/foo 2)))
(is (= [{:args [1] :return 2}
{:args [2] :return 4}]
(bond/calls target/foo)))
(let [exception (is (thrown? clojure.lang.ArityException (target/foo 3 4)))]
(is (= {:args [3 4] :throw exception}
(-> target/foo bond/calls last))))))
(deftest calls-fails-on-unspied-fns
(is (thrown? IllegalArgumentException
(bond/calls target/foo))))
(deftest spy-can-spy-private-fns
(bond/with-spy [target/private-foo]
(is (= 4 (#'target/private-foo 2)))
(is (= 6 (#'target/private-foo 3)))
(is (= [{:args [2] :return 4}
{:args [3] :return 6}]
(bond/calls #'target/private-foo)))))
(deftest stub-works
(is (= ""
(with-out-str
(bond/with-stub [target/bar]
(target/bar 3))))))
(deftest stub-works-with-private-fn
(testing "without replacement"
(bond/with-stub [target/private-foo]
(is (nil? (#'target/private-foo 3)))
(is (= [3] (-> #'target/private-foo bond/calls first :args)))))
(testing "with replacement"
(bond/with-stub [[target/private-foo (fn [x] (* x x))]]
(is (= 9 (#'target/private-foo 3)))
(is (= [3] (-> #'target/private-foo bond/calls first :args))))))
(deftest stub-with-replacement-works
(bond/with-stub [target/foo
[target/bar #(str "arg is " %)]]
(testing "stubbing works"
(is (nil? (target/foo 4)))
(is (= "arg is 3" (target/bar 3))))
(testing "spying works"
(is (= [4] (-> target/foo bond/calls first :args)))
(is (= [3] (-> target/bar bond/calls first :args))))))
(deftest iterator-style-stubbing-works
(bond/with-stub [target/foo
[target/bar [#(str "first arg is " %)
#(str "second arg is " %)
#(str "third arg is " %)]]]
(testing "stubbing works"
(is (nil? (target/foo 4)))
(is (= "first arg is 3" (target/bar 3)))
(is (= "second arg is 4" (target/bar 4)))
(is (= "third arg is 5" (target/bar 5))))
(testing "spying works"
(is (= [4] (-> target/foo bond/calls first :args)))
(is (= [3] (-> target/bar bond/calls first :args)))
(is (= [4] (-> target/bar bond/calls second :args)))
(is (= [5] (-> target/bar bond/calls last :args))))))
(deftest stub!-complains-loudly-if-there-is-no-arglists
(is (thrown? IllegalArgumentException
(bond/with-stub! [[target/without-arglists (constantly 42)]]
(throw (Exception. "shouldn't get here"))))))
(deftest stub!-throws-arity-exception
(bond/with-stub! [[target/foo (constantly 9)]]
(is (= 9 (target/foo 12)))
(is (= [{:args [12] :return 9}] (bond/calls target/foo))))
(bond/with-stub! [target/bar
target/quuk
[target/quux (fn [_ _ & x] x)]]
(is (thrown? clojure.lang.ArityException
(target/bar 1 2)))
(is (thrown? clojure.lang.ArityException
(target/quuk 1)))
(is (= [6 5] (target/quux 8 7 6 5)))))
(deftest spying-entire-namespaces-works
(bond/with-spy-ns [bond.target-data]
(target/foo 1)
(target/foo 2)
(is (= [{:args [1] :return 2}
{:args [2] :return 4}]
(bond/calls target/foo)))
(is (= 0 (-> target/bar bond/calls count)))))
(deftest stubbing-entire-namespaces-works
(testing "without replacements"
(bond/with-stub-ns [bond.target-data]
(is (nil? (target/foo 10)))
(is (= [10] (-> target/foo bond/calls first :args)))))
(testing "with replacements"
(bond/with-stub-ns [[bond.target-data (constantly 3)]]
(is (= 3 (target/foo 10)))
(is (= [10] (-> target/foo bond/calls first :args))))))

View file

@ -0,0 +1,35 @@
(ns bond.target-data
"Reference def targets for bond to test against."
{:clj-kondo/config {:linters {:unused-binding {:level :off}
:unused-private-var {:level :off}}}})
(defn foo
[x]
(* 2 x))
(defn- private-foo
[x]
(* 2 x))
(defn foo-caller [x]
(foo x))
(defn bar
[x]
(println "bar!") (* 2 x))
(defn quux
[a b & c]
c)
(defn quuk
[a b & c]
c)
(defmacro baz
[x]
`(* ~x 2))
(def without-arglists
(fn [x]
(* 2 x)))

View file

@ -47,7 +47,11 @@
(testing "handling of blank input string"
(is (= "" (csk/->kebab-case "")))
(is (= "" (csk/->kebab-case " ")))))
(is (= "" (csk/->kebab-case " "))))
(testing "handling of input consisting of only separator(s)"
(is (= "" (csk/->kebab-case "a" :separator \a)))
(is (= "" (csk/->kebab-case "aa" :separator \a)))))
(deftest http-header-case-test
(are [x y] (= x (csk/->HTTP-Header-Case y))

View file

@ -0,0 +1,16 @@
(ns camel-snake-kebab.extras-test
(:require [camel-snake-kebab.core :as csk]
[camel-snake-kebab.extras :refer [transform-keys]]
#?(:clj [clojure.test :refer :all]
:cljs [cljs.test :refer-macros [deftest testing is are]])))
(deftest transform-keys-test
(are [x y] (= x (transform-keys csk/->kebab-case-keyword y))
nil nil
{} {}
[] []
{:total-books 0 :all-books []} {'total_books 0 "allBooks" []}
[{:the-author "Dr. Seuss" :the-title "Green Eggs and Ham"}]
[{'the-Author "Dr. Seuss" "The_Title" "Green Eggs and Ham"}]
{:total-books 1 :all-books [{:the-author "Dr. Seuss" :the-title "Green Eggs and Ham"}]}
{'total_books 1 "allBooks" [{'THE_AUTHOR "Dr. Seuss" "the_Title" "Green Eggs and Ham"}]}))

View file

@ -0,0 +1,41 @@
(ns camel-snake-kebab.internals.string-separator-test
(:require [camel-snake-kebab.internals.string-separator :refer [split generic-separator]]
#?(:clj [clojure.test :refer :all]
:cljs [cljs.test :refer-macros [deftest testing is are]])))
(deftest split-test
(testing "regex, string and character separators"
(are [sep]
(and (= ["foo" "bar"] (split sep "foo.bar"))
(= [""] (split sep "")))
#"\." "." \.))
(testing "input consisting of separator(s)"
(is (empty? (split "x" "x")))
(is (empty? (split "x" "xx"))))
(testing "generic separator"
(are [x y]
(= x (split generic-separator y))
[""] ""
[""] " "
["x"] " x "
["foo" "bar"] "foo bar"
["foo" "bar"] "foo\n\tbar"
["foo" "bar"] "foo-bar"
["foo" "Bar"] "fooBar"
["Foo" "Bar"] "FooBar"
["foo" "bar"] "foo_bar"
["FOO" "BAR"] "FOO_BAR"
["räksmörgås"] "räksmörgås"
["IP" "Address"] "IPAddress"
["Adler" "32"] "Adler32"
["Inet" "4" "Address"] "Inet4Address"
["Arc" "2" "D"] "Arc2D"
["a" "123b"] "a123b"
["A" "123" "B"] "A123B")))

View file

@ -0,0 +1,10 @@
(ns camel-snake-kebab.test-runner
(:require [cljs.test :as test]
[doo.runner :refer-macros [doo-all-tests doo-tests]]
[camel-snake-kebab.core-test]
[camel-snake-kebab.extras-test]
[camel-snake-kebab.internals.string-separator-test]))
(doo-tests 'camel-snake-kebab.core-test
'camel-snake-kebab.extras-test
'camel-snake-kebab.internals.string-separator-test)

View file

@ -32,4 +32,4 @@
(gen/reservoir-sample 10 coll))
sample-2 (binding [gen/*rnd* (java.util.Random. n)]
(gen/reservoir-sample 10 coll))]
(is (= sample-1 sample-2)))))
(is (= sample-1 sample-2)))))

View file

@ -0,0 +1,307 @@
(ns com.stuartsierra.component-test
(:require [clojure.test :refer (deftest is are)]
[clojure.set :refer (map-invert)]
[com.stuartsierra.component :as component]))
(def ^:dynamic *log* nil)
(defn- log [& args]
(when (thread-bound? #'*log*)
(set! *log* (conj *log* args))))
(defn- ordering
"Given an ordered collection of messages, returns a map from the
head of each message to its index position in the collection."
[log]
(into {} (map-indexed (fn [i [message & _]] [message i]) log)))
(defn before?
"In the collection of messages, does the message beginning with
symbol a come before the message begging with symbol b?"
[log sym-a sym-b]
(let [order (ordering log)]
(< (get order sym-a) (get order sym-b))))
(defn started? [component]
(true? (::started? component)))
(defn stopped? [component]
(false? (::started? component)))
(defrecord ComponentA [state]
component/Lifecycle
(start [this]
(log 'ComponentA.start this)
(assoc this ::started? true))
(stop [this]
(log 'ComponentA.stop this)
(assoc this ::started? false)))
(defn component-a []
(->ComponentA (rand-int Integer/MAX_VALUE)))
(defrecord ComponentB [state a]
component/Lifecycle
(start [this]
(log 'ComponentB.start this)
(assert (started? a))
(assoc this ::started? true))
(stop [this]
(log 'ComponentB.stop this)
(assert (started? a))
(assoc this ::started? false)))
(defn component-b []
(component/using
(map->ComponentB {:state (rand-int Integer/MAX_VALUE)})
[:a]))
(defrecord ComponentC [state a b]
component/Lifecycle
(start [this]
(log 'ComponentC.start this)
(assert (started? a))
(assert (started? b))
(assoc this ::started? true))
(stop [this]
(log 'ComponentC.stop this)
(assert (started? a))
(assert (started? b))
(assoc this ::started? false)))
(defn component-c []
(component/using
(map->ComponentC {:state (rand-int Integer/MAX_VALUE)})
[:a :b]))
(defrecord ComponentD [state my-c b]
component/Lifecycle
(start [this]
(log 'ComponentD.start this)
(assert (started? b))
(assert (started? my-c))
(assoc this ::started? true))
(stop [this]
(log 'ComponentD.stop this)
(assert (started? b))
(assert (started? my-c))
(assoc this ::started? false)))
(defn component-d []
(map->ComponentD {:state (rand-int Integer/MAX_VALUE)}))
(defrecord ComponentE [state]
component/Lifecycle
(start [this]
(log 'ComponentE.start this)
(assoc this ::started? true))
(stop [this]
(log 'ComponentE.stop this)
(assoc this ::started? false)))
(defn component-e []
(map->ComponentE {:state (rand-int Integer/MAX_VALUE)}))
(defrecord System1 [d a e c b] ; deliberately scrambled order
component/Lifecycle
(start [this]
(log 'System1.start this)
(component/start-system this))
(stop [this]
(log 'System1.stop this)
(component/stop-system this)))
(defn system-1 []
(map->System1 {:a (component-a)
:b (component-b)
:c (component-c)
:d (component/using (component-d)
{:b :b
:my-c :c})
:e (component-e)}))
(defmacro with-log [& body]
`(binding [*log* []]
~@body
*log*))
(deftest components-start-in-order
(let [log (with-log (component/start (system-1)))]
(are [k1 k2] (before? log k1 k2)
'ComponentA.start 'ComponentB.start
'ComponentA.start 'ComponentC.start
'ComponentB.start 'ComponentC.start
'ComponentC.start 'ComponentD.start
'ComponentB.start 'ComponentD.start)))
(deftest all-components-started
(let [system (component/start (system-1))]
(doseq [component (vals system)]
(is (started? component)))))
(deftest all-components-stopped
(let [system (component/stop (component/start (system-1)))]
(doseq [component (vals system)]
(is (stopped? component)))))
(deftest dependencies-satisfied
(let [system (component/start (component/start (system-1)))]
(are [keys] (started? (get-in system keys))
[:b :a]
[:c :a]
[:c :b]
[:d :my-c])))
(defrecord ErrorStartComponentC [state error a b]
component/Lifecycle
(start [this]
(throw error))
(stop [this]
this))
(defn error-start-c [error]
(component/using
(map->ErrorStartComponentC {:error error})
[:a :b]))
(defn setup-error
([]
(setup-error (ex-info "Boom!" {})))
([error]
(try (component/start
(assoc (system-1) :c (error-start-c error)))
(catch Exception e e))))
(deftest error-thrown-with-partial-system
(let [ex (setup-error)]
(is (started? (-> ex ex-data :system :b :a)))))
(deftest error-thrown-with-component-dependencies
(let [ex (setup-error)]
(is (started? (-> ex ex-data :component :a)))
(is (started? (-> ex ex-data :component :b)))))
(deftest error-thrown-with-cause
(let [error (ex-info "Boom!" {})
ex (setup-error error)]
(is (identical? error (.getCause ^Exception ex)))))
(deftest error-is-from-component
(let [error (ex-info "Boom!" {})
ex (setup-error error)]
(is (component/ex-component? ex))))
(deftest error-is-not-from-component
(is (not (component/ex-component? (ex-info "Boom!" {})))))
(deftest remove-components-from-error
(let [error (ex-info (str (rand-int Integer/MAX_VALUE)) {})
^Exception ex (setup-error error)
^Exception ex-without (component/ex-without-components ex)]
(is (contains? (ex-data ex) :component))
(is (contains? (ex-data ex) :system))
(is (not (contains? (ex-data ex-without) :component)))
(is (not (contains? (ex-data ex-without) :system)))
(is (= (.getMessage ex)
(.getMessage ex-without)))
(is (= (.getCause ex)
(.getCause ex-without)))
(is (java.util.Arrays/equals
(.getStackTrace ex)
(.getStackTrace ex-without)))))
(defrecord System2b [one]
component/Lifecycle
(start [this]
(assert (started? (get-in one [:b :a])))
this)
(stop [this]
(assert (started? (get-in one [:b :a])))
this))
(defn system-2 []
(component/system-map :alpha (system-1)
:beta (component/using (->System2b nil)
{:one :alpha})))
(deftest composed-systems
(let [system (component/start (system-2))]
(is (started? (get-in system [:beta :one :d :my-c])))))
(defn increment-all-components [system]
(component/update-system
system (keys system) update-in [:n] inc))
(defn assert-increments [system]
(are [n keys] (= n (get-in system keys))
11 [:a :n]
11 [:b :a :n]
11 [:c :a :n]
11 [:c :b :a :n]
11 [:e :d :b :a :n]
21 [:b :n]
21 [:c :b :n]
21 [:d :b :n]
31 [:c :n]
41 [:d :n]
51 [:e :n]))
(deftest update-with-custom-function-on-maps
(let [system {:a {:n 10}
:b (component/using {:n 20} [:a])
:c (component/using {:n 30} [:a :b])
:d (component/using {:n 40} [:a :b])
:e (component/using {:n 50} [:b :c :d])}]
(assert-increments (increment-all-components system))))
(deftest t-system-using
(let [dependency-map {:b [:a]
:c [:a :b]
:d {:a :a :b :b}
:e [:b :c :d]}
system {:a {:n 10}
:b {:n 20}
:c {:n 30}
:d {:n 40}
:e {:n 50}}
system (component/system-using system dependency-map)]
(assert-increments (increment-all-components system))))
(defrecord ComponentWithoutLifecycle [state])
;; BB-TEST-PATCH: No implementation of method errors for start and stop
#_(deftest component-without-lifecycle
(let [c (->ComponentWithoutLifecycle nil)]
(is (= c (component/start c)))
(is (= c (component/stop c)))))
(defrecord ComponentReturningNil [state]
component/Lifecycle
(start [this]
nil)
(stop [this]
nil))
(deftest component-returning-nil
(let [a (->ComponentReturningNil nil)
s (component/system-map :a a :b (component-b))
e (try (component/start s)
false
(catch Exception e e))]
(is (= ::component/nil-component (:reason (ex-data e))))))
(deftest missing-dependency-error
(let [system-key ::system-b
local-key ::local-b
a (component/using (component-a) {local-key system-key})
system (component/system-map
:a a)
e (try (component/start system)
false
(catch Exception e e))
data (ex-data e)]
(is (= ::component/missing-dependency (:reason data)))
(is (= system-key (:system-key data)))
(is (= local-key (:dependency-key data)))
(is (= a (:component data)))
(is (= system (:system data)))))

View file

@ -1,41 +0,0 @@
(ns component.component-test
(:require [clojure.test :refer [deftest is testing]]
[com.stuartsierra.component :as component]))
(def syslog (atom []))
(defn log [msg]
(swap! syslog conj msg))
(defrecord FakeDB [state]
component/Lifecycle
(start [_]
(log "start DB"))
(stop [_]
(log "stop DB")))
(defrecord FakeApp [db]
component/Lifecycle
(start [_]
(log "start app"))
(stop [_]
(log "stop app")))
(defn base-app []
(map->FakeApp {}))
(def sm
(component/system-map
:db (->FakeDB :foo)
:app (component/using (base-app) [:db])))
(component/start sm)
;; do useful stuff here
(component/stop sm)
(deftest ordering-test
(testing "components are started and stopped in expected order"
(is (= ["start DB" "start app" "stop app" "stop DB"] @syslog))))

View file

@ -0,0 +1,5 @@
(ns failjure.runner
(:require [doo.runner :refer-macros [doo-tests]]
[failjure.test-core]))
(doo-tests 'failjure.test-core)

View file

@ -1,7 +1,6 @@
(ns gaka.core-test
(:require [clojure.test :refer [deftest is are]]
[gaka.core :refer [css compile* inline-css render-rule]]
))
(:use gaka.core
clojure.test))
(defmacro =? [& body]
`(are [x# y#] (= x# y#)
@ -194,3 +193,4 @@
(is
(re-find #"^(color: red; border: 1;|border: 1; color: red;)$"
(inline-css {:color :red :border 1}))))

View file

@ -40,6 +40,16 @@
[1]
[2]
(-> (h/fn int?)
(h/with-condition (h/fn odd?)))
[1]
[2]
(-> (h/fn symbol?)
(h/with-condition (h/fn (complement #{'if 'val}))))
['a]
['if]
;; enum
(h/enum #{1 "2" :3})
[1 "2" :3]
@ -79,8 +89,7 @@
{:a 1, :b 'bar, [1 2 3] "soleil !"}]
;; map, keys and values
(h/map-of (h/fn keyword?)
(h/fn int?))
(h/map-of (h/vector (h/fn keyword?) (h/fn int?)))
[{} {:a 1, :b 2}]
[{:a 1, :b "2"} [[:a 1] [:b 2]] {true 1, false 2}]
@ -89,6 +98,10 @@
['(1 2 3) [1 2 3] `(1 2 ~3)]
['(1 :a) #{1 2 3} {:a 1, :b 2, :c 3}]
(h/sequence-of (h/fn char?))
["" "hi" "hello"]
[[1 2 3]]
;; sequence, with condition
(-> (h/sequence-of (h/fn int?))
(h/with-condition (h/fn (fn [coll] (= coll (reverse coll))))))
@ -97,29 +110,38 @@
;; sequence as a list
(h/list-of (h/fn int?))
['(1 2 3)]
['(1 :a) [1 2 3] #{1 2 3}
#_`(1 2 ~3)] ; this is not a list in cljs
['(1 2 3) `(1 2 ~3)]
['(1 :a) [1 2 3] #{1 2 3}]
;; sequence as a vector
(h/vector-of (h/fn int?))
[[1 2 3]]
[[1 :a] '(1 2 3) #{1 2 3} `(1 2 ~3)]
;; sequence as a string
(h/string-of (h/enum (set "0123456789abcdef")))
["03ab4c" "cafe"]
["coffee" [1 :a] '(1 2 3) #{1 2 3} `(1 2 ~3)]
;; sequence with size specified using a model
(-> (h/sequence-of (h/fn any?))
(h/with-count (h/enum #{2 3})))
['(1 2) [1 "2"] `(1 ~"2") [1 "2" :3]]
[#{1 "a"} [1 "2" :3 :4]]
['(1 2) [1 "2"] `(1 ~"2") [1 "2" :3] "hi"]
[#{1 "a"} [1 "2" :3 :4] "hello"]
;; sequence with entries (fixed size is implied)
(h/tuple (h/fn int?) (h/fn string?))
['(1 "2") [1 "2"] `(1 ~"2")]
[#{1 "a"} [1 "2" :3]]
;; sequence with entries in a string
(h/string-tuple (h/val \a) (h/enum #{\b \c}))
["ab" "ac"]
[[\a \b] #{\a \b}]
;; alt
(h/alt [:int (h/fn int?)]
[:strings (h/cat (h/fn string?))])
[:strings (h/vector-of (h/fn string?))])
[1 ["1"]]
[[1] "1" :1 [:1]]
@ -144,6 +166,12 @@
[[1 "2" 3] [1 :2 3] [1 ["a" :b] 3]]
[[1 "a" :b 3]]
;; cat & repeat - a color string
(-> (h/cat (h/val \#)
(h/repeat 6 6 (h/enum (set "0123456789abcdefABCDEF")))))
["#000000" "#af4Ea5"]
["000000" "#cafe" "#coffee"]
;; cat of cat, the inner cat is implicitly inlined
(-> (h/cat (h/fn int?)
(h/cat (h/fn int?)))
@ -163,15 +191,20 @@
[[] [1] [1 2] '() '(1) '(2 3)]
[[1 2 3] '(1 2 3)]
;; repeat - inside a list
(h/in-list (h/repeat 0 2 (h/fn int?)))
['() '(1) '(2 3)]
[[] [1] [1 2] [1 2 3] '(1 2 3)]
;; repeat - inside a vector
(h/in-vector (h/repeat 0 2 (h/fn int?)))
[[] [1] [1 2]]
[[1 2 3] '() '(1) '(2 3) '(1 2 3)]
;; repeat - inside a list
(h/in-list (h/repeat 0 2 (h/fn int?)))
['() '(1) '(2 3)]
[[] [1] [1 2] [1 2 3] '(1 2 3)]
;; repeat - inside a string
(h/in-string (h/repeat 4 6 (h/fn char?)))
["hello"]
["" "hi" [] [1] '(1 2 3)]
;; repeat - min > 0
(h/repeat 2 3 (h/fn int?))
@ -196,6 +229,16 @@
[[[1 "a"]] [[1 "a"] [2 "b"]] ['(1 "a") [2 "b"]]]
[[] [1] [1 2] [1 "a"] [1 "a" 2 "b"] [1 "a" 2 "b" 3 "c"]]
;; char-cat & char-set
(-> (h/cat (h/char-cat "good")
(h/val \space)
(h/alt (h/char-cat "morning")
(h/char-cat "afternoon")
(h/repeat 3 10 (h/char-set "#?!@_*+%"))))
(h/in-string))
["good morning" "good afternoon" "good #@*+?!"]
["good" "good " "good day"]
;; let / ref
(h/let ['pos-even? (h/and (h/fn pos-int?)
(h/fn even?))]
@ -236,14 +279,24 @@
[1 1 1 "hi" "hi" "hi"]]
[[1 1 "hi"]
[1 "hi" "hi"]
[1 1 :no "hi" "hi"]]]]
[1 1 :no "hi" "hi"]]
; let / ref - with shadowed local model
(h/let ['foo (h/ref 'bar)
'bar (h/fn int?)]
(h/let ['bar (h/fn string?)]
(h/ref 'foo)))
[1]
["hi"]]]
(doseq [[model valid-coll invalid-coll] (partition 3 test-data)]
(doseq [data valid-coll]
(is (valid? model data)))
(doseq [data invalid-coll]
(is (not (valid? model data)))))))
(is (not (valid? model data))))))
(is (thrown? #?(:clj Exception :cljs js/Object)
(valid? (h/let [] (h/ref 'foo)) 'bar))))
(deftest describe-test
@ -252,6 +305,16 @@
[1 1
2 :invalid]
(-> (h/fn int?)
(h/with-condition (h/fn odd?)))
[1 1
2 :invalid]
(-> (h/fn symbol?)
(h/with-condition (h/fn (complement #{'if 'val}))))
['a 'a
'if :invalid]
;; enum
(h/enum #{1 "2" false nil})
[1 1
@ -278,7 +341,7 @@
;; set
(h/set-of (h/fn int?))
[#{1} #{1}]
[#{1 2} [1 2]]
;; map
(h/map [:a {:optional true} (h/fn int?)]
@ -294,20 +357,29 @@
; extra entry
{:a 1, :b 2, :c 3} {:a 1, :b 2}]
;; map-of - :keys
(h/map-of (h/fn keyword?) (h/fn any?))
[{:a 1, :b 2} {:a 1, :b 2}
;; map-of - entry-model
(h/map-of (h/vector (h/fn keyword?) (h/fn int?)))
[{:a 1, :b 2} [[:a 1] [:b 2]]
{"a" 1} :invalid]
;; map-of - :values
(h/map-of (h/fn any?) (h/fn int?))
[{:a 1, "b" 2} {:a 1, "b" 2}
{:a "1"} :invalid]
;; map-of - real world use case
(h/map-of (h/alt [:symbol (h/vector (h/fn simple-symbol?) (h/fn keyword?))]
[:keys (h/vector (h/val :keys) (h/vector-of (h/fn symbol?)))]
[:as (h/vector (h/val :as) (h/fn simple-symbol?))]))
'[{first-name :first-name
last-name :last-name
:keys [foo bar]
:as foobar}
[[:symbol [first-name :first-name]]
[:symbol [last-name :last-name]]
[:keys [:keys [foo bar]]]
[:as [:as foobar]]]]
;; sequence - :elements-model
(h/sequence-of (h/fn int?))
[[1 2 3] [1 2 3]
'(1 2 3) '(1 2 3)
`(1 2 3) '(1 2 3)
[1 "2" 3] :invalid]
;; sequence - :elements-model with condition
@ -319,7 +391,14 @@
;; sequence - :coll-type vector
(h/vector-of (h/fn any?))
[[1 2 3] [1 2 3]
'(1 2 3) :invalid]
'(1 2 3) :invalid
`(1 2 3) :invalid]
;; sequence - :coll-type list
(h/list-of (h/fn any?))
[[1 2 3] :invalid
'(1 2 3) '(1 2 3)
`(1 2 3) '(1 2 3)]
;; sequence - :entries
(h/tuple (h/fn int?) (h/fn string?))
@ -327,16 +406,27 @@
[1 2] :invalid
[1] :invalid]
(h/tuple (h/fn int?)
[:text (h/fn string?)])
[[1 "a"] {:text "a"}]
(h/tuple [:number (h/fn int?)]
[:text (h/fn string?)])
[[1 "a"] {:number 1, :text "a"}]
;; sequence - :count-model
(-> (h/sequence-of (h/fn any?))
(h/with-count (h/val 3)))
[[1 2] :invalid
[1 2 3] [1 2 3]
[1 2 3 4] :invalid]
[1 2 3 4] :invalid
"12" :invalid
"123" (into [] "123")
"1234" :invalid]
;; alt - not inside a sequence
(h/alt [:number (h/fn int?)]
[:sequence (h/cat (h/fn string?))])
[:sequence (h/vector-of (h/fn string?))])
[1 [:number 1]
["1"] [:sequence ["1"]]
[1] :invalid
@ -457,4 +547,7 @@
(doseq [[model data-description-pairs] (partition 2 test-data)]
(doseq [[data description] (partition 2 data-description-pairs)]
(is (= [data (describe model data)]
[data description]))))))
[data description])))))
(is (thrown? #?(:clj Exception :cljs js/Object)
(describe (h/let [] (h/ref 'foo)) 'bar))))

View file

@ -0,0 +1,546 @@
(ns minimallist.generator-test
(:require [clojure.test :refer [deftest testing is are]]
[clojure.test.check.generators :as tcg]
[clojure.string :as str]
[minimallist.core :refer [valid?]]
[minimallist.helper :as h]
[minimallist.util :as util]
[minimallist.generator :as mg :refer [gen fn-any? fn-int? fn-string? fn-char?
fn-symbol? fn-simple-symbol? fn-qualified-symbol?
fn-keyword? fn-simple-keyword? fn-qualified-keyword?]]))
(defn- path-test-visitor []
;; Testing using side effects.
;; A little ugly, but good enough for tests.
(let [paths (atom [])]
(fn
([] @paths)
([model stack path]
(swap! paths conj path)
model))))
(deftest postwalk-visit-order-test
(are [model expected-paths]
(let [visitor (path-test-visitor)]
(mg/postwalk model visitor) ; Create side effects
(= (visitor) expected-paths)) ; Collect and compare the side effects
(h/let ['leaf (h/fn int?)
'tree (h/ref 'leaf)]
(h/ref 'tree))
[[:bindings 'leaf]
[:bindings 'tree]
[:body]
[]]
(h/let ['root (h/let ['leaf (h/fn int?)
'tree (h/ref 'leaf)]
(h/ref 'tree))]
(h/ref 'root))
[[:bindings 'root :bindings 'leaf]
[:bindings 'root :bindings 'tree]
[:bindings 'root :body]
[:bindings 'root]
[:body]
[]]
(h/let ['leaf (h/fn int?)
'root (h/let ['tree (h/ref 'leaf)]
(h/ref 'tree))]
(h/ref 'root))
[[:bindings 'leaf]
[:bindings 'root :bindings 'tree]
[:bindings 'root :body]
[:bindings 'root]
[:body]
[]]
; test of no visit more than once
(h/let ['leaf (h/fn int?)
'tree (h/tuple (h/ref 'leaf) (h/ref 'leaf))]
(h/ref 'tree))
[[:bindings 'leaf]
[:bindings 'tree :entries 0 :model]
[:bindings 'tree :entries 1 :model]
[:bindings 'tree]
[:body]
[]]
; test of no visit more than once, infinite loop otherwise
(h/let ['leaf (h/fn int?)
'tree (h/tuple (h/ref 'tree) (h/ref 'leaf))]
(h/ref 'tree))
[[:bindings 'tree :entries 0 :model]
[:bindings 'leaf]
[:bindings 'tree :entries 1 :model]
[:bindings 'tree]
[:body]
[]]
#__))
(deftest assoc-leaf-distance-visitor-test
(are [model expected-walked-model]
(= (-> model
(mg/postwalk mg/assoc-leaf-distance-visitor)
(util/walk-map-dissoc :fn))
expected-walked-model)
; Recursive data-structure impossible to generate
; This one is trying to bring the generator function in an infinite loop.
(h/let ['loop (h/ref 'loop)]
(h/ref 'loop))
{:type :let
:bindings {'loop {:type :ref
:key 'loop}}
:body {:type :ref
:key 'loop}}
; Recursive data-structure impossible to generate
(h/let ['leaf (h/fn int?)
'tree (h/tuple (h/ref 'tree) (h/ref 'leaf))]
(h/ref 'tree))
{:type :let
:bindings {'leaf {:type :fn
::mg/leaf-distance 0}
'tree {:type :sequence
:entries [{:model {:type :ref
:key 'tree}}
{:model {:type :ref
:key 'leaf
::mg/leaf-distance 1}}]}}
:body {:type :ref
:key 'tree}}
; Recursive data-structure impossible to generate
(h/let ['rec-map (h/map [:a (h/fn int?)]
[:b (h/ref 'rec-map)])]
(h/ref 'rec-map))
{:type :let
:bindings {'rec-map {:type :map
:entries [{:key :a
:model {:type :fn
::mg/leaf-distance 0}}
{:key :b
:model {:type :ref
:key 'rec-map}}]}}
:body {:type :ref
:key 'rec-map}}
; Recursive data-structure which can be generated
(h/let ['leaf (h/fn int?)
'tree (h/alt (h/ref 'tree) (h/ref 'leaf))]
(h/ref 'tree))
{:type :let
:bindings {'leaf {:type :fn
::mg/leaf-distance 0}
'tree {:type :alt
:entries [{:model {:type :ref
:key 'tree}}
{:model {:type :ref
:key 'leaf
::mg/leaf-distance 1}}]
::mg/leaf-distance 2}}
:body {:type :ref
:key 'tree
::mg/leaf-distance 3}
::mg/leaf-distance 4}
(h/let ['rec-map (h/map [:a (h/fn int?)]
[:b {:optional true} (h/ref 'rec-map)])]
(h/ref 'rec-map))
{:type :let
:bindings {'rec-map {:type :map
:entries [{:key :a
:model {:type :fn
::mg/leaf-distance 0}}
{:key :b
:optional true
:model {:type :ref
:key 'rec-map}}]
::mg/leaf-distance 1}}
:body {:type :ref
:key 'rec-map
::mg/leaf-distance 2}
::mg/leaf-distance 3}
#__))
(deftest assoc-min-cost-visitor-test
(are [model expected-walked-model]
(= (-> model
(mg/postwalk mg/assoc-min-cost-visitor)
(util/walk-map-dissoc :fn))
expected-walked-model)
(h/tuple (h/fn int?) (h/fn string?))
{:type :sequence
:entries [{:model {:type :fn
::mg/min-cost 1}}
{:model {:type :fn
::mg/min-cost 1}}]
::mg/min-cost 3}
(h/cat (h/fn int?) (h/fn string?))
{:type :cat
:entries [{:model {:type :fn
::mg/min-cost 1}}
{:model {:type :fn
::mg/min-cost 1}}]
::mg/min-cost 3}
(h/in-vector (h/cat (h/fn int?) (h/fn string?)))
{:type :cat
:coll-type :vector
:entries [{:model {:type :fn
::mg/min-cost 1}}
{:model {:type :fn
::mg/min-cost 1}}]
::mg/min-cost 3}
(h/not-inlined (h/cat (h/fn int?) (h/fn string?)))
{:type :cat
:inlined false
:entries [{:model {:type :fn
::mg/min-cost 1}}
{:model {:type :fn
::mg/min-cost 1}}]
::mg/min-cost 3}
(h/map [:a (h/fn int?)]
[:b {:optional true} (h/fn int?)])
{:type :map
:entries [{:key :a
:model {:type :fn
::mg/min-cost 1}}
{:key :b
:optional true
:model {:type :fn
::mg/min-cost 1}}]
::mg/min-cost 2}
(h/map-of (h/vector (h/fn keyword?) (h/fn int?)))
{:type :map-of
:entry-model {:type :sequence
:coll-type :vector
:entries [{:model {:type :fn
::mg/min-cost 1}}
{:model {:type :fn
::mg/min-cost 1}}]
::mg/min-cost 3}
::mg/min-cost 1}
(-> (h/map-of (h/vector (h/fn keyword?) (h/fn int?)))
(h/with-count (h/enum #{3 4})))
{:type :map-of
:entry-model {:type :sequence
:coll-type :vector
:entries [{:model {:type :fn
::mg/min-cost 1}}
{:model {:type :fn
::mg/min-cost 1}}]
::mg/min-cost 3}
:count-model {:type :enum
:values #{3 4}}
::mg/min-cost 7}
(h/set-of (h/fn any?))
{:type :set-of
:elements-model {:type :fn
::mg/min-cost 1}
::mg/min-cost 1}
(-> (h/set-of (h/fn any?))
(h/with-count (h/val 3)))
{:type :set-of
:elements-model {:type :fn
::mg/min-cost 1}
:count-model {:type :enum
:values #{3}}
::mg/min-cost 4}
(h/let ['foo (-> (h/set-of (h/fn int?))
(h/with-count (h/val 3)))]
(h/ref 'foo))
{:type :let
:bindings {'foo {:type :set-of
:count-model {:type :enum
:values #{3}}
:elements-model {:type :fn
::mg/min-cost 1}
::mg/min-cost 4}}
:body {:type :ref
:key 'foo
::mg/min-cost 4}
::mg/min-cost 4}
#__))
(deftest budget-split-gen-test
(is (every? (fn [[a b c]]
(and (<= 0 a 5)
(<= 5 b 10)
(<= 10 c 15)))
(-> (#'mg/budget-split-gen 20.0 [0 5 10])
tcg/sample)))
(is (every? #(= % [5 10 10])
(-> (#'mg/budget-split-gen 20.0 [5 10 10])
tcg/sample)))
(is (every? empty?
(-> (#'mg/budget-split-gen 10.0 [])
tcg/sample))))
(comment
;; For occasional hand testing
(tcg/sample (gen (-> (h/set-of fn-any?)
(h/with-count (h/enum #{1 2 3 10}))
(h/with-condition (h/fn (comp #{1 2 3} count))))))
(tcg/sample (gen (h/map-of (h/vector fn-int? fn-simple-symbol?))))
(tcg/sample (gen (-> (h/map [:a fn-int?])
(h/with-optional-entries [:b fn-string?]))))
(tcg/sample (gen (h/sequence-of fn-int?)))
(tcg/sample (gen (h/tuple fn-int? fn-string?)))
(tcg/sample (gen (h/cat fn-int? fn-string?)))
(tcg/sample (gen (h/repeat 2 3 fn-int?)))
(tcg/sample (gen (h/repeat 2 3 (h/cat fn-int? fn-string?))))
(tcg/sample (gen (h/let ['int? fn-int?
'string? fn-string?
'int-string? (h/cat (h/ref 'int?) (h/ref 'string?))]
(h/repeat 2 3 (h/ref 'int-string?)))))
(tcg/sample (gen (-> (h/set-of fn-int?)
(h/with-condition (h/fn (fn [coll]
(or (empty? coll)
(some even? coll))))))))
(tcg/sample (gen (-> (h/set-of fn-any?)
(h/with-count (h/enum #{1 2 3 10}))
(h/with-condition (h/fn (comp #{1 2 3} count))))))
(tcg/sample (gen (h/let ['node (h/set-of (h/ref 'node))]
(h/ref 'node))))
(tcg/sample (gen (h/let ['node (h/map-of (h/vector fn-int? (h/ref 'node)))]
(h/ref 'node)) 50))
(tcg/sample (gen (h/let ['node (h/map-of (h/vector fn-keyword? (h/ref 'node)))]
(h/ref 'node)) 100) 1)
(tcg/sample (gen (h/map [:a fn-int?])))
(tcg/sample (gen (-> (h/map [:a fn-int?])
(h/with-optional-entries [:b fn-string?]))))
(tcg/sample (gen (h/cat (h/vector-of fn-int?)
(h/vector-of fn-int?)) 20))
(tcg/sample (gen (h/repeat 5 10 fn-int?)))
(tcg/sample (gen fn-symbol?))
(tcg/sample (gen fn-simple-symbol?))
(tcg/sample (gen fn-qualified-symbol?))
(tcg/sample (gen fn-keyword?))
(tcg/sample (gen fn-simple-keyword?))
(tcg/sample (gen fn-qualified-keyword?))
(tcg/sample (gen (-> (h/cat (h/char-cat "good")
(h/val \space)
(h/alt (h/char-cat "morning")
(h/char-cat "afternoon")
(h/repeat 3 10 (h/char-set "#?!@_*+%"))))
(h/in-string)))
100)
#__)
(deftest gen-test
(let [model fn-string?]
(is (every? (partial valid? model)
(tcg/sample (gen model)))))
(let [model (h/enum #{:1 2 "3"})]
(is (every? (partial valid? model)
(tcg/sample (gen model)))))
(let [model (-> (h/set-of fn-int?)
(h/with-condition (h/fn (fn [coll]
(or (empty? coll)
(some even? coll))))))]
(is (every? (partial valid? model)
(tcg/sample (gen model)))))
(let [model (-> (h/set-of fn-any?)
(h/with-count (h/enum #{1 2 3 10}))
(h/with-condition (h/fn (comp #{1 2 3} count))))]
(is (every? (partial valid? model)
(tcg/sample (gen model)))))
(let [model (h/map-of (h/vector fn-int? fn-string?))]
(is (every? (partial valid? model)
(tcg/sample (gen model)))))
(let [model (-> (h/map [:a fn-int?])
(h/with-optional-entries [:b fn-string?])
(h/with-entries [:c fn-int?])
(h/with-optional-entries [:d fn-string?]))
sample (tcg/sample (gen model) 100)]
(is (and (every? (partial valid? model) sample)
(every? (fn [element] (contains? element :a)) sample)
(some (fn [element] (contains? element :b)) sample)
(some (fn [element] (not (contains? element :b))) sample)
(every? (fn [element] (contains? element :c)) sample)
(some (fn [element] (contains? element :d)) sample)
(some (fn [element] (not (contains? element :d))) sample))))
(let [model (h/sequence-of fn-int?)]
(is (every? (partial valid? model)
(tcg/sample (gen model)))))
(let [model (h/tuple fn-int? fn-string?)
sample (tcg/sample (gen model) 100)]
(is (and (every? (partial valid? model) sample)
(some list? sample)
(some vector? sample))))
(let [model (h/list fn-int? fn-string?)
sample (tcg/sample (gen model))]
(is (and (every? (partial valid? model) sample)
(every? list? sample))))
(let [model (h/vector fn-int? fn-string?)
sample (tcg/sample (gen model))]
(is (and (every? (partial valid? model) sample)
(every? vector? sample))))
(let [model (h/string-tuple fn-char? fn-char?)
sample (tcg/sample (gen model))]
(is (and (every? (partial valid? model) sample)
(every? string? sample))))
(let [model (h/in-list (h/cat fn-int? fn-string?))
sample (tcg/sample (gen model))]
(is (and (every? (partial valid? model) sample)
(every? list? sample))))
(let [model (h/in-vector (h/cat fn-int? fn-string?))
sample (tcg/sample (gen model))]
(is (and (every? (partial valid? model) sample)
(every? vector? sample))))
(let [model (h/in-string (h/cat fn-char? fn-char?))
sample (tcg/sample (gen model))]
(is (and (every? (partial valid? model) sample)
(every? string? sample))))
(let [model (h/alt fn-int? fn-string?)]
(is (every? (partial valid? model)
(tcg/sample (gen model)))))
(let [model (h/cat fn-int? fn-string?)]
(is (every? (partial valid? model)
(tcg/sample (gen model)))))
(let [model (h/repeat 2 3 fn-int?)]
(is (every? (partial valid? model)
(tcg/sample (gen model)))))
(let [model (h/repeat 2 3 (h/cat fn-int? fn-string?))]
(is (every? (partial valid? model)
(tcg/sample (gen model)))))
(let [model (h/not-inlined (h/cat fn-int?))]
(is (every? (partial valid? model)
(tcg/sample (gen model)))))
(let [model (h/not-inlined (h/repeat 1 2 fn-int?))]
(is (every? (partial valid? model)
(tcg/sample (gen model)))))
(let [model (h/let ['int? fn-int?
'string? fn-string?
'int-string? (h/cat (h/ref 'int?) (h/ref 'string?))]
(h/repeat 2 3 (h/ref 'int-string?)))]
(is (every? (partial valid? model)
(tcg/sample (gen model)))))
;; Budget-based limit on model choice.
(let [model (h/let ['tree (h/alt [:leaf fn-int?]
[:branch (h/vector (h/ref 'tree)
(h/ref 'tree))])]
(h/ref 'tree))]
(is (every? (partial valid? model)
(tcg/sample (gen model)))))
;; Budget-based limit on variable set size.
(let [model (h/let ['node (h/set-of (h/ref 'node))]
(h/ref 'node))]
(is (every? (partial valid? model)
(tcg/sample (gen model)))))
;; Budget-based limit on variable sequence size.
(let [model (h/let ['node (h/vector-of (h/ref 'node))]
(h/ref 'node))]
(is (every? (partial valid? model)
(tcg/sample (gen model)))))
;; Budget-based limit on variable map size.
(let [model (h/let ['node (h/map-of (h/vector fn-int? (h/ref 'node)))]
(h/ref 'node))]
(is (every? (partial valid? model)
(tcg/sample (gen model)))))
;; Budget-based limit on optional entries in a map.
(let [model (h/let ['node (-> (h/map [:a fn-int?])
(h/with-optional-entries [:x (h/ref 'node)]
[:y (h/ref 'node)]
[:z (h/ref 'node)]))]
(h/ref 'node))]
(is (every? (partial valid? model)
(tcg/sample (gen model)))))
;;; Budget-based limit on number of occurrences in a repeat.
;(let [model (h/let ['node (h/repeat 0 1 (h/ref 'node))]
; (h/ref 'node))]
; (is (every? (partial valid? model)
; (tcg/sample (gen model)))))
;; Model impossible to generate.
(let [model (h/let ['node (h/map [:a (h/ref 'node)])]
(h/ref 'node))]
(is (thrown? #?(:clj Exception :cljs js/Object) (tcg/sample (gen model)))))
;; Model impossible to generate.
(let [model (h/let ['node (h/tuple (h/ref 'node))]
(h/ref 'node))]
(is (thrown? #?(:clj Exception :cljs js/Object) (tcg/sample (gen model)))))
;; Model impossible to generate.
(let [model (h/let ['node (h/cat (h/ref 'node))]
(h/ref 'node))]
(is (thrown? #?(:clj Exception :cljs js/Object) (tcg/sample (gen model)))))
;; Model impossible to generate.
(let [model (h/let ['node (h/cat (h/ref 'node))]
(h/ref 'node))]
(is (thrown? #?(:clj Exception :cljs js/Object) (tcg/sample (gen model)))))
(let [model (h/let ['node (h/repeat 1 2 (h/ref 'node))]
(h/ref 'node))]
(is (thrown? #?(:clj Exception :cljs js/Object) (tcg/sample (gen model))))))
;; TODO: [later] reuse the cat-ipsum model for parsing the output.
;; TODO: in the :alt node, introduce a property :occurrence for the generator.
;; TODO: generate models, use them to generate data, should not stack overflow.

View file

@ -0,0 +1,53 @@
(ns minimallist.util-test
(:require [clojure.test :refer [deftest testing is are]]
[minimallist.util :as util]
[minimallist.helper :as h]))
(deftest reduce-update-test
(let [m {:a 1
:b 5}
f (fn [acc elm]
(let [elm10 (* elm 10)]
[(conj acc elm10) elm10]))]
(is (= (-> [[] m]
(util/reduce-update :a f)
(util/reduce-update :b f))
[[10 50] {:a 10, :b 50}]))))
(deftest reduce-update-in-test
(let [m {:a {:x 1, :y 2}
:b [3 4 5]}
f (fn [acc elm]
(let [elm10 (* elm 10)]
[(conj acc elm10) elm10]))]
(is (= (-> [[] m]
(util/reduce-update-in [:a :x] f)
(util/reduce-update-in [:b 2] f))
[[10 50] {:a {:x 10, :y 2}, :b [3 4 50]}]))))
(deftest reduce-mapv
(let [m {:a {:x 1, :y 2}
:b [3 4 5]}
f (fn [acc elm]
(let [elm10 (* elm 10)]
[(conj acc elm10) elm10]))]
(is (= (util/reduce-update [[] m] :b (partial util/reduce-mapv f))
[[30 40 50] {:a {:x 1, :y 2}, :b [30 40 50]}]))))
(deftest iterate-while-different-test
(let [inc-up-to-10 (fn [x] (cond-> x (< x 10) inc))]
(is (= (util/iterate-while-different inc-up-to-10 0 0) 0))
(is (= (util/iterate-while-different inc-up-to-10 0 5) 5))
(is (= (util/iterate-while-different inc-up-to-10 0 10) 10))
(is (= (util/iterate-while-different inc-up-to-10 0 15) 10))
(is (= (util/iterate-while-different inc-up-to-10 7 2) 9))
(is (= (util/iterate-while-different inc-up-to-10 7 3) 10))
(is (= (util/iterate-while-different inc-up-to-10 7 4) 10))
(is (= (util/iterate-while-different inc-up-to-10 12 0) 12))
(is (= (util/iterate-while-different inc-up-to-10 12 3) 12))
(is (= (util/iterate-while-different inc-up-to-10 0 ##Inf) 10))
(is (= (util/iterate-while-different inc-up-to-10 10 ##Inf) 10))
(is (= (util/iterate-while-different inc-up-to-10 15 ##Inf) 15))))

View file

@ -74,18 +74,19 @@
(mega-try (throw+ exception-1))
(mega-try (throw exception-1))
(try (throw+ exception-1)
(catch Exception e [:class-exception e]))
(catch Exception e [:class-exception e]))
(try (throw exception-1)
(catch Exception e [:class-exception e])))))
(catch Exception e [:class-exception e])))))
(testing "IllegalArgumentException thrown by clojure/core"
(is (= :class-iae (first (mega-try (str/replace "foo" 1 1)))))))
(testing "catch by java class generically"
(is (= [:class-string "fail"] (mega-try (throw+ "fail")))))
;; BB-TEST-PATCH: bb has different record internals
#_(testing "catch by clojure record type"
(is (= [:class-exception-record exception-record-1]
(mega-try (throw+ exception-record-1)))))
(is (= [:class-exception-record exception-record-1]
(mega-try (throw+ exception-record-1)))))
(testing "catch by key is present"
(is (= [:key-is-present #{:a-key}] (mega-try (throw+ #{:a-key})))))

View file

@ -46,9 +46,11 @@
(defn stack-trace-fn []
(stack-trace))
;; BB-TEST-PATCH: Returns jdk.internal.reflect.DelegatingMethodAccessorImpl
;; instead of what's expected
#_(deftest test-stack-trace
(let [{:keys [methodName className]} (-> (stack-trace-fn) first bean)]
(is (= methodName "invoke"))
(is (.startsWith ^String methodName "invoke"))
(is (re-find #"stack_trace_fn" className))))
(deftest test-resolve-local

View file

@ -70,4 +70,13 @@
;; Some more zero-extension Tests
"1-SNAPSHOT" "1.0-SNAPSHOT" 0
"1-alpha" "1-alpha0" 0
;; Prefixed versions
"v1" "v1" 0
"v1" "v2" -1
"v1" "v1.1" -1
"v1.1" "v1.2" -1
"v1.1" "v2" -1
"alpaca1.0" "bermuda1.0" -1
))

View file

@ -35,6 +35,10 @@
"0.5.0-alpha.1" [[0 5 0] ["alpha" 1]]
"0.5.0-alpha.1" [[0 5 0] ["alpha" 1]]
"0.0.3-alpha.8+oryOS.15" [[0 0 3] ["alpha" [8 "+oryos"] 15]]
"v1" [["v" 1]]
"v1.1" [["v" [1 1]]]
"ver1" [["ver" 1]]
"ver1.1" [["ver" [1 1]]]
))
(deftest t-split-without-qualifiers

View file

@ -6,6 +6,7 @@
(use 'version-clj.core)
;; BB-TEST-PATCH: This test doesn't exist upstream
(deftest sanity-test
(is (= [[1 0 0] ["snapshot"]] (version->seq "1.0.0-SNAPSHOT")))
(is (= 0 (version-compare "1.0" "1.0.0")))