From a1851986383148593ca85675d3dafd1e8517481a Mon Sep 17 00:00:00 2001 From: Andrey Orst Date: Thu, 12 Nov 2020 19:25:57 +0300 Subject: fix(CI): overhaul --- .depend.mk | 6 +- .luacov | 8 +- Makefile | 44 ++-- cljlib-macros.fnl | 212 +++++++++++-------- cljlib.fnl | 12 +- test/core.fnl | 609 ------------------------------------------------------ test/fn.fnl | 44 ---- test/macros.fnl | 168 --------------- test/test.fnl | 58 ------ tests/core.fnl | 609 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/fn.fnl | 44 ++++ tests/macros.fnl | 198 ++++++++++++++++++ tests/test.fnl | 62 ++++++ 13 files changed, 1072 insertions(+), 1002 deletions(-) delete mode 100644 test/core.fnl delete mode 100644 test/fn.fnl delete mode 100644 test/macros.fnl delete mode 100644 test/test.fnl create mode 100644 tests/core.fnl create mode 100644 tests/fn.fnl create mode 100644 tests/macros.fnl create mode 100644 tests/test.fnl diff --git a/.depend.mk b/.depend.mk index e13bdcd..1c09089 100644 --- a/.depend.mk +++ b/.depend.mk @@ -1,4 +1,4 @@ cljlib.lua: cljlib.fnl cljlib-macros.fnl -test/core.lua: test/core.fnl cljlib-macros.fnl test/test.fnl -test/macros.lua: test/macros.fnl cljlib-macros.fnl test/test.fnl -test/fn.lua: test/fn.fnl cljlib-macros.fnl test/test.fnl +tests/core.lua: tests/core.fnl cljlib-macros.fnl tests/test.fnl cljlib.fnl +tests/macros.lua: tests/macros.fnl cljlib-macros.fnl tests/test.fnl +tests/fn.lua: tests/fn.fnl cljlib-macros.fnl tests/test.fnl diff --git a/.luacov b/.luacov index 1e6023e..9e4a6c2 100644 --- a/.luacov +++ b/.luacov @@ -3,10 +3,8 @@ -- see https://keplerproject.github.io/luacov/doc/modules/luacov.defaults.html return { - ["exclude"] = { - "src/fennel/macros.fnl" - }, + exclude = {"src/fennel/macros.fnl"}, runreport = true, - statsfile = "luacov.stats.out"; - reportfile = "luacov.report.out"; + statsfile = "luacov.stats"; + reportfile = "luacov.report"; } diff --git a/Makefile b/Makefile index d6363d2..dca6dd8 100644 --- a/Makefile +++ b/Makefile @@ -1,50 +1,44 @@ LUA ?= lua - -FNLSOURCES = cljlib.fnl test/core.fnl test/macros.fnl test/fn.fnl +FENNEL ?= fennel +FNLSOURCES = cljlib.fnl LUASOURCES = $(FNLSOURCES:.fnl=.lua) +FNLTESTS = tests/fn.fnl tests/macros.fnl tests/core.fnl +LUATESTS = $(FNLTESTS:.fnl=.lua) -all: $(LUASOURCES) +.PHONY: all clean distclean help test luacov luacov-console -.PHONY: clean help test coverage all +all: $(LUASOURCES) ${LUASOURCES}: $(FNLSOURCES) %.lua: %.fnl - fennel --lua $(LUA) --compile $< > $@ + $(FENNEL) --lua $(LUA) --compile $< > $@ clean: - rm -f *.lua - rm -f test/*.lua + find . -type f -name '*.lua' | xargs rm -f -clean-all: clean +distclean: clean rm -f luacov* -test: clean - @fennel --lua $(LUA) --metadata test/fn.fnl - @fennel --lua $(LUA) --metadata test/core.fnl - @fennel --lua $(LUA) --metadata test/macros.fnl +test: $(FNLTESTS) + @$(foreach test, $?, $(FENNEL) --lua $(LUA) --metadata $(test);) -luacov: | clean-all all luacov-stats +luacov: all $(LUATESTS) + @$(foreach test, $(LUATESTS), $(LUA) -lluarocks.loader -lluacov $(test);) luacov -luacov-console: | luacov - @mv test/core.lua test/core.lua.tmp - @mv test/macros.lua test/macros.lua.tmp - @mv test/fn.lua test/fn.lua.tmp +luacov-console: luacov + @$(foreach test, $(LUATESTS), mv $(test) $(test).tmp;) luacov-console . - @mv test/core.lua.tmp test/core.lua - @mv test/macros.lua.tmp test/macros.lua - @mv test/fn.lua.tmp test/fn.lua - -luacov-stats: test/core.lua test/macros.lua test/fn.lua - @$(foreach test, $?, $(LUA) -lluarocks.loader -lluacov $(test);) + @$(foreach test, $(LUATESTS), mv $(test).tmp $(test);) help: @echo "make -- run tests and create lua library" >&2 @echo "make test -- run tests" >&2 @echo "make clean -- remove lua files" >&2 - @echo "make luacov -- build coverage report (requires working tests)" >&2 - @echo "make luacov-console -- build coverage report for luacov-console (requires working tests)" >&2 + @echo "make distclean -- remove all unnecessary files" >&2 + @echo "make luacov -- build coverage report" >&2 + @echo "make luacov-console -- build coverage report for luacov-console" >&2 @echo "make help -- print this message and exit" >&2 -include .depend.mk diff --git a/cljlib-macros.fnl b/cljlib-macros.fnl index 908fd44..ae6f93e 100644 --- a/cljlib-macros.fnl +++ b/cljlib-macros.fnl @@ -280,7 +280,10 @@ namespaced functions. See `fn*' for more info." (assert-compile (= (length bindings) 2) "expected exactly two forms in binding vector." bindings))) (fn if-let [...] - (let [[bindings then else] [...]] + (let [[bindings then else] (match (select :# ...) + 2 [...] + 3 [...] + _ (error "wrong argument amount for if-some" 2))] (check-bindings bindings) (let [[form test] bindings] `(let [tmp# ,test] @@ -290,7 +293,8 @@ namespaced functions. See `fn*' for more info." ,else))))) (fn when-let [...] - (let [[bindings & body] [...]] + (let [[bindings & body] (if (> (select :# ...) 0) [...] + (error "wrong argument amount for when-let" 2))] (check-bindings bindings) (let [[form test] bindings] `(let [tmp# ,test] @@ -299,7 +303,10 @@ namespaced functions. See `fn*' for more info." ,(unpack body))))))) (fn if-some [...] - (let [[bindings then else] [...]] + (let [[bindings then else] (match (select :# ...) + 2 [...] + 3 [...] + _ (error "wrong argument amount for if-some" 2))] (check-bindings bindings) (let [[form test] bindings] `(let [tmp# ,test] @@ -309,7 +316,8 @@ namespaced functions. See `fn*' for more info." ,then)))))) (fn when-some [...] - (let [[bindings & body] [...]] + (let [[bindings & body] (if (> (select :# ...) 0) [...] + (error "wrong argument amount for when-some" 2))] (check-bindings bindings) (let [[form test] bindings] `(let [tmp# ,test] @@ -335,6 +343,7 @@ namespaced functions. See `fn*' for more info." (if (and (= (type k#) :number) (= k# 1)) :seq (= k# nil) :empty :table)))) + (= t# :nil) :nil :else)))) (fn seq-fn [] @@ -342,7 +351,7 @@ namespaced functions. See `fn*' for more info." (var assoc# false) (let [res# [] insert# table.insert] - (each [k# v# (pairs tbl#)] + (each [k# v# (pairs (or tbl# []))] (if (and (not assoc#) (not (= (type k#) :number))) (set assoc# true)) @@ -359,61 +368,83 @@ namespaced functions. See `fn*' for more info." (let [to-type (table-type to) from-type (table-type from)] (if (and (= to-type :seq) (= from-type :seq)) - `(let [to# ,to + `(let [to# (or ,to []) insert# table.insert] - (each [_# v# (ipairs ,from)] + (each [_# v# (ipairs (or ,from []))] (insert# to# v#)) - to#) + (setmetatable to# {:cljlib/table-type :seq})) (= to-type :seq) - `(let [to# ,to + `(let [to# (or ,to []) seq# ,(seq-fn) insert# table.insert] - (each [_# v# (ipairs (seq# ,from))] + (each [_# v# (ipairs (seq# (or ,from [])))] (insert# to# v#)) - to#) + (setmetatable to# {:cljlib/table-type :seq})) (and (= to-type :table) (= from-type :seq)) - `(let [to# ,to] - (each [_# [k# v#] (ipairs ,from)] + `(let [to# (or ,to [])] + (each [_# [k# v#] (ipairs (or ,from []))] (tset to# k# v#)) - to#) + (setmetatable to# {:cljlib/table-type :table})) (and (= to-type :table) (= from-type :table)) - `(let [to# ,to - from# ,from] + `(let [to# (or ,to []) + from# (or ,from [])] (each [k# v# (pairs from#)] (tset to# k# v#)) - to#) + (setmetatable to# {:cljlib/table-type :table})) (= to-type :table) - `(let [to# ,to - from# ,from] + `(let [to# (or ,to []) + from# (or ,from [])] (match (,(table-type-fn) from#) :seq (each [_# [k# v#] (ipairs from#)] (tset to# k# v#)) :table (each [k# v# (pairs from#)] (tset to# k# v#)) - :else (error "expected table as second argument")) - to#) + :else (error "expected table as second argument" 2)) + (setmetatable to# {:cljlib/table-type :table})) + ;; runtime branch `(let [to# ,to from# ,from insert# table.insert table-type# ,(table-type-fn) - seq# ,(seq-fn)] - (match (table-type# to#) - :seq (each [_# v# (ipairs (seq# from#))] - (insert# to# v#)) - :table (match (table-type# from#) - :seq (each [_# [k# v#] (ipairs from#)] - (tset to# k# v#)) - :table (each [k# v# (pairs from#)] - (tset to# k# v#)) - :else (error "expected table as second argument")) - ;; If we could not deduce type, it means that - ;; we've got empty table. We use will default - ;; to sequential table, because it will never - ;; break when converting into - :empty (each [_# v# (ipairs (seq# from#))] - (insert# to# v#)) - :else (error "expected table as first argument")) - to#)))) + seq# ,(seq-fn) + to-type# (table-type# to#) + to# (or to# []) ;; secure nil + res# (match to-type# + :seq (do (each [_# v# (ipairs (seq# from#))] + (insert# to# v#)) + to#) + :table (match (table-type# from#) + :seq (do (each [_# [k# v#] (ipairs (or from# []))] + (tset to# k# v#)) + to#) + :table (do (each [k# v# (pairs (or from# []))] + (tset to# k# v#)) + to#) + :empty to# + :else (error "expected table as second argument" 2)) + ;; If we could not deduce type, it means that + ;; we've got empty table. We use will default + ;; to sequential table, because it will never + ;; break when converting into + :empty (do (each [_# v# (ipairs (seq# from#))] + (insert# to# v#)) + to#) + :nil (match (table-type# from#) + :nil nil + :empty to# + :seq (do (each [k# v# (pairs (or from# []))] + (tset to# k# v#)) + to#) + :table (do (each [k# v# (pairs (or from# []))] + (tset to# k# v#)) + to#) + :else (error "expected table as second argument" 2)) + :else (error "expected table as first argument" 2))] + (if res# + (setmetatable res# {:cljlib/table-type (match to-type# + :seq :seq + :empty :seq + :table :table)})))))) (fn first [tbl] (. tbl 1)) @@ -425,7 +456,8 @@ namespaced functions. See `fn*' for more info." (= (type x) :string)) (fn when-meta [...] - (when meta-enabled `(do ,...))) + (when meta-enabled + `(do ,...))) (fn meta [v] (when-meta @@ -447,53 +479,69 @@ namespaced functions. See `fn*' for more info." res#) (= a# b#)))) +(fn seq->table [seq] + (let [tbl {}] + (var v nil) + (var (i k) (next seq)) + (while i + (set (i v) (next seq i)) + (tset tbl k v) + (set (i k) (next seq i))) + tbl)) + (fn defmulti [...] - (let [[name & opts] [...] - docstring (if (string? (first opts)) (first opts)) - opts (if docstring (rest opts) opts) - dispatch-fn (first opts)] - (if (in-scope? name) - nil - `(local ,name - (let [multimethods# {}] - (setmetatable - ,(with-meta {} {:fnl/docstring docstring}) - {:__call - (fn [_# ...] - ,docstring - (let [dispatch-value# (,dispatch-fn ...) - (res# view#) (pcall require :fennelview)] - ((or (. multimethods# dispatch-value#) - (. multimethods# :default) - (error (.. "No method in multimethod '" - ,(tostring name) - "' for dispatch value: " - ((if res# view# tostring) dispatch-value#)) - 2)) ...))) - :multimethods (setmetatable multimethods# - {:__index - (fn [tbl# key#] - (let [eq# ,(eq-fn)] - (var res# nil) - (each [k# v# (pairs tbl#)] - (when (eq# k# key#) - (set res# v#) - (lua :break))) - res#))})})))))) - -(fn defmethod [...] - (let [[multifn dispatch-val & fn-tail] [...]] - `(let [multifn# ,multifn] - (tset (. (getmetatable multifn#) :multimethods) - ,dispatch-val - (fn* ,(unpack fn-tail))) - multifn#))) + (let [[name & options] (if (> (select :# ...) 0) [...] + (error "wrong argument amount for defmulti")) + docstring (if (string? (first options)) (first options)) + options (if docstring (rest options) options) + dispatch-fn (first options) + options (rest options)] + (assert (= (% (length options) 2) 0) "wrong argument amount for defmulti") + (let [options (seq->table options)] + (if (in-scope? name) + nil + `(local ,name + (let [multimethods# {}] + (setmetatable + ,(with-meta {} {:fnl/docstring docstring}) + {:__call + (fn [_# ...] + ,docstring + (let [dispatch-value# (,dispatch-fn ...) + (res# view#) (pcall require :fennelview) + tostr# (if res# view# tostring)] + ((or (. multimethods# dispatch-value#) + (. multimethods# (or (. ,options :default) :default)) + (error (.. "No method in multimethod '" + ,(tostring name) + "' for dispatch value: " + (tostr# dispatch-value#)) + 2)) ...))) + :multimethods (setmetatable multimethods# + {:__index + (fn [tbl# key#] + (let [eq# ,(eq-fn)] + (var res# nil) + (each [k# v# (pairs tbl#)] + (when (eq# k# key#) + (set res# v#) + (lua :break))) + res#))})}))))))) + +(fn defmethod [multifn dispatch-val ...] + (when (= (select :# ...) 0) (error "wrong argument amount for defmethod")) + `(let [multifn# ,multifn] + (tset (. (getmetatable multifn#) :multimethods) + ,dispatch-val + (do (fn* f# ,...) + f#)) + multifn#)) (fn def [...] (let [[attr-map name expr] (match (select :# ...) 2 [{} ...] 3 [...] - _ (error "wa")) + _ (error "wrong argument amount for def" 2)) attr-map (if (table? attr-map) attr-map (string? attr-map) {attr-map true} (error "def: expected keyword or literal table as first argument" 2)) @@ -511,7 +559,7 @@ namespaced functions. See `fn*' for more info." (let [[attr-map name expr] (match (select :# ...) 2 [{} ...] 3 [...] - _ (error "wa"))] + _ (error "wrong argument amount for def" 2))] (if (in-scope? name) nil (def attr-map name expr)))) diff --git a/cljlib.fnl b/cljlib.fnl index 14b5869..f0b4d18 100644 --- a/cljlib.fnl +++ b/cljlib.fnl @@ -213,15 +213,11 @@ If `tbl' is sequential table, returns its shallow copy." ([tbl x & xs] (apply conj (conj tbl x) xs))) -(fn* consj +(fn consj [...] "Like conj but joins at the front. Modifies `tbl'." - ([] (empty [])) - ([tbl] tbl) - ([tbl x] - (when-some [x x] - (doto tbl (insert 1 x)))) - ([tbl x & xs] - (apply consj (consj tbl x) xs))) + (let [[tbl x & xs] [...]] + (if (nil? x) tbl + (consj (doto tbl (insert 1 x)) (unpack xs))))) (fn& core.cons "Insert `x' to `tbl' at the front. Modifies `tbl'." diff --git a/test/core.fnl b/test/core.fnl deleted file mode 100644 index 6c705b6..0000000 --- a/test/core.fnl +++ /dev/null @@ -1,609 +0,0 @@ -(require-macros :cljlib-macros) -(require-macros :test.test) - -(local - {: vector - : hash-map - : apply - : seq - : first - : rest - : last - : butlast - : conj - : cons - : concat - : reduce - : reduce-kv - : mapv - : filter - : map? - : seq? - : nil? - : zero? - : pos? - : neg? - : even? - : odd? - : int? - : pos-int? - : neg-int? - : double? - : string? - : boolean? - : false? - : true? - : empty? - : not-empty - : eq - : identity - : comp - : every? - : some - : not-any? - : complement - : constantly - : range - : reverse - : inc - : dec - : assoc - : get - : get-in - : get-method - : methods - : remove-method - : remove-all-methods - : add - : sub - : mul - : div - : le - : ge - : lt - : gt} - (require :cljlib)) - -(deftest equality - (testing "comparing basetypes" - (assert* (not (pcall eq))) - (assert-eq 1 1) - (assert-ne 1 2) - (assert* (eq 1 1 1 1 1)) - (assert-eq 1.0 1.0) - (assert* (eq 1.0 1.0 1.0)) - (assert* (eq 1.0 1.0 1.0)) - (assert* (eq "1" "1" "1" "1" "1"))) - - (testing "deep comparison" - (assert* (eq [])) - (assert-eq [] []) - (assert-eq [] {}) - (assert-eq [1 2] [1 2]) - (assert-ne [1] [1 2]) - (assert-ne [1 2] [1]) - (assert* (eq [1 [2]] [1 [2]] [1 [2]])) - (assert* (eq [1 [2]] [1 [2]] [1 [2]])) - (assert* (not (eq [1 [2]] [1 [2]] [1 [2 [3]]]))) - - (let [a {:a 1 :b 2} - b {:a 1 :b 2}] - (table.insert b 10) - (assert-ne a b)) - - (let [a [1 2 3] - b [1 2 3]] - (tset b :a 10) - (assert-ne a b)) - - (assert-eq [1 2 3] {1 1 2 2 3 3}) - - ;; TODO: decide if this is right or not. Looking from `seq' - ;; perspective, it is correct, as `(seq {4 1})' and `(seq [nil nil - ;; nil 1])' both yield `{4 1}'. From Lua's point this is not the - ;; same thing, for example because the sizes of these tables are - ;; different. - (assert-eq {4 1} [nil nil nil 1]))) - -(testing "range" - (assert* (not (pcall range))) - (assert-eq (range 10) [0 1 2 3 4 5 6 7 8 9]) - (assert-eq (range -5 5) [-5 -4 -3 -2 -1 0 1 2 3 4]) - (assert-eq [0 0.2 0.4 0.6 0.8] [0 0.2 0.4 0.6 0.8]) - (assert-eq (range 0 1 0.2) (range 0 1 0.2))) - -(deftest predicates - (testing "zero?" - (assert* (zero? 0)) - (assert* (zero? -0)) - (assert* (not (zero? 1)))) - - (testing "int?" - (assert* (int? 1)) - (assert* (not (int? 1.1)))) - - (testing "pos?" - (assert* (pos? 1)) - (assert* (and (not (pos? 0)) (not (pos? -1))))) - - (testing "neg?" - (assert* (neg? -1)) - (assert* (and (not (neg? 0)) (not (neg? 1))))) - - (testing "pos-int?" - (assert* (pos-int? 42)) - (assert* (not (pos-int? 4.2)))) - - (testing "neg-int?" - (assert* (neg-int? -42)) - (assert* (not (neg-int? -4.2)))) - - (testing "string?" - (assert* (string? :s))) - - (testing "double?" - (assert* (double? 3.3)) - (assert* (not (double? 3.0)))) - - (testing "map?" - (assert* (map? {:a 1})) - (assert* (not (map? {}))) - (assert* (map? (empty {}))) - (assert* (not (map? (empty []))))) - - (testing "seq?" - (assert* (not (seq? []))) - (assert* (seq? [{:a 1}])) - (assert* (not (seq? {}))) - (assert* (not (seq? {:a 1}))) - (assert* (seq? (empty []))) - (assert* (not (seq? (empty {}))))) - - (testing "nil?" - (assert* (nil?)) - (assert* (nil? nil)) - (assert* (not (nil? 1)))) - - (testing "odd?" - (assert* (odd? 3)) - (assert* (odd? -3)) - (assert* (not (odd? 2))) - (assert* (not (odd? -2)))) - - (testing "even?" - (assert* (even? 2)) - (assert* (even? -2)) - (assert* (not (even? 23))) - (assert* (not (even? -23)))) - - (testing "true?" - (assert* (true? true)) - (assert* (not (true? false))) - (assert* (not (true? 10))) - (assert* (not (true? :true)))) - - (testing "false?" - (assert* (false? false)) - (assert* (not (false? true))) - (assert* (not (false? 10))) - (assert* (not (false? :true)))) - - (testing "boolean?" - (assert* (boolean? true)) - (assert* (boolean? false)) - (assert* (not (boolean? :false))) - (assert* (not (boolean? (fn [] true)))))) - -(deftest sequence-functions - (testing "seq" - (assert-eq (seq []) nil) - (assert-eq (seq {}) nil) - (assert-eq (seq [1]) [1]) - (assert-eq (seq [1 2 3]) [1 2 3]) - (assert-eq (seq {:a 1}) [["a" 1]])) - - (testing "mapv" - (assert* (not (pcall mapv))) - (assert* (not (pcall mapv #(do nil)))) - (assert-eq (mapv #(* $ $) [1 2 3 4]) [1 4 9 16]) - - (assert-eq (into {} (mapv (fn [[k v]] [k (* v v)]) {:a 1 :b 2 :c 3})) - (into {} [[:a 1] [:b 4] [:c 9]])) - - (assert-eq (into {} (mapv (fn [[k1 v1] [k2 v2]] [k1 (* v1 v2)]) - {:a 1 :b 2 :c 3} - {:a -1 :b 0 :c 2})) - {:a -1 :b 0 :c 6}) - (assert-eq (mapv #(* $1 $2 $3) [1] [2] [-1]) [-2]) - (assert-eq (mapv string.upper ["a" "b" "c"]) ["A" "B" "C"]) - (assert-eq (mapv #(+ $1 $2 $3 $4) [1 -1] [2 -2] [3 -3] [4 -4]) [(+ 1 2 3 4) (+ -1 -2 -3 -4)]) - (assert-eq (mapv (fn [f-name s-name company position] - (.. f-name " " s-name " works as " position " at " company)) - ["Bob" "Alice"] - ["Smith" "Watson"] - ["Happy Days co." "Coffee With You"] - ["secretary" "chief officer"]) - ["Bob Smith works as secretary at Happy Days co." - "Alice Watson works as chief officer at Coffee With You"])) - - (testing "reduce" - (fn* add - ([] 0) - ([a] a) - ([a b] (+ a b)) - ([a b & c] - (var res (+ a b)) - (each [_ v (ipairs c)] - (set res (+ res v))) - res)) - - (assert-eq (reduce add []) 0) - (assert-eq (reduce add [1]) 1) - (assert-eq (reduce add [1 2]) 3) - (assert-eq (reduce add (range 10)) 45) - (assert-eq (reduce add -3 (range 10)) 42) - (assert-eq (reduce add 10 []) 10) - (assert-eq (reduce add 10 [1]) 11) - (assert-eq (reduce add 10 nil) 10) - (assert* (not (pcall reduce))) - (assert* (not (pcall reduce add)))) - - (testing "reduce reference implementation" - (fn mapping [f] - (fn [reducing] - (fn [result input] - (reducing result (f input))))) - - (fn reduce- [f init [x & tbl]] - (if x (reduce- f (f init x) tbl) init)) - - (assert-eq (reduce add (range 10)) (reduce- add 0 (range 10))) - (assert-eq (reduce ((mapping inc) add) 0 (range 10)) - (reduce- ((mapping inc) add) 0 (range 10)))) - - (testing "filter" - (assert* (not (pcall filter))) - (assert* (not (pcall filter even?))) - (assert-eq (filter even? (range 10)) [0 2 4 6 8]) - (assert-eq (filter odd? (range 10)) [1 3 5 7 9]) - (assert-eq (filter map? [{:a 1} {5 1} [1 2] [] {}]) [{:a 1} {5 1}]) - (assert-eq (filter seq? [{:a 1} {5 1} [1 2] [] {}]) [[1 2]])) - - (testing "concat" - (assert-eq (concat) nil) - (assert-eq (concat []) []) - (assert-eq (concat [1 2 3]) [1 2 3]) - (assert-eq (concat [1 2 3] [4 5 6]) [1 2 3 4 5 6]) - (assert-eq (concat [1 2] [3 4] [5 6]) [1 2 3 4 5 6]) - (assert-eq (concat {:a 1} {:b 2}) [[:a 1] [:b 2]]) - (assert-eq (concat [[:a 1]] {:b 2}) [[:a 1] [:b 2]]) - (assert-eq (concat {:a 1} [[:b 2]]) [[:a 1] [:b 2]]) - (assert-eq (concat [] [[:b 2]]) [[:b 2]]) - (assert-eq (concat [] []) []) - (assert* (not (pcall concat 1))) - (assert* (not (pcall concat 1 2))) - (assert* (not (pcall concat 1 []))) - (assert* (not (pcall concat [] 2))) - (assert* (not (pcall concat [1] 2)))) - - (testing "reverse" - (assert-eq (reverse []) nil) - (assert-eq (reverse [1 2 3]) [3 2 1]) - (assert-eq (reverse {:a 1}) [[:a 1]])) - - (testing "conj" - (assert-eq (conj) []) - (assert-eq (conj [1] nil) [1]) - (assert-eq (conj [] 1 2 3) [1 2 3]) - (assert-eq (conj [0] 1 2 3) [0 1 2 3]) - (assert-eq (conj {:a 1} [:b 2]) {:a 1 :b 2}) - (assert-eq (conj {:a 1}) {:a 1}) - (assert-eq (conj [1] 2 3 4 5 6 7) [1 2 3 4 5 6 7])) - - (testing "cons" - (assert-eq (cons nil [1]) [1]) - (assert-eq (cons 1 []) [1]) - (assert-eq (cons 1 [0]) [1 0])) - - (testing "first" - (assert-eq (first [1 2 3]) 1) - (assert-eq (first {:a 1}) [:a 1]) - (assert-eq (first []) nil)) - - (testing "last" - (assert-eq (last [1 2 3]) 3) - (assert-eq (last []) nil) - (assert-eq (last nil) nil) - (assert-eq (last {:a 1}) [:a 1])) - - (testing "rest" - (assert-eq (rest [1 2 3]) [2 3]) - (assert-eq (rest {:a 1}) []) - (assert-eq (rest []) []) - (assert-eq (rest nil) [])) - - (testing "butlast" - (assert-eq (butlast [1 2 3]) [1 2]) - (assert-eq (butlast {:a 1}) nil) - (assert-eq (butlast []) nil) - (assert-eq (butlast nil) nil)) - - (testing "reduce-kv" - (assert-eq (reduce-kv #(+ $1 $3) 0 {:a 1 :b 2 :c 3}) 6) - (assert* (not (pcall reduce-kv #(+ $1 $3) 0))) - (assert* (not (pcall reduce-kv #(+ $1 $3)))) - (assert* (not (pcall reduce-kv)))) - - (testing "assoc" - (assert* (not (pcall assoc))) - (assert* (not (pcall assoc {}))) - (assert-eq (assoc {} :a 1) {:a 1}) - (assert-eq (assoc {} :a 1 :b 2 :c 3 :d 4) {:a 1 :b 2 :c 3 :d 4}))) - -(deftest function-manipulation - (testing "constantly" - (let [always-nil (constantly nil)] - (assert-eq (always-nil) nil) - (assert-eq (always-nil 1) nil) - (assert-eq (always-nil 1 2 3 4 "5") nil)) - - (let [always-true (constantly true)] - (assert* (always-true)) - (assert* (always-true false)))) - - (testing "complement" - (assert* ((complement #(do false)))) - (assert* ((complement nil?) 10)) - (assert* ((complement every?) double? [1 2 3 4])) - (assert* ((complement #(= $1 $2 $3)) 1 1 2 1)) - (assert* ((complement #(= $1 $2)) 1 2))) - - (testing "apply" - (fn* add - ([x] x) - ([x y] (+ x y)) - ([x y & zs] - (add (+ x y) ((or _G.unpack table.unpack) zs)))) - (assert-eq (apply add [1 2 3 4]) 10) - (assert-eq (apply add -1 [1 2 3 4]) 9) - (assert-eq (apply add -2 -1 [1 2 3 4]) 7) - (assert-eq (apply add -3 -2 -1 [1 2 3 4]) 4) - (assert-eq (apply add -4 -3 -2 -1 [1 2 3 4]) 0) - (assert-eq (apply add -5 -4 -3 -2 -1 [1 2 3 4]) -5) - (assert* (not (pcall apply))) - (assert* (not (pcall apply add)))) - - (testing "comp" - (assert-eq ((comp) 10) 10) - (fn square [x] (* x x)) - (assert-eq (comp square) square) - (assert-eq ((comp square inc) 6) 49) - (assert-eq ((comp #(- $ 7) square inc inc inc inc inc inc inc) 0) 42) - (fn sum-squares [x y] (+ (* x x) (* y y))) - (assert-eq ((comp square inc sum-squares) 2 3) 196) - (fn f [a b c] (+ a b c)) - (assert-eq ((comp inc f) 1 2 3) 7) - (fn g [a b c d] (+ a b c d)) - (assert-eq ((comp inc g) 1 2 3 4) 11) - (fn h [a b c d e f] (+ a b c d e f)) - (assert-eq ((comp inc h) 1 2 3 4 5 6) 22)) - - (testing "identity" - (fn f [] nil) - (assert-eq (identity 1) 1) - (assert-eq (identity {:a 1 :b 2}) {:a 1 :b 2}) - (assert-eq (identity [1 2 3]) [1 2 3]) - (assert-eq (identity "abc") "abc") - (assert-eq (identity f) f))) - -(deftest sequence-predicates - (testing "some" - (assert* (not (pcall some))) - (assert* (not (pcall some pos-int?))) - (assert* (some pos-int? [-1 1.1 2.3 -5.5 42 10 -27])) - (assert* (not (some pos-int? {:a 1}))) - (assert* (some pos-int? [{:a 1} "1" -1 1]))) - - (testing "not-any?" - (assert* (not (pcall not-any?))) - (assert* (not (pcall not-any? pos-int?))) - (assert* (not-any? pos-int? [-1 1.1 2.3 -5.5 -42 -10 -27])) - (assert* (not-any? pos-int? {:a 1})) - (assert* (not (not-any? pos-int? [1 2 3 4 5])))) - - (testing "every?" - (assert* (not (pcall every?))) - (assert* (not (pcall every? pos-int?))) - (assert* (not (every? pos-int? [-1 1.1 2.3 -5.5 42 10 -27]))) - (assert* (not (every? pos-int? {:a 1}))) - (assert* (every? pos-int? [1 2 3 4 5]))) - - (testing "empty?" - (assert* (not (pcall empty?))) - (assert* (empty? [])) - (assert* (empty? {})) - (assert* (empty? "")) - (assert* (not (empty? "1"))) - (assert* (not (empty? [1]))) - (assert* (not (empty? {:a 1}))) - (assert* (not (pcall empty? 10)))) - - (testing "not-empty" - (assert* (not (pcall not-empty))) - (assert-eq (not-empty []) nil) - (assert-eq (not-empty {}) nil) - (assert-eq (not-empty "") nil) - (assert-eq (not-empty "1") "1") - (assert-eq (not-empty [1]) [1]) - (assert-eq (not-empty {:a 1}) {:a 1}))) - -(deftest math-functions - (testing "inc" - (assert-eq (inc 1) 2) - (assert-eq (inc -1) 0) - (assert* (not (pcall inc))) - (assert* (not (pcall inc nil)))) - - (testing "dec" - (assert-eq (dec 1) 0) - (assert-eq (dec -1) -2) - (assert* (not (pcall dec))) - (assert* (not (pcall dec nil))))) - -(deftest table-access - (testing "get" - (assert-eq (get {:key1 10 :key2 20} :key1) 10) - (assert-eq (get {:key1 10 :key2 20} :key1 false) 10) - (assert-eq (get {:key1 10 :key2 20} :key3 false) false) - (assert-eq (get {:key1 10 :key2 20} :key3) nil) - (assert* (not (pcall get))) - (assert* (not (pcall get {})))) - - (testing "get-in" - (local t {:a {:b {:c 10}}}) - (assert-eq (get-in t [:a]) {:b {:c 10}}) - (assert-eq (get-in t [:a :b]) {:c 10}) - (assert-eq (get-in t [:a :b :c]) 10) - (assert-eq (get-in t [:a :b :c] false) 10) - (assert-eq (get-in t [:a :b :d] false) false) - (assert-eq (get-in t [:a :b :d]) nil) - (assert-eq (get-in t []) t) - (assert* (not (pcall get-in))) - (assert* (not (pcall get-in {}))))) - -(deftest methods - (testing "methods" - (defmulti f identity) - (defmethod f :a [_] :a) - (defmethod f :b [_] :b) - (defmethod f :c [x] (* x x)) - (assert-eq (methods f) (. (getmetatable f) :multimethods)) - (assert* (not (pcall methods))) - (assert* (not (pcall methods f f)))) - - (testing "get-method" - (defmulti f identity) - (defmethod f :a [_] :a) - (defmethod f :b [_] :b) - (defmethod f :c [x] (* x x)) - (assert-eq ((get-method f :a) 10) :a) - (assert-eq ((get-method f :b) 20) :b) - (assert-eq ((get-method f :c) 4) 16) - (assert* (not (pcall get-method))) - (assert* (not (pcall get-method f))) - (assert* (not (pcall get-method f :a :b)))) - - (testing "remove-method" - (defmulti f identity) - (defmethod f :a [_] :a) - (defmethod f :b [_] :b) - (remove-method f :a) - (assert-eq (get-method f :a) nil) - (defmethod f :default [_] :default) - (assert-eq (get-method f :a) (get-method f :default)) - (assert* (not (pcall remove-method))) - (assert* (not (pcall remove-method f))) - (assert* (not (pcall remove-method f :a :b)))) - - (testing "remove-all-methods" - (defmulti f identity) - (defmethod f :a [_] :a) - (defmethod f :b [_] :b) - (defmethod f :default [_] :default) - (remove-all-methods f) - (assert-eq (methods f) {}) - (assert* (not (pcall remove-all-methods))) - (assert* (not (pcall remove-all-methods f f))))) - -(deftest math-functions - (testing "add" - (assert-eq (add) 0) - (assert-eq (add 1) 1) - (assert-eq (add -1) -1) - (assert-eq (add 1 2) 3) - (assert-eq (add 1 2 3) 6) - (assert-eq (add 1 2 3 4) 10) - (assert-eq (add 1 2 3 4 5) 15)) - - (testing "sub" - (assert-eq (sub) 0) - (assert-eq (sub 1) -1) - (assert-eq (sub -1) 1) - (assert-eq (sub 1 2) -1) - (assert-eq (sub 1 2 3) -4) - (assert-eq (sub 1 2 3 4) -8) - (assert-eq (sub 1 2 3 4 5) -13)) - - (testing "mul" - (assert-eq (mul) 1) - (assert-eq (mul 1) 1) - (assert-eq (mul -1) -1) - (assert-eq (mul 1 2) 2) - (assert-eq (mul 1 2 3) 6) - (assert-eq (mul 1 2 3 4) 24) - (assert-eq (mul 1 2 3 4 5) 120)) - - (testing "div" - (assert* (not (pcall div))) - (assert-eq (div 1) 1) - (assert-eq (div -1) -1) - (assert-eq (div 1 2) (/ 1 2)) - (assert-eq (div 1 2 3) (/ 1 2 3)) - (assert-eq (div 1 2 3 4) (/ 1 2 3 4)) - (assert-eq (div 1 2 3 4 5) (/ 1 2 3 4 5)))) - -(deftest comparison-functions - (testing "le" - (assert* (not (pcall le))) - (assert* (le 1)) - (assert* (le 1 2)) - (assert* (le 1 2 2)) - (assert* (le 1 2 3 4)) - (assert* (not (le 2 1))) - (assert* (not (le 2 1 3))) - (assert* (not (le 1 2 4 3)))) - - (testing "lt" - (assert* (not (pcall lt))) - (assert* (lt 1)) - (assert* (lt 1 2)) - (assert* (lt 1 2 3)) - (assert* (lt 1 2 3 4)) - (assert* (not (lt 2 1))) - (assert* (not (lt 2 1 3))) - (assert* (not (lt 1 2 4 4)))) - - (testing "ge" - (assert* (not (pcall ge))) - (assert* (ge 2)) - (assert* (ge 2 1)) - (assert* (ge 3 3 2)) - (assert* (ge 4 3 2 -1)) - (assert* (not (ge 1 2))) - (assert* (not (ge 2 1 3))) - (assert* (not (ge 1 2 4 4)))) - - (testing "gt" - (assert* (not (pcall gt))) - (assert* (gt 2)) - (assert* (gt 2 1)) - (assert* (gt 3 2 1)) - (assert* (gt 4 3 2 -1)) - (assert* (not (gt 1 2))) - (assert* (not (gt 2 1 3))) - (assert* (not (gt 1 2 4 4))))) - -(deftest vec - (testing "vec" - (assert-eq (vector) []) - (assert-eq (vector 1) [1]) - (assert-eq (vector 1 2 3) [1 2 3]) - (assert-eq (getmetatable (vector 1 2 3)) {:cljlib/table-type :seq}))) - -(deftest hash-map - (testing "hash-map" - (assert* (not (pcall hash-map :a))) - (assert-eq (hash-map) {}) - (assert-eq (hash-map :a 1) {:a 1}) - (assert-eq (hash-map :a 1 :b 2 :c 3) {:a 1 :b 2 :c 3}) - (assert-eq (getmetatable (hash-map)) {:cljlib/table-type :table}))) diff --git a/test/fn.fnl b/test/fn.fnl deleted file mode 100644 index ec4e835..0000000 --- a/test/fn.fnl +++ /dev/null @@ -1,44 +0,0 @@ -(require-macros :test.test) -(require-macros :cljlib-macros) - -(deftest fn* - (testing "fn* meta" - (fn* f - "docstring" - [x] x) - (assert-eq (meta f) (when-meta {:fnl/docstring "docstring" - :fnl/arglist ["x"]})) - - (fn* f - "docstring" - ([x] x)) - (assert-eq (meta f) (when-meta {:fnl/docstring "docstring" - :fnl/arglist ["x"]})) - - (fn* f - "docstring" - ([x] x) - ([x y] (+ x y))) - (assert-eq (meta f) (when-meta {:fnl/docstring "docstring" - :fnl/arglist ["\n [x]" - "\n [x y]"]})) - - (fn* f - "docstring" - ([x] x) - ([x y] (+ x y)) - ([x y & z] (+ x y))) - (assert-eq (meta f) (when-meta {:fnl/docstring "docstring" - :fnl/arglist ["\n [x]" - "\n [x y]" - "\n [x y & z]"]})))) - -(deftest fn& - (testing "fn& meta" - (fn& f "docstring" [x] x) - (assert-eq (meta f) (when-meta {:fnl/docstring "docstring" - :fnl/arglist ["x"]})) - - (fn& f "docstring" [...] [...]) - (assert-eq (meta f) (when-meta {:fnl/docstring "docstring" - :fnl/arglist ["..."]})))) diff --git a/test/macros.fnl b/test/macros.fnl deleted file mode 100644 index 1876126..0000000 --- a/test/macros.fnl +++ /dev/null @@ -1,168 +0,0 @@ -(require-macros :test.test) -(require-macros :cljlib-macros) - -(deftest into - (testing "into" - (assert-eq (into [] []) []) - (assert-eq (into [1 2 3] []) [1 2 3]) - (assert-eq (into [1 2 3] [4 5 6]) [1 2 3 4 5 6]) - - (assert-eq (into {} {}) {}) - (assert-eq (into {:a 1} {}) {:a 1}) - (assert-eq (into {:a 1} {:b 2}) {:a 1 :b 2}) - - ;; different bodies are being used so worth testing - (assert-eq (into [] {}) []) - (assert-eq (into {} []) []) - - ;; can't transform table with more than one key-value pair, as order - ;; is undeterminitive - (assert-eq (into [] {:a 1}) [[:a 1]]) - (assert-eq (into [[:b 2]] {:a 1}) [[:b 2] [:a 1]]) - (assert-eq (into [[:c 3]] {}) [[:c 3]]) - - (assert-eq (into {} [[:c 3] [:a 1] [:b 2]]) {:a 1 :b 2 :c 3}) - (assert-eq (into {:d 4} [[:c 3] [:a 1] [:b 2]]) {:a 1 :b 2 :c 3 :d 4}) - (assert-eq (into {:a 0 :b 0 :c 0} [[:c 3] [:a 1] [:b 2]]) {:a 1 :b 2 :c 3}) - - (let [a (fn [] {:a 1}) - b (fn [] [[:b 2]])] - (assert-eq (into (a) (b)) {:a 1 :b 2}) - (assert-eq (into (b) (a)) [[:b 2] [:a 1]]) - (let [c []] - (assert-eq (into c (b)) [[:b 2]])) - (let [c []] - (assert-eq (into c (a)) [[:a 1]])) - (let [c []] - (assert-eq (into (b) c) (b)) - (assert-eq (into (a) c) (a)))) - - (let [a {} - b []] - (assert-eq (into a [1 2 3]) [1 2 3]) - (assert-eq (into b [1 2 3]) [1 2 3])) - (let [a {} - b []] - (assert-eq (into b {:a 1}) [[:a 1]])))) - -(deftest let-variants - (testing "when-let" - (assert-eq (when-let [a 4] a) 4) - (assert* (not (when-let [a false] a)) "(not (when-let [a false] a))") - (assert* (not (when-let [a nil] a)) "(not (when-let [a nil] a))")) - - (testing "when-some" - (assert-eq (when-some [a [1 2 3]] a) [1 2 3]) - (assert-eq (when-some [a false] a) false) - (assert* (not (when-some [a nil] a)) "(when-some [a nil] a)")) - - (testing "if-let" - (assert-eq (if-let [a 4] a 10) 4) - (assert-eq (if-let [a false] a 10) 10) - (assert-eq (if-let [a nil] a 10) 10)) - - (testing "if-some" - (assert-eq (if-some [a [1 2 3]] a :nothing) [1 2 3]) - (assert-eq (if-some [a false] a :nothing) false) - (assert-eq (if-some [a nil] a :nothing) :nothing))) - -(deftest multimethods - (testing "defmulti" - (defmulti x (fn [x] x)) - (assert-eq (defmulti x (fn [x] (+ x 1))) nil)) - - (testing "defmulti defalut" - (defmulti fac (fn [x] x)) - (defmethod fac 0 [_] 1) - (defmethod fac :default [x] (* x (fac (- x 1)))) - (assert-eq (fac 42) 7538058755741581312)) - - (testing "defmulti keys" - (defmulti send-data (fn [protocol data] protocol)) - (defmethod send-data :http [protocol data] (.. data " will be sent over HTTP")) - (defmethod send-data :icap [protocol data] (.. data " will be sent over ICAP")) - (assert-eq (send-data :http 42) "42 will be sent over HTTP") - (assert-eq (send-data :icap 42) "42 will be sent over ICAP") - - (defmulti send-message (fn [message] (. message :protocol))) - (defmethod send-message :http [message] (.. "sending " (. message :message) " over HTTP")) - (defmethod send-message :icap [message] (.. "sending " (. message :message) " over ICAP")) - (assert-eq (send-message {:protocol :http :message "ваыв"}) - "sending ваыв over HTTP") - (assert-eq (send-message {:protocol :icap :message 42}) - "sending 42 over ICAP")) - - (testing "defmulti with dispatch on tables" - (defmulti encounter (fn [x y] [(. x :species) (. y :species)])) - (defmethod encounter [:bunny :lion] [_ _] :run) - (defmethod encounter [:lion :bunny] [_ _] :eat) - (defmethod encounter [:lion :lion] [_ _] :fight) - (defmethod encounter [:bunny :bunny] [_ _] :mate) - - (let [l {:species :lion} - b {:species :bunny}] - (assert-eq (encounter b b) :mate) - (assert-eq (encounter l l) :fight) - (assert-eq (encounter b l) :run) - (assert-eq (encounter l b) :eat)))) - -(deftest def-macros - (testing "def" - (def {:dynamic true} a 10) - (assert-eq a 10) - (set a 20) - (assert-eq a 20) - (def a {}) - (assert-eq a {}) - (def a.b 10) - (assert-eq a.b 10) - (assert-eq b 10) - (def :dynamic c 10) - (set c 15) - (assert-eq c 15)) - - (testing "defonce" - (defonce {:dynamic true} a 10) - (assert-eq a 10) - (defonce a {}) - (assert-eq a 10) - (defonce b {}) - (defonce b.a 10) - (assert-eq b.a 10) - (assert-eq a 10))) - -(deftest meta - (testing "with-meta" - (assert-eq (meta (with-meta :a {:k :v})) (when-meta {:k :v}))) - - (testing "def meta" - (def {:doc "x"} x 10) - (assert-eq (meta x) (when-meta {:fnl/docstring "x"})) - (def {:doc "x" :dynamic true} x 10) - (assert-eq (meta x) (when-meta {:fnl/docstring "x"}))) - - (testing "defonce meta table" - (defonce {:doc "x"} x 10) - (assert-eq (meta x) (when-meta {:fnl/docstring "x"})) - (defonce {:doc "y"} x 20) - (assert-eq (meta x) (when-meta {:fnl/docstring "x"})) - (defonce {:doc "y" :dynamic true} y 20) - (assert-eq (meta y) (when-meta {:fnl/docstring "y"})))) - -(deftest empty - (testing "empty map" - (assert-eq (empty {}) {}) - (assert-eq (getmetatable (empty {})) {:cljlib/table-type :table}) - (let [a {:a 1 :b 2}] - (assert-eq (empty a) {}) - (assert-eq (getmetatable (empty a)) {:cljlib/table-type :table})) - (let [a {}] - (assert-eq (empty a) []) - (assert-eq (getmetatable (empty a)) {:cljlib/table-type :empty}))) - - (testing "empty seq" - (assert-eq (empty []) {}) - (assert-eq (getmetatable (empty [])) {:cljlib/table-type :seq}) - (let [a [:a 1 :b 2]] - (assert-eq (empty a) []) - (assert-eq (getmetatable (empty a)) {:cljlib/table-type :seq})))) diff --git a/test/test.fnl b/test/test.fnl deleted file mode 100644 index a83b510..0000000 --- a/test/test.fnl +++ /dev/null @@ -1,58 +0,0 @@ -(local test {}) - -(fn eq-fn [] - `(fn eq# [a# b#] - (if (and (= (type a#) :table) (= (type b#) :table)) - (do (var [res# count-a# count-b#] [true 0 0]) - (each [k# v# (pairs a#)] - (set res# (eq# v# (. b# k#))) - (set count-a# (+ count-a# 1)) - (when (not res#) (lua :break))) - (when res# - (each [_# _# (pairs b#)] - (set count-b# (+ count-b# 1))) - (set res# (= count-a# count-b#))) - res#) - (= a# b#)))) - -(fn test.assert-eq - [expr1 expr2 msg] - `(let [left# ,expr1 - right# ,expr2 - (res# view#) (pcall require :fennelview) - eq# ,(eq-fn) - tostr# (if res# view# tostring)] - (assert (eq# left# right#) - (or ,msg (.. "equality assertion failed - Left: " (tostr# left#) " - Right: " (tostr# right#) "\n"))))) - -(fn test.assert-ne - [expr1 expr2 msg] - `(let [left# ,expr1 - right# ,expr2 - (res# view#) (pcall require :fennelview) - eq# ,(eq-fn) - tostr# (if res# view# tostring)] - (assert (not (eq# left# right#)) - (or ,msg (.. "unequality assertion failed - Left: " (tostr# left#) " - Right: " (tostr# right#) "\n"))))) - -(fn test.assert* - [expr msg] - `(assert ,expr (.. "assertion failed for " - (or ,msg ,(tostring expr))))) - -(fn test.deftest - [name ...] - "Simple way of grouping tests" - `,((or table.unpack _G.unpack) [...])) - -(fn test.testing - [description ...] - "Define test function, print its name and run it." - `(do (io.stderr:write (.. "testing: " ,description "\n")) - ,((or table.unpack _G.unpack) [...]))) - -test diff --git a/tests/core.fnl b/tests/core.fnl new file mode 100644 index 0000000..b0c02aa --- /dev/null +++ b/tests/core.fnl @@ -0,0 +1,609 @@ +(require-macros :cljlib-macros) +(require-macros :tests.test) + +(local + {: vector + : hash-map + : apply + : seq + : first + : rest + : last + : butlast + : conj + : cons + : concat + : reduce + : reduce-kv + : mapv + : filter + : map? + : seq? + : nil? + : zero? + : pos? + : neg? + : even? + : odd? + : int? + : pos-int? + : neg-int? + : double? + : string? + : boolean? + : false? + : true? + : empty? + : not-empty + : eq + : identity + : comp + : every? + : some + : not-any? + : complement + : constantly + : range + : reverse + : inc + : dec + : assoc + : get + : get-in + : get-method + : methods + : remove-method + : remove-all-methods + : add + : sub + : mul + : div + : le + : ge + : lt + : gt} + (require :cljlib)) + +(deftest equality + (testing "comparing basetypes" + (assert-not (pcall eq)) + (assert-eq 1 1) + (assert-ne 1 2) + (assert* (eq 1 1 1 1 1)) + (assert-eq 1.0 1.0) + (assert* (eq 1.0 1.0 1.0)) + (assert* (eq 1.0 1.0 1.0)) + (assert* (eq "1" "1" "1" "1" "1"))) + + (testing "deep comparison" + (assert* (eq [])) + (assert-eq [] []) + (assert-eq [] {}) + (assert-eq [1 2] [1 2]) + (assert-ne [1] [1 2]) + (assert-ne [1 2] [1]) + (assert* (eq [1 [2]] [1 [2]] [1 [2]])) + (assert* (eq [1 [2]] [1 [2]] [1 [2]])) + (assert-not (eq [1 [2]] [1 [2]] [1 [2 [3]]])) + + (let [a {:a 1 :b 2} + b {:a 1 :b 2}] + (table.insert b 10) + (assert-ne a b)) + + (let [a [1 2 3] + b [1 2 3]] + (tset b :a 10) + (assert-ne a b)) + + (assert-eq [1 2 3] {1 1 2 2 3 3}) + + ;; TODO: decide if this is right or not. Looking from `seq' + ;; perspective, it is correct, as `(seq {4 1})' and `(seq [nil nil + ;; nil 1])' both yield `{4 1}'. From Lua's point this is not the + ;; same thing, for example because the sizes of these tables are + ;; different. + (assert-eq {4 1} [nil nil nil 1]))) + +(testing "range" + (assert-not (pcall range)) + (assert-eq (range 10) [0 1 2 3 4 5 6 7 8 9]) + (assert-eq (range -5 5) [-5 -4 -3 -2 -1 0 1 2 3 4]) + (assert-eq [0 0.2 0.4 0.6 0.8] [0 0.2 0.4 0.6 0.8]) + (assert-eq (range 0 1 0.2) (range 0 1 0.2))) + +(deftest predicates + (testing "zero?" + (assert* (zero? 0)) + (assert* (zero? -0)) + (assert-not (zero? 1))) + + (testing "int?" + (assert* (int? 1)) + (assert-not (int? 1.1))) + + (testing "pos?" + (assert* (pos? 1)) + (assert* (and (not (pos? 0)) (not (pos? -1))))) + + (testing "neg?" + (assert* (neg? -1)) + (assert* (and (not (neg? 0)) (not (neg? 1))))) + + (testing "pos-int?" + (assert* (pos-int? 42)) + (assert-not (pos-int? 4.2))) + + (testing "neg-int?" + (assert* (neg-int? -42)) + (assert-not (neg-int? -4.2))) + + (testing "string?" + (assert* (string? :s))) + + (testing "double?" + (assert* (double? 3.3)) + (assert-not (double? 3.0))) + + (testing "map?" + (assert* (map? {:a 1})) + (assert-not (map? {})) + (assert* (map? (empty {}))) + (assert-not (map? (empty [])))) + + (testing "seq?" + (assert-not (seq? [])) + (assert* (seq? [{:a 1}])) + (assert-not (seq? {})) + (assert-not (seq? {:a 1})) + (assert* (seq? (empty []))) + (assert-not (seq? (empty {})))) + + (testing "nil?" + (assert* (nil?)) + (assert* (nil? nil)) + (assert-not (nil? 1))) + + (testing "odd?" + (assert* (odd? 3)) + (assert* (odd? -3)) + (assert-not (odd? 2)) + (assert-not (odd? -2))) + + (testing "even?" + (assert* (even? 2)) + (assert* (even? -2)) + (assert-not (even? 23)) + (assert-not (even? -23))) + + (testing "true?" + (assert* (true? true)) + (assert-not (true? false)) + (assert-not (true? 10)) + (assert-not (true? :true))) + + (testing "false?" + (assert* (false? false)) + (assert-not (false? true)) + (assert-not (false? 10)) + (assert-not (false? :true))) + + (testing "boolean?" + (assert* (boolean? true)) + (assert* (boolean? false)) + (assert-not (boolean? :false)) + (assert-not (boolean? (fn [] true))))) + +(deftest sequence-functions + (testing "seq" + (assert-eq (seq []) nil) + (assert-eq (seq {}) nil) + (assert-eq (seq [1]) [1]) + (assert-eq (seq [1 2 3]) [1 2 3]) + (assert-eq (seq {:a 1}) [["a" 1]])) + + (testing "mapv" + (assert-not (pcall mapv)) + (assert-not (pcall mapv #(do nil))) + (assert-eq (mapv #(* $ $) [1 2 3 4]) [1 4 9 16]) + + (assert-eq (into {} (mapv (fn [[k v]] [k (* v v)]) {:a 1 :b 2 :c 3})) + (into {} [[:a 1] [:b 4] [:c 9]])) + + (assert-eq (into {} (mapv (fn [[k1 v1] [k2 v2]] [k1 (* v1 v2)]) + {:a 1 :b 2 :c 3} + {:a -1 :b 0 :c 2})) + {:a -1 :b 0 :c 6}) + (assert-eq (mapv #(* $1 $2 $3) [1] [2] [-1]) [-2]) + (assert-eq (mapv string.upper ["a" "b" "c"]) ["A" "B" "C"]) + (assert-eq (mapv #(+ $1 $2 $3 $4) [1 -1] [2 -2] [3 -3] [4 -4]) [(+ 1 2 3 4) (+ -1 -2 -3 -4)]) + (assert-eq (mapv (fn [f-name s-name company position] + (.. f-name " " s-name " works as " position " at " company)) + ["Bob" "Alice"] + ["Smith" "Watson"] + ["Happy Days co." "Coffee With You"] + ["secretary" "chief officer"]) + ["Bob Smith works as secretary at Happy Days co." + "Alice Watson works as chief officer at Coffee With You"])) + + (testing "reduce" + (fn* add + ([] 0) + ([a] a) + ([a b] (+ a b)) + ([a b & c] + (var res (+ a b)) + (each [_ v (ipairs c)] + (set res (+ res v))) + res)) + + (assert-eq (reduce add []) 0) + (assert-eq (reduce add [1]) 1) + (assert-eq (reduce add [1 2]) 3) + (assert-eq (reduce add (range 10)) 45) + (assert-eq (reduce add -3 (range 10)) 42) + (assert-eq (reduce add 10 []) 10) + (assert-eq (reduce add 10 [1]) 11) + (assert-eq (reduce add 10 nil) 10) + (assert-not (pcall reduce)) + (assert-not (pcall reduce add))) + + (testing "reduce reference implementation" + (fn mapping [f] + (fn [reducing] + (fn [result input] + (reducing result (f input))))) + + (fn reduce- [f init [x & tbl]] + (if x (reduce- f (f init x) tbl) init)) + + (assert-eq (reduce add (range 10)) (reduce- add 0 (range 10))) + (assert-eq (reduce ((mapping inc) add) 0 (range 10)) + (reduce- ((mapping inc) add) 0 (range 10)))) + + (testing "filter" + (assert-not (pcall filter)) + (assert-not (pcall filter even?)) + (assert-eq (filter even? (range 10)) [0 2 4 6 8]) + (assert-eq (filter odd? (range 10)) [1 3 5 7 9]) + (assert-eq (filter map? [{:a 1} {5 1} [1 2] [] {}]) [{:a 1} {5 1}]) + (assert-eq (filter seq? [{:a 1} {5 1} [1 2] [] {}]) [[1 2]])) + + (testing "concat" + (assert-eq (concat) nil) + (assert-eq (concat []) []) + (assert-eq (concat [1 2 3]) [1 2 3]) + (assert-eq (concat [1 2 3] [4 5 6]) [1 2 3 4 5 6]) + (assert-eq (concat [1 2] [3 4] [5 6]) [1 2 3 4 5 6]) + (assert-eq (concat {:a 1} {:b 2}) [[:a 1] [:b 2]]) + (assert-eq (concat [[:a 1]] {:b 2}) [[:a 1] [:b 2]]) + (assert-eq (concat {:a 1} [[:b 2]]) [[:a 1] [:b 2]]) + (assert-eq (concat [] [[:b 2]]) [[:b 2]]) + (assert-eq (concat [] []) []) + (assert-not (pcall concat 1)) + (assert-not (pcall concat 1 2)) + (assert-not (pcall concat 1 [])) + (assert-not (pcall concat [] 2)) + (assert-not (pcall concat [1] 2))) + + (testing "reverse" + (assert-eq (reverse []) nil) + (assert-eq (reverse [1 2 3]) [3 2 1]) + (assert-eq (reverse {:a 1}) [[:a 1]])) + + (testing "conj" + (assert-eq (conj) []) + (assert-eq (conj [1] nil) [1]) + (assert-eq (conj [] 1 2 3) [1 2 3]) + (assert-eq (conj [0] 1 2 3) [0 1 2 3]) + (assert-eq (conj {:a 1} [:b 2]) {:a 1 :b 2}) + (assert-eq (conj {:a 1}) {:a 1}) + (assert-eq (conj [1] 2 3 4 5 6 7) [1 2 3 4 5 6 7])) + + (testing "cons" + (assert-eq (cons nil [1]) [1]) + (assert-eq (cons 1 []) [1]) + (assert-eq (cons 1 [0]) [1 0])) + + (testing "first" + (assert-eq (first [1 2 3]) 1) + (assert-eq (first {:a 1}) [:a 1]) + (assert-eq (first []) nil)) + + (testing "last" + (assert-eq (last [1 2 3]) 3) + (assert-eq (last []) nil) + (assert-eq (last nil) nil) + (assert-eq (last {:a 1}) [:a 1])) + + (testing "rest" + (assert-eq (rest [1 2 3]) [2 3]) + (assert-eq (rest {:a 1}) []) + (assert-eq (rest []) []) + (assert-eq (rest nil) [])) + + (testing "butlast" + (assert-eq (butlast [1 2 3]) [1 2]) + (assert-eq (butlast {:a 1}) nil) + (assert-eq (butlast []) nil) + (assert-eq (butlast nil) nil)) + + (testing "reduce-kv" + (assert-eq (reduce-kv #(+ $1 $3) 0 {:a 1 :b 2 :c 3}) 6) + (assert-not (pcall reduce-kv #(+ $1 $3) 0)) + (assert-not (pcall reduce-kv #(+ $1 $3))) + (assert-not (pcall reduce-kv))) + + (testing "assoc" + (assert-not (pcall assoc)) + (assert-not (pcall assoc {})) + (assert-eq (assoc {} :a 1) {:a 1}) + (assert-eq (assoc {} :a 1 :b 2 :c 3 :d 4) {:a 1 :b 2 :c 3 :d 4}))) + +(deftest function-manipulation + (testing "constantly" + (let [always-nil (constantly nil)] + (assert-eq (always-nil) nil) + (assert-eq (always-nil 1) nil) + (assert-eq (always-nil 1 2 3 4 "5") nil)) + + (let [always-true (constantly true)] + (assert* (always-true)) + (assert* (always-true false)))) + + (testing "complement" + (assert* ((complement #(do false)))) + (assert* ((complement nil?) 10)) + (assert* ((complement every?) double? [1 2 3 4])) + (assert* ((complement #(= $1 $2 $3)) 1 1 2 1)) + (assert* ((complement #(= $1 $2)) 1 2))) + + (testing "apply" + (fn* add + ([x] x) + ([x y] (+ x y)) + ([x y & zs] + (add (+ x y) ((or _G.unpack table.unpack) zs)))) + (assert-eq (apply add [1 2 3 4]) 10) + (assert-eq (apply add -1 [1 2 3 4]) 9) + (assert-eq (apply add -2 -1 [1 2 3 4]) 7) + (assert-eq (apply add -3 -2 -1 [1 2 3 4]) 4) + (assert-eq (apply add -4 -3 -2 -1 [1 2 3 4]) 0) + (assert-eq (apply add -5 -4 -3 -2 -1 [1 2 3 4]) -5) + (assert-not (pcall apply)) + (assert-not (pcall apply add))) + + (testing "comp" + (assert-eq ((comp) 10) 10) + (fn square [x] (* x x)) + (assert-eq (comp square) square) + (assert-eq ((comp square inc) 6) 49) + (assert-eq ((comp #(- $ 7) square inc inc inc inc inc inc inc) 0) 42) + (fn sum-squares [x y] (+ (* x x) (* y y))) + (assert-eq ((comp square inc sum-squares) 2 3) 196) + (fn f [a b c] (+ a b c)) + (assert-eq ((comp inc f) 1 2 3) 7) + (fn g [a b c d] (+ a b c d)) + (assert-eq ((comp inc g) 1 2 3 4) 11) + (fn h [a b c d e f] (+ a b c d e f)) + (assert-eq ((comp inc h) 1 2 3 4 5 6) 22)) + + (testing "identity" + (fn f [] nil) + (assert-eq (identity 1) 1) + (assert-eq (identity {:a 1 :b 2}) {:a 1 :b 2}) + (assert-eq (identity [1 2 3]) [1 2 3]) + (assert-eq (identity "abc") "abc") + (assert-eq (identity f) f))) + +(deftest sequence-predicates + (testing "some" + (assert-not (pcall some)) + (assert-not (pcall some pos-int?)) + (assert* (some pos-int? [-1 1.1 2.3 -5.5 42 10 -27])) + (assert-not (some pos-int? {:a 1})) + (assert* (some pos-int? [{:a 1} "1" -1 1]))) + + (testing "not-any?" + (assert-not (pcall not-any?)) + (assert-not (pcall not-any? pos-int?)) + (assert* (not-any? pos-int? [-1 1.1 2.3 -5.5 -42 -10 -27])) + (assert* (not-any? pos-int? {:a 1})) + (assert-not (not-any? pos-int? [1 2 3 4 5]))) + + (testing "every?" + (assert-not (pcall every?)) + (assert-not (pcall every? pos-int?)) + (assert-not (every? pos-int? [-1 1.1 2.3 -5.5 42 10 -27])) + (assert-not (every? pos-int? {:a 1})) + (assert* (every? pos-int? [1 2 3 4 5]))) + + (testing "empty?" + (assert-not (pcall empty?)) + (assert* (empty? [])) + (assert* (empty? {})) + (assert* (empty? "")) + (assert-not (empty? "1")) + (assert-not (empty? [1])) + (assert-not (empty? {:a 1})) + (assert-not (pcall empty? 10))) + + (testing "not-empty" + (assert-not (pcall not-empty)) + (assert-eq (not-empty []) nil) + (assert-eq (not-empty {}) nil) + (assert-eq (not-empty "") nil) + (assert-eq (not-empty "1") "1") + (assert-eq (not-empty [1]) [1]) + (assert-eq (not-empty {:a 1}) {:a 1}))) + +(deftest math-functions + (testing "inc" + (assert-eq (inc 1) 2) + (assert-eq (inc -1) 0) + (assert-not (pcall inc)) + (assert-not (pcall inc nil))) + + (testing "dec" + (assert-eq (dec 1) 0) + (assert-eq (dec -1) -2) + (assert-not (pcall dec)) + (assert-not (pcall dec nil)))) + +(deftest table-access + (testing "get" + (assert-eq (get {:key1 10 :key2 20} :key1) 10) + (assert-eq (get {:key1 10 :key2 20} :key1 false) 10) + (assert-eq (get {:key1 10 :key2 20} :key3 false) false) + (assert-eq (get {:key1 10 :key2 20} :key3) nil) + (assert-not (pcall get)) + (assert-not (pcall get {}))) + + (testing "get-in" + (local t {:a {:b {:c 10}}}) + (assert-eq (get-in t [:a]) {:b {:c 10}}) + (assert-eq (get-in t [:a :b]) {:c 10}) + (assert-eq (get-in t [:a :b :c]) 10) + (assert-eq (get-in t [:a :b :c] false) 10) + (assert-eq (get-in t [:a :b :d] false) false) + (assert-eq (get-in t [:a :b :d]) nil) + (assert-eq (get-in t []) t) + (assert-not (pcall get-in)) + (assert-not (pcall get-in {})))) + +(deftest methods + (testing "methods" + (defmulti f identity) + (defmethod f :a [_] :a) + (defmethod f :b [_] :b) + (defmethod f :c [x] (* x x)) + (assert-eq (methods f) (. (getmetatable f) :multimethods)) + (assert-not (pcall methods)) + (assert-not (pcall methods f f))) + + (testing "get-method" + (defmulti f identity) + (defmethod f :a [_] :a) + (defmethod f :b [_] :b) + (defmethod f :c [x] (* x x)) + (assert-eq ((get-method f :a) 10) :a) + (assert-eq ((get-method f :b) 20) :b) + (assert-eq ((get-method f :c) 4) 16) + (assert-not (pcall get-method)) + (assert-not (pcall get-method f)) + (assert-not (pcall get-method f :a :b))) + + (testing "remove-method" + (defmulti f identity) + (defmethod f :a [_] :a) + (defmethod f :b [_] :b) + (remove-method f :a) + (assert-eq (get-method f :a) nil) + (defmethod f :default [_] :default) + (assert-eq (get-method f :a) (get-method f :default)) + (assert-not (pcall remove-method)) + (assert-not (pcall remove-method f)) + (assert-not (pcall remove-method f :a :b))) + + (testing "remove-all-methods" + (defmulti f identity) + (defmethod f :a [_] :a) + (defmethod f :b [_] :b) + (defmethod f :default [_] :default) + (remove-all-methods f) + (assert-eq (methods f) {}) + (assert-not (pcall remove-all-methods)) + (assert-not (pcall remove-all-methods f f)))) + +(deftest math-functions + (testing "add" + (assert-eq (add) 0) + (assert-eq (add 1) 1) + (assert-eq (add -1) -1) + (assert-eq (add 1 2) 3) + (assert-eq (add 1 2 3) 6) + (assert-eq (add 1 2 3 4) 10) + (assert-eq (add 1 2 3 4 5) 15)) + + (testing "sub" + (assert-eq (sub) 0) + (assert-eq (sub 1) -1) + (assert-eq (sub -1) 1) + (assert-eq (sub 1 2) -1) + (assert-eq (sub 1 2 3) -4) + (assert-eq (sub 1 2 3 4) -8) + (assert-eq (sub 1 2 3 4 5) -13)) + + (testing "mul" + (assert-eq (mul) 1) + (assert-eq (mul 1) 1) + (assert-eq (mul -1) -1) + (assert-eq (mul 1 2) 2) + (assert-eq (mul 1 2 3) 6) + (assert-eq (mul 1 2 3 4) 24) + (assert-eq (mul 1 2 3 4 5) 120)) + + (testing "div" + (assert-not (pcall div)) + (assert-eq (div 1) 1) + (assert-eq (div -1) -1) + (assert-eq (div 1 2) (/ 1 2)) + (assert-eq (div 1 2 3) (/ 1 2 3)) + (assert-eq (div 1 2 3 4) (/ 1 2 3 4)) + (assert-eq (div 1 2 3 4 5) (/ 1 2 3 4 5)))) + +(deftest comparison-functions + (testing "le" + (assert-not (pcall le)) + (assert* (le 1)) + (assert* (le 1 2)) + (assert* (le 1 2 2)) + (assert* (le 1 2 3 4)) + (assert-not (le 2 1)) + (assert-not (le 2 1 3)) + (assert-not (le 1 2 4 3))) + + (testing "lt" + (assert-not (pcall lt)) + (assert* (lt 1)) + (assert* (lt 1 2)) + (assert* (lt 1 2 3)) + (assert* (lt 1 2 3 4)) + (assert-not (lt 2 1)) + (assert-not (lt 2 1 3)) + (assert-not (lt 1 2 4 4))) + + (testing "ge" + (assert-not (pcall ge)) + (assert* (ge 2)) + (assert* (ge 2 1)) + (assert* (ge 3 3 2)) + (assert* (ge 4 3 2 -1)) + (assert-not (ge 1 2)) + (assert-not (ge 2 1 3)) + (assert-not (ge 1 2 4 4))) + + (testing "gt" + (assert-not (pcall gt)) + (assert* (gt 2)) + (assert* (gt 2 1)) + (assert* (gt 3 2 1)) + (assert* (gt 4 3 2 -1)) + (assert-not (gt 1 2)) + (assert-not (gt 2 1 3)) + (assert-not (gt 1 2 4 4)))) + +(deftest vec + (testing "vec" + (assert-eq (vector) []) + (assert-eq (vector 1) [1]) + (assert-eq (vector 1 2 3) [1 2 3]) + (assert-eq (getmetatable (vector 1 2 3)) {:cljlib/table-type :seq}))) + +(deftest hash-map + (testing "hash-map" + (assert-not (pcall hash-map :a)) + (assert-eq (hash-map) {}) + (assert-eq (hash-map :a 1) {:a 1}) + (assert-eq (hash-map :a 1 :b 2 :c 3) {:a 1 :b 2 :c 3}) + (assert-eq (getmetatable (hash-map)) {:cljlib/table-type :table}))) diff --git a/tests/fn.fnl b/tests/fn.fnl new file mode 100644 index 0000000..e508541 --- /dev/null +++ b/tests/fn.fnl @@ -0,0 +1,44 @@ +(require-macros :tests.test) +(require-macros :cljlib-macros) + +(deftest fn* + (testing "fn* meta" + (fn* f + "docstring" + [x] x) + (assert-eq (meta f) (when-meta {:fnl/docstring "docstring" + :fnl/arglist ["x"]})) + + (fn* f + "docstring" + ([x] x)) + (assert-eq (meta f) (when-meta {:fnl/docstring "docstring" + :fnl/arglist ["x"]})) + + (fn* f + "docstring" + ([x] x) + ([x y] (+ x y))) + (assert-eq (meta f) (when-meta {:fnl/docstring "docstring" + :fnl/arglist ["\n [x]" + "\n [x y]"]})) + + (fn* f + "docstring" + ([x] x) + ([x y] (+ x y)) + ([x y & z] (+ x y))) + (assert-eq (meta f) (when-meta {:fnl/docstring "docstring" + :fnl/arglist ["\n [x]" + "\n [x y]" + "\n [x y & z]"]})))) + +(deftest fn& + (testing "fn& meta" + (fn& f "docstring" [x] x) + (assert-eq (meta f) (when-meta {:fnl/docstring "docstring" + :fnl/arglist ["x"]})) + + (fn& f "docstring" [...] [...]) + (assert-eq (meta f) (when-meta {:fnl/docstring "docstring" + :fnl/arglist ["..."]})))) diff --git a/tests/macros.fnl b/tests/macros.fnl new file mode 100644 index 0000000..9b2e6ac --- /dev/null +++ b/tests/macros.fnl @@ -0,0 +1,198 @@ +(require-macros :tests.test) +(require-macros :cljlib-macros) + +(deftest into + (testing "into" + (assert-eq (into [] nil) []) + (assert-eq (into nil nil) nil) + (assert-eq (into nil [1 2 3]) [1 2 3]) + + (assert-eq (into [] []) []) + (assert-eq (into [1 2 3] []) [1 2 3]) + (assert-eq (into [1 2 3] [4 5 6]) [1 2 3 4 5 6]) + + (assert-eq (into {} {}) {}) + (assert-eq (into {:a 1} {}) {:a 1}) + (assert-eq (into {:a 1} {:b 2}) {:a 1 :b 2}) + + ;; different bodies are being used at compile time so worth testing + (assert-eq (into [] {}) []) + (assert-eq (into {} []) []) + (assert-eq (. (getmetatable (into [] {})) :cljlib/table-type) :seq) + (assert-eq (. (getmetatable (into {} [])) :cljlib/table-type) :table) + (let [a []] (assert-eq (. (getmetatable (into a a)) :cljlib/table-type) :seq)) + + ;; can't transform table with more than one key-value pair, as order + ;; is undeterminitive + (assert-eq (into [] {:a 1}) [[:a 1]]) + (assert-eq (into [[:b 2]] {:a 1}) [[:b 2] [:a 1]]) + (assert-eq (into [[:c 3]] {}) [[:c 3]]) + + (assert-eq (into {} [[:c 3] [:a 1] [:b 2]]) {:a 1 :b 2 :c 3}) + (assert-eq (into {:d 4} [[:c 3] [:a 1] [:b 2]]) {:a 1 :b 2 :c 3 :d 4}) + (assert-eq (into {:a 0 :b 0 :c 0} [[:c 3] [:a 1] [:b 2]]) {:a 1 :b 2 :c 3}) + + (let [a (fn [] {:a 1}) + b (fn [] [[:b 2]])] + (assert-eq (into (a) (b)) {:a 1 :b 2}) + (assert-eq (into (b) (a)) [[:b 2] [:a 1]]) + (let [c []] + (assert-eq (into c (b)) [[:b 2]])) + (let [c []] + (assert-eq (into c (a)) [[:a 1]])) + (let [c []] + (assert-eq (into (b) c) (b)) + (assert-eq (into (a) c) (a)))) + + (let [a {} + b []] + (assert-eq (into a [1 2 3]) [1 2 3]) + (assert-eq (into b [1 2 3]) [1 2 3])) + (let [a {} + b []] + (assert-eq (into b {:a 1}) [[:a 1]])))) + +(deftest let-variants + (testing "when-let" + (assert-eq (when-let [a 4] a) 4) + (assert-not (when-let [a false] a) "(not (when-let [a false] a))") + (assert-not (when-let [a nil] a) "(not (when-let [a nil] a))")) + + (testing "when-some" + (assert-eq (when-some [a [1 2 3]] a) [1 2 3]) + (assert-eq (when-some [a false] a) false) + (assert-not (when-some [a nil] a) "(when-some [a nil] a)")) + + (testing "if-let" + (assert-eq (if-let [a 4] a 10) 4) + (assert-eq (if-let [a false] a 10) 10) + (assert-eq (if-let [a nil] a 10) 10)) + + (testing "if-some" + (assert-eq (if-some [a [1 2 3]] a :nothing) [1 2 3]) + (assert-eq (if-some [a false] a :nothing) false) + (assert-eq (if-some [a nil] a :nothing) :nothing))) + +(deftest multimethods + (testing "defmulti" + (defmulti x (fn [x] x)) + (assert-eq (defmulti x (fn [x] (+ x 1))) nil)) + + (testing "defmulti defalut" + (defmulti fac (fn [x] x)) + (defmethod fac 0 [_] 1) + (defmethod fac :default [x] (* x (fac (- x 1)))) + (assert-eq (fac 42) 7538058755741581312)) + + (testing "defmulti keys" + (defmulti send-data (fn [protocol data] protocol)) + (defmethod send-data :http [protocol data] (.. data " will be sent over HTTP")) + (defmethod send-data :icap [protocol data] (.. data " will be sent over ICAP")) + (assert-eq (send-data :http 42) "42 will be sent over HTTP") + (assert-eq (send-data :icap 42) "42 will be sent over ICAP") + + (defmulti send-message (fn [message] (. message :protocol))) + (defmethod send-message :http [message] (.. "sending " (. message :message) " over HTTP")) + (defmethod send-message :icap [message] (.. "sending " (. message :message) " over ICAP")) + (assert-eq (send-message {:protocol :http :message "ваыв"}) + "sending ваыв over HTTP") + (assert-eq (send-message {:protocol :icap :message 42}) + "sending 42 over ICAP")) + + (testing "defmulti with dispatch on tables" + (defmulti encounter (fn [x y] [(. x :species) (. y :species)])) + (defmethod encounter [:bunny :lion] [_ _] :run) + (defmethod encounter [:lion :bunny] [_ _] :eat) + (defmethod encounter [:lion :lion] [_ _] :fight) + (defmethod encounter [:bunny :bunny] [_ _] :mate) + + (let [l {:species :lion} + b {:species :bunny}] + (assert-eq (encounter b b) :mate) + (assert-eq (encounter l l) :fight) + (assert-eq (encounter b l) :run) + (assert-eq (encounter l b) :eat))) + + (testing "defmulti default name" + (defmulti f (fn [x] x) :default :my-default) + (defmethod f :my-default [_] 42) + (assert-eq (f 10) 42)) + + + (testing "defmulti docstring" + (defmulti f "documentation" (fn [x] x)) + (assert-eq (meta f) (when-meta {:fnl/docstring "documentation"})) + (defmulti g "documentation" (fn [x] x) :default 0) + (assert-eq (meta g) (when-meta {:fnl/docstring "documentation"}))) + + (testing "defmulti with multiple arity" + (defmulti f (fn* ([x] x) ([x y] [x y]))) + (defmethod f :default ([_] :def) ([_ _] :def2)) + (defmethod f :4 ([x] (.. x :2))) + (defmethod f [:4 :2] ([x y] 42)) + + (assert-eq (f 0) :def) + (assert-eq (f 0 1) :def2) + (assert-eq (f :4) :42) + (assert-eq (f :4 :2) 42))) + +(deftest def-macros + (testing "def" + (def {:dynamic true} a 10) + (assert-eq a 10) + (set a 20) + (assert-eq a 20) + (def a {}) + (assert-eq a {}) + (def a.b 10) + (assert-eq a.b 10) + (assert-eq b 10) + (def :dynamic c 10) + (set c 15) + (assert-eq c 15)) + + (testing "defonce" + (defonce {:dynamic true} a 10) + (assert-eq a 10) + (defonce a {}) + (assert-eq a 10) + (defonce b {}) + (defonce b.a 10) + (assert-eq b.a 10) + (assert-eq a 10))) + +(deftest meta + (testing "with-meta" + (assert-eq (meta (with-meta :a {:k :v})) (when-meta {:k :v}))) + + (testing "def meta" + (def {:doc "x"} x 10) + (assert-eq (meta x) (when-meta {:fnl/docstring "x"})) + (def {:doc "x" :dynamic true} x 10) + (assert-eq (meta x) (when-meta {:fnl/docstring "x"}))) + + (testing "defonce meta table" + (defonce {:doc "x"} x 10) + (assert-eq (meta x) (when-meta {:fnl/docstring "x"})) + (defonce {:doc "y"} x 20) + (assert-eq (meta x) (when-meta {:fnl/docstring "x"})) + (defonce {:doc "y" :dynamic true} y 20) + (assert-eq (meta y) (when-meta {:fnl/docstring "y"})))) + +(deftest empty + (testing "empty map" + (assert-eq (empty {}) {}) + (assert-eq (getmetatable (empty {})) {:cljlib/table-type :table}) + (let [a {:a 1 :b 2}] + (assert-eq (empty a) {}) + (assert-eq (getmetatable (empty a)) {:cljlib/table-type :table})) + (let [a {}] + (assert-eq (empty a) []) + (assert-eq (getmetatable (empty a)) {:cljlib/table-type :empty}))) + + (testing "empty seq" + (assert-eq (empty []) {}) + (assert-eq (getmetatable (empty [])) {:cljlib/table-type :seq}) + (let [a [:a 1 :b 2]] + (assert-eq (empty a) []) + (assert-eq (getmetatable (empty a)) {:cljlib/table-type :seq})))) diff --git a/tests/test.fnl b/tests/test.fnl new file mode 100644 index 0000000..f678b6b --- /dev/null +++ b/tests/test.fnl @@ -0,0 +1,62 @@ +(local test {}) + +(fn eq-fn [] + `(fn eq# [a# b#] + (if (and (= (type a#) :table) (= (type b#) :table)) + (do (var [res# count-a# count-b#] [true 0 0]) + (each [k# v# (pairs a#)] + (set res# (eq# v# (. b# k#))) + (set count-a# (+ count-a# 1)) + (when (not res#) (lua :break))) + (when res# + (each [_# _# (pairs b#)] + (set count-b# (+ count-b# 1))) + (set res# (= count-a# count-b#))) + res#) + (= a# b#)))) + +(fn test.assert-eq + [expr1 expr2 msg] + `(let [left# ,expr1 + right# ,expr2 + (res# view#) (pcall require :fennelview) + eq# ,(eq-fn) + tostr# (if res# view# tostring)] + (assert (eq# left# right#) + (or ,msg (.. "equality assertion failed + Left: " (tostr# left#) " + Right: " (tostr# right#) "\n"))))) + +(fn test.assert-ne + [expr1 expr2 msg] + `(let [left# ,expr1 + right# ,expr2 + (res# view#) (pcall require :fennelview) + eq# ,(eq-fn) + tostr# (if res# view# tostring)] + (assert (not (eq# left# right#)) + (or ,msg (.. "unequality assertion failed + Left: " (tostr# left#) " + Right: " (tostr# right#) "\n"))))) + +(fn test.assert* + [expr msg] + `(assert ,expr (.. "assertion failed for " + (or ,msg ,(tostring expr))))) +(fn test.assert-not + [expr msg] + `(assert (not ,expr) (.. "assertion failed for " + (or ,msg ,(tostring expr))))) + +(fn test.deftest + [name ...] + "Simple way of grouping tests" + `(do ,...)) + +(fn test.testing + [description ...] + "Print test description and run it." + `(do (io.stderr:write (.. "testing: " ,description "\n")) + ,...)) + +test -- cgit v1.2.3