diff --git a/deps.edn b/deps.edn index f522c23d..9e4879b8 100644 --- a/deps.edn +++ b/deps.edn @@ -29,4 +29,6 @@ {com.clojure-goes-fast/clj-async-profiler {:mvn/version "0.4.1"}} :extra-paths ["test"] :jvm-opts ["-Djdk.attach.allowAttachSelf"] - :main-opts ["-m" "babashka.profile"]}}} + :main-opts ["-m" "babashka.profile"]} + :lib-tests + {:extra-deps {minimallist {:mvn/version "0.0.1"}}}}} diff --git a/script/lib_tests/run_all_libtests b/script/lib_tests/run_all_libtests new file mode 100755 index 00000000..03a1f42e --- /dev/null +++ b/script/lib_tests/run_all_libtests @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -eo pipefail + +if [ "$BABASHKA_TEST_ENV" = "native" ]; then + BB_CMD="./bb" +else + BB_CMD="lein bb" +fi + +export BABASHKA_CLASSPATH +BABASHKA_CLASSPATH=$(clojure -A:lib-tests -Spath) + +$BB_CMD -cp "$BABASHKA_CLASSPATH:test-resources/lib_tests" \ + -f "test-resources/lib_tests/babashka/run_all_libtests.clj" diff --git a/script/run_lib_tests b/script/run_lib_tests index 69fded16..1bc97cb5 100755 --- a/script/run_lib_tests +++ b/script/run_lib_tests @@ -28,3 +28,4 @@ script/lib_tests/camel_snake_kebab_test script/lib_tests/aero_test script/lib_tests/clojure_data_generators_test script/lib_tests/honeysql_test +script/lib_tests/run_all_libtests diff --git a/test-resources/lib_tests/babashka/run_all_libtests.clj b/test-resources/lib_tests/babashka/run_all_libtests.clj new file mode 100644 index 00000000..518c9716 --- /dev/null +++ b/test-resources/lib_tests/babashka/run_all_libtests.clj @@ -0,0 +1,23 @@ +(ns babashka.run-all-libtests + (:require [clojure.test :as t])) + +(def status (atom {})) + +(defn test-namespaces [& namespaces] + (let [m (apply t/run-tests namespaces)] + (swap! status (fn [status] + (merge-with + status m))))) + +;;;; Minimallist + +(require '[minimallist.core-test]) + +(test-namespaces 'minimallist.core-test) + +;;;; Final exit code + +(let [{:keys [:test :fail :error] :as m} @status] + (prn m) + (when-not (pos? test) + (System/exit 1)) + (System/exit (+ fail error))) diff --git a/test-resources/lib_tests/minimallist/core_test.cljc b/test-resources/lib_tests/minimallist/core_test.cljc new file mode 100644 index 00000000..9b9a6360 --- /dev/null +++ b/test-resources/lib_tests/minimallist/core_test.cljc @@ -0,0 +1,214 @@ +(ns minimallist.core-test + (:require [clojure.test :refer [deftest testing is are]] + [minimallist.core :refer [valid?]] + [minimallist.helper :as h])) + +(deftest valid?-test + (let [test-data [;; fn + (h/fn #(= 1 %)) + [1] + [2] + + ;; enum + (h/enum #{1 "2" :3}) + [1 "2" :3] + [[1] 2 true false nil] + + (h/enum #{nil false}) + [nil false] + [true '()] + + ;; and + (h/and (h/fn pos-int?) + (h/fn even?)) + [2 4 6] + [0 :a -1 1 3] + + ;; or + (h/or (h/fn pos-int?) + (h/fn even?)) + [-2 0 1 2 3] + [-3 -1] + + ;; set + (-> (h/set-of (h/fn int?)) + (h/with-count (h/enum #{2 3}))) + [#{1 2} #{1 2 3}] + [#{1 :a} [1 2 3] '(1 2) `(1 ~2) #{1} #{1 2 3 4}] + + ;; map, entries + (h/map [:a (h/fn int?)] + [:b {:optional true} (h/fn string?)] + [(list 1 2 3) (h/fn string?)]) + [{:a 1, :b "foo", (list 1 2 3) "you can count on me like ..."} + {:a 1, :b "bar", [1 2 3] "soleil !"} + {:a 1, [1 2 3] "soleil !"}] + [{:a 1, :b "foo"} + {:a 1, :b "foo", #{1 2 3} "bar"} + {:a 1, :b 'bar, [1 2 3] "soleil !"}] + + ;; map, keys and values + (h/map-of (h/fn keyword?) + (h/fn int?)) + [{} {:a 1, :b 2}] + [{:a 1, :b "2"} [[:a 1] [:b 2]] {true 1, false 2}] + + ;; sequence, no collection type specified + (h/sequence-of (h/fn int?)) + ['(1 2 3) [1 2 3] `(1 2 ~3)] + ['(1 :a) #{1 2 3} {:a 1, :b 2, :c 3}] + + ;; sequence, with condition + (-> (h/sequence-of (h/fn int?)) + (h/with-condition (h/fn (fn [coll] (= coll (reverse coll)))))) + [[1] '(1 1) '[1 2 1]] + ['(1 2) '(1 2 3)] + + ;; 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 + + ;; 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 with size specified using a model + (-> (h/sequence) (h/with-count (h/enum #{2 3}))) + ['(1 2) [1 "2"] `(1 ~"2") [1 "2" :3]] + [#{1 "a"} [1 "2" :3 :4]] + + ;; 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]] + + ;; alt + (h/alt [:int (h/fn int?)] + [:strings (h/cat (h/fn string?))]) + [1 ["1"]] + [[1] "1" :1 [:1]] + + ;; alt - inside a cat + (h/cat (h/fn int?) + (h/alt [:string (h/fn string?)] + [:keyword (h/fn keyword?)] + [:string-keyword (h/cat (h/fn string?) + (h/fn keyword?))]) + (h/fn int?)) + [[1 "2" 3] [1 :2 3] [1 "a" :b 3]] + [[1 ["a" :b] 3]] + + ;; alt - inside a cat, but with :inline false on its cat entry + (h/cat (h/fn int?) + (h/alt [:string (h/fn string?)] + [:keyword (h/fn keyword?)] + [:string-keyword (-> (h/cat (h/fn string?) + (h/fn keyword?)) + (h/not-inlined))]) + (h/fn int?)) + [[1 "2" 3] [1 :2 3] [1 ["a" :b] 3]] + [[1 "a" :b 3]] + + ;; cat of cat, the inner cat is implicitly inlined + (-> (h/cat (h/fn int?) + (h/cat (h/fn int?))) + (h/in-vector)) + [[1 2]] + [[1] [1 [2]] [1 2 3] '(1) '(1 2) '(1 (2)) '(1 2 3)] + + ;; cat of cat, the inner cat is explicitly not inlined + (-> (h/cat (h/fn int?) + (-> (h/cat (h/fn int?)) + (h/not-inlined)))) + [[1 [2]] '[1 (2)] '(1 (2))] + [[1] [1 2] [1 [2] 3]] + + ;; repeat - no collection type specified + (h/repeat 0 2 (h/fn int?)) + [[] [1] [1 2] '() '(1) '(2 3)] + [[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 - min > 0 + (h/repeat 2 3 (h/fn int?)) + [[1 2] [1 2 3]] + [[] [1] [1 2 3 4]] + + ;; repeat - max = +Infinity + (h/repeat 2 ##Inf (h/fn int?)) + [[1 2] [1 2 3] [1 2 3 4]] + [[] [1]] + + ;; repeat - of a cat + (h/repeat 1 2 (h/cat (h/fn int?) + (h/fn string?))) + [[1 "a"] [1 "a" 2 "b"]] + [[] [1] [1 2] [1 "a" 2 "b" 3 "c"]] + + ;; repeat - of a cat with :inlined false + (h/repeat 1 2 (-> (h/cat (h/fn int?) + (h/fn string?)) + (h/not-inlined))) + [[[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"]] + + ;; let / ref + (h/let ['pos-even? (h/and (h/fn pos-int?) + (h/fn even?))] + (h/ref 'pos-even?)) + [2 4] + [-2 -1 0 1 3] + + ;; let / ref - with structural recursion + (h/let ['hiccup (h/alt + [:node (h/in-vector (h/cat (h/fn keyword?) + (h/? (h/map)) + (h/* (h/not-inlined (h/ref 'hiccup)))))] + [:primitive (h/or (h/fn nil?) + (h/fn boolean?) + (h/fn number?) + (h/fn string?))])] + (h/ref 'hiccup)) + [nil + false + 1 + "hi" + [:div] + [:div {}] + [:div "hei" [:p "bonjour"]] + [:div {:a 1} "hei" [:p "bonjour"]]] + [{} + {:a 1} + ['div] + [:div {:a 1} "hei" [:p {} {} "bonjour"]]] + + ;; let / ref - with recursion within a sequence + (h/let ['foo (h/cat (h/fn int?) + (h/? (h/ref 'foo)) + (h/fn string?))] + (h/ref 'foo)) + [[1 "hi"] + [1 1 "hi" "hi"] + [1 1 1 "hi" "hi" "hi"]] + [[1 1 "hi"] + [1 "hi" "hi"] + [1 1 :no "hi" "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)))))))