[eval-opt] Similar to uberscript but creates jar file.
+ prepare Download deps & pods defined in bb.edn and cache their metadata. Only an optimization, this will happen on demand when needed.
In- and output flags (only to be used with -e one-liners):
@@ -244,6 +254,7 @@ Use bb run --help to show this help output.
:feature/yaml %s
:feature/jdbc %s
:feature/postgresql %s
+ :feature/sqlite %s
:feature/hsqldb %s
:feature/oracledb %s
:feature/httpkit-client %s
@@ -262,8 +273,8 @@ Use bb run --help to show this help output.
features/xml?
features/yaml?
features/jdbc?
- features/sqlite?
features/postgresql?
+ features/sqlite?
features/hsqldb?
features/oracledb?
features/httpkit-client?
@@ -309,7 +320,9 @@ Use bb run --help to show this help output.
(def aliases
(cond->
- '{tools.cli clojure.tools.cli
+ '{str clojure.string
+ set clojure.set
+ tools.cli clojure.tools.cli
edn clojure.edn
wait babashka.wait
signal babashka.signal
@@ -333,6 +346,11 @@ Use bb run --help to show this help output.
(def clojure-main-ns (sci/create-ns 'clojure.main))
+(defn catvec [& xs]
+ (into [] cat xs))
+
+(def sci-ns (sci/create-ns 'sci.core))
+
(def namespaces
(cond->
{'user {'*input* (reify
@@ -342,6 +360,7 @@ Use bb run --help to show this help output.
'clojure.tools.cli tools-cli-namespace
'clojure.java.shell shell-namespace
'babashka.core bbcore/core-namespace
+ 'babashka.nrepl.server nrepl-server-namespace
'babashka.wait wait-namespace
'babashka.signal signal-ns
'clojure.java.io io-namespace
@@ -356,10 +375,12 @@ Use bb run --help to show this help output.
'repl (sci/new-var 'repl
(fn [& opts]
(let [opts (apply hash-map opts)]
- (repl/start-repl! @common/ctx opts))) {:ns clojure-main-ns})}
+ (repl/start-repl! @common/ctx opts))) {:ns clojure-main-ns})
+ 'with-bindings (sci/copy-var clojure-main/with-bindings clojure-main-ns)}
'clojure.test t/clojure-test-namespace
'clojure.math math-namespace
'babashka.classpath classpath-namespace
+ 'babashka.classes classes-namespace
'clojure.pprint pprint-namespace
'babashka.curl curl-namespace
'babashka.fs fs-namespace
@@ -380,7 +401,21 @@ Use bb run --help to show this help output.
'rewrite-clj.paredit rewrite/paredit-namespace
'rewrite-clj.parser rewrite/parser-namespace
'rewrite-clj.zip rewrite/zip-namespace
- 'rewrite-clj.zip.subedit rewrite/subedit-namespace}
+ 'rewrite-clj.zip.subedit rewrite/subedit-namespace
+ 'clojure.core.rrb-vector (if features/rrb-vector?
+ @(resolve 'babashka.impl.rrb-vector/rrb-vector-namespace)
+ {'catvec (sci/copy-var catvec
+ (sci/create-ns 'clojure.core.rrb-vector))})
+ 'edamame.core edamame-namespace
+ 'sci.core {'format-stacktrace (sci/copy-var sci/format-stacktrace sci-ns)
+ 'stacktrace (sci/copy-var sci/stacktrace sci-ns)
+ ;; 'eval-string (sci/copy-var sci/eval-string sci-ns)
+ ;; 'eval-string* (sci/copy-var sci/eval-string* sci-ns)
+ ;; 'init (sci/copy-var sci/init sci-ns)
+ ;; 'fork (sci/copy-var sci/fork sci-ns)
+ }
+ 'babashka.cli cli/cli-namespace
+ }
features/xml? (assoc 'clojure.data.xml @(resolve 'babashka.impl.xml/xml-namespace)
'clojure.data.xml.event @(resolve 'babashka.impl.xml/xml-event-namespace)
'clojure.data.xml.tree @(resolve 'babashka.impl.xml/xml-tree-namespace))
@@ -518,9 +553,6 @@ Use bb run --help to show this help output.
("--verbose") (recur (next options)
(assoc opts-map
:verbose? true))
- ("--force") (recur (next options)
- (assoc opts-map
- :force? true))
("--describe") (recur (next options)
(assoc opts-map
:describe? true))
@@ -614,6 +646,12 @@ Use bb run --help to show this help output.
:command-line-args (if (= "--" (second options))
(nthrest options 2)
(rest options))))
+ ("--exec", "-x",)
+ (let [options (next options)]
+ (assoc opts-map :exec (first options)
+ :command-line-args (if (= "--" (second options))
+ (nthrest options 2)
+ (rest options))))
("--run")
(parse-run-opts opts-map (next options))
("--tasks")
@@ -621,6 +659,10 @@ Use bb run --help to show this help output.
:command-line-args (next options))
("--print-deps")
(parse-print-deps-opts opts-map (next options))
+ ("--prepare")
+ (let [options (next options)]
+ (recur (next options)
+ (assoc opts-map :prepare true)))
;; fallback
(if (and opts-map
(some opts-map [:file :jar :socket-repl :expressions :main :run]))
@@ -655,6 +697,12 @@ Use bb run --help to show this help output.
("--init")
(recur (nnext options) (assoc opts-map :init (second options)))
+ ("--force" "-Sforce")
+ (recur (next options) (assoc opts-map :force? true))
+
+ ("-Sdeps")
+ (recur (nnext options) (assoc opts-map :merge-deps (second options)))
+
("--config")
(recur (nnext options) (assoc opts-map :config (second options)))
@@ -700,11 +748,32 @@ Use bb run --help to show this help output.
(def pod-namespaces (volatile! {}))
+(defn download-only?
+ "If we're preparing pods for another OS / arch, don't try to run them."
+ []
+ (let [env-os-name (System/getenv "BABASHKA_PODS_OS_NAME")
+ env-os-name-present? (not (str/blank? env-os-name))
+ sys-os-name (System/getProperty "os.name")
+ env-os-arch (System/getenv "BABASHKA_PODS_OS_ARCH")
+ env-os-arch-present? (not (str/blank? env-os-arch))
+ sys-os-arch (System/getProperty "os.arch")]
+ (when @common/debug
+ (binding [*out* *err*]
+ (println "System OS name:" sys-os-name)
+ (when env-os-name-present? (println "BABASHKA_PODS_OS_NAME:" env-os-name))
+ (println "System OS arch:" sys-os-arch)
+ (when env-os-arch-present? (println "BABASHKA_PODS_OS_ARCH:" env-os-arch))))
+ (cond
+ env-os-name-present? (not= env-os-name sys-os-name)
+ env-os-arch-present? (not= env-os-arch sys-os-arch))))
+
(defn exec [cli-opts]
(binding [*unrestricted* true]
(sci/binding [core/warn-on-reflection @core/warn-on-reflection
+ core/unchecked-math @core/unchecked-math
core/data-readers @core/data-readers
- sci/ns @sci/ns]
+ sci/ns @sci/ns
+ sci/print-length @sci/print-length]
(let [{version-opt :version
:keys [:shell-in :edn-in :shell-out :edn-out
:help :file :command-line-args
@@ -714,11 +783,13 @@ Use bb run --help to show this help output.
:main :uberscript :describe?
:jar :uberjar :clojure
:doc :run :list-tasks
- :print-deps]}
+ :print-deps :prepare]
+ exec-fn :exec}
cli-opts
_ (when debug (vreset! common/debug true))
_ (do ;; set properties
(when main (System/setProperty "babashka.main" main))
+ ;; TODO: what about exec here?
(System/setProperty "babashka.version" version))
read-next (fn [*in*]
(if (pipe-signal-received?)
@@ -750,40 +821,45 @@ Use bb run --help to show this help output.
_ (when jar
(cp/add-classpath jar))
load-fn (fn [{:keys [:namespace :reload]}]
- (or (when-let [{:keys [:loader]}
- @cp/cp-state]
- (if ;; ignore built-in namespaces when uberscripting, unless with :reload
- (and uberscript
- (not reload)
- (or (contains? namespaces namespace)
- (contains? sci-namespaces/namespaces namespace)))
- ""
- (when-let [res (cp/source-for-namespace loader namespace nil)]
- (if uberscript
- (do (swap! uberscript-sources conj (:source res))
- (uberscript/uberscript {:ctx @common/ctx
- :expressions [(:source res)]})
- {})
- res))))
- (if-let [pod (get @pod-namespaces namespace)]
- (if uberscript
- (do
- (swap! uberscript-sources conj
- (format
- "(babashka.pods/load-pod '%s \"%s\" '%s)\n"
- (:pod-spec pod) (:version (:opts pod))
- (dissoc (:opts pod)
- :version :metadata)))
- {})
- (pods/load-pod (:pod-spec pod) (:opts pod)))
- (case namespace
- clojure.spec.alpha
- (binding [*out* *err*]
- (println "[babashka] WARNING: Use the babashka-compatible version of clojure.spec.alpha, available here: https://github.com/babashka/spec.alpha"))
- clojure.core.specs.alpha
- (binding [*out* *err*]
- (println "[babashka] WARNING: clojure.core.specs.alpha is removed from the classpath, unless you explicitly add the dependency."))
- nil))))
+ (let [{:keys [loader]}
+ @cp/cp-state]
+ (or
+ (when ;; ignore built-in namespaces when uberscripting, unless with :reload
+ (and uberscript
+ (not reload)
+ (or (contains? namespaces namespace)
+ (contains? sci-namespaces/namespaces namespace)))
+ "")
+ ;; pod namespaces go before namespaces from source,
+ ;; unless reload is used
+ (when-not reload
+ (when-let [pod (get @pod-namespaces namespace)]
+ (if uberscript
+ (do
+ (swap! uberscript-sources conj
+ (format
+ "(babashka.pods/load-pod '%s \"%s\" '%s)\n"
+ (:pod-spec pod) (:version (:opts pod))
+ (dissoc (:opts pod)
+ :version :metadata)))
+ {})
+ (pods/load-pod (:pod-spec pod) (:opts pod)))))
+ (when loader
+ (when-let [res (cp/source-for-namespace loader namespace nil)]
+ (if uberscript
+ (do (swap! uberscript-sources conj (:source res))
+ (uberscript/uberscript {:ctx @common/ctx
+ :expressions [(:source res)]})
+ {})
+ res)))
+ (case namespace
+ clojure.spec.alpha
+ (binding [*out* *err*]
+ (println "[babashka] WARNING: Use the babashka-compatible version of clojure.spec.alpha, available here: https://github.com/babashka/spec.alpha"))
+ clojure.core.specs.alpha
+ (binding [*out* *err*]
+ (println "[babashka] WARNING: clojure.core.specs.alpha is removed from the classpath, unless you explicitly add the dependency."))
+ nil))))
main (if (and jar (not main))
(when-let [res (cp/getResource
(cp/loader jar)
@@ -809,7 +885,8 @@ Use bb run --help to show this help output.
sci-ctx (sci/init opts)
_ (vreset! common/ctx sci-ctx)
_ (when-let [pods (:pods @common/bb-edn)]
- (let [pod-metadata (pods/load-pods-metadata pods)]
+ (when-let [pod-metadata (pods/load-pods-metadata
+ pods {:download-only (download-only?)})]
(vreset! pod-namespaces pod-metadata)))
preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim))
[expressions exit-code]
@@ -823,6 +900,9 @@ Use bb run --help to show this help output.
"-main")]
[[(format "(ns user (:require [%1$s])) (apply %1$s/%2$s *command-line-args*)"
ns var-name)] nil])
+ exec-fn
+ (let [sym (symbol exec-fn)]
+ [[(cli/exec-fn-snippet sym)] nil])
run (if (:run-help cli-opts)
[(print-run-help) 0]
(do
@@ -882,6 +962,7 @@ Use bb run --help to show this help output.
uberjar [nil 0]
list-tasks [(tasks/list-tasks sci-ctx) 0]
print-deps [(print-deps/print-deps (:print-deps-format cli-opts)) 0]
+ prepare [nil 0]
uberscript
[nil (do (uberscript/uberscript {:ctx sci-ctx
:expressions expressions})
@@ -890,8 +971,8 @@ Use bb run --help to show this help output.
;; execute code
(sci/binding [sci/file abs-path]
(try
- ; when evaluating expression(s), add in repl-requires so things like
- ; pprint and dir are available
+ ;; when evaluating expression(s), add in repl-requires so things like
+ ;; pprint and dir are available
(sci/eval-form sci-ctx `(apply require (quote ~clojure-main/repl-requires)))
(loop []
(let [in (read-next *in*)]
@@ -963,20 +1044,31 @@ Use bb run --help to show this help output.
(and (= minor-current minor-min)
(>= patch-current patch-min)))))))
+(defn load-bb-edn [string]
+ (try (edn/read-string {:default tagged-literal} string)
+ (catch java.lang.RuntimeException e
+ (if (re-find #"No dispatch macro for: \"" (.getMessage e))
+ (throw (ex-info "Invalid regex literal found in EDN config, use re-pattern instead" {}))
+ (throw e)))))
+
(defn main [& args]
(let [[args global-opts] (parse-global-opts args)
{:keys [:jar] :as file-opt} (when (some-> args first io/file .isFile)
(parse-file-opt args global-opts))
config (:config global-opts)
+ merge-deps (:merge-deps global-opts)
abs-path #(-> % io/file .getAbsolutePath)
bb-edn-file (cond
config (when (fs/exists? config) (abs-path config))
jar (some-> jar cp/loader (cp/resource "META-INF/bb.edn") .toString)
:else (when (fs/exists? "bb.edn") (abs-path "bb.edn")))
- bb-edn (when bb-edn-file
- (System/setProperty "babashka.config" bb-edn-file)
- (let [raw-string (slurp bb-edn-file)
- edn (edn/read-string raw-string)
+ bb-edn (when (or bb-edn-file merge-deps)
+ (when bb-edn-file (System/setProperty "babashka.config" bb-edn-file))
+ (let [raw-string (when bb-edn-file (slurp bb-edn-file))
+ edn (when bb-edn-file (load-bb-edn raw-string))
+ edn (if merge-deps
+ (deps/merge-deps [edn (load-bb-edn merge-deps)])
+ edn)
edn (assoc edn
:raw raw-string
:file bb-edn-file)
@@ -985,6 +1077,7 @@ Use bb run --help to show this help output.
(assoc edn :deps-root deps-root)
edn)]
(vreset! common/bb-edn edn)))
+ ;; _ (.println System/err (str bb-edn))
min-bb-version (:min-bb-version bb-edn)]
(when min-bb-version
(when-not (satisfies-min-version? min-bb-version)
diff --git a/src/babashka/wait.clj b/src/babashka/wait.clj
index 5cbea999..d8c4fe69 100644
--- a/src/babashka/wait.clj
+++ b/src/babashka/wait.clj
@@ -26,8 +26,9 @@
:wait-for-port.impl/timed-out
:wait-for-port.impl/try-again))))]
(cond (identical? :wait-for-port.impl/try-again v)
- (do (Thread/sleep (or pause 100))
- (recur))
+ (let [^long pause (or pause 100)]
+ (Thread/sleep pause)
+ (recur))
(identical? :wait-for-port.impl/timed-out v)
default
:else
@@ -51,8 +52,9 @@
:wait-for-path.impl/timed-out
:wait-for-path.impl/try-again)))]
(cond (identical? :wait-for-path.impl/try-again v)
- (do (Thread/sleep (or pause 100))
- (recur))
+ (let [^long pause (or pause 100)]
+ (Thread/sleep pause)
+ (recur))
(identical? :wait-for-path.impl/timed-out v)
default
:else
diff --git a/test-resources/babashka/exec_test.clj b/test-resources/babashka/exec_test.clj
new file mode 100644
index 00000000..6eca718f
--- /dev/null
+++ b/test-resources/babashka/exec_test.clj
@@ -0,0 +1,7 @@
+(ns babashka.exec-test
+ {:org.babashka/cli {:coerce {:foo []}}})
+
+(defn exec-test
+ {:org.babashka/cli {:coerce {:bar :keyword}}}
+ [m]
+ (prn m))
diff --git a/test-resources/lib_tests/babashka/run_all_libtests.clj b/test-resources/lib_tests/babashka/run_all_libtests.clj
index 56fcda74..72c9d8fb 100644
--- a/test-resources/lib_tests/babashka/run_all_libtests.clj
+++ b/test-resources/lib_tests/babashka/run_all_libtests.clj
@@ -1,5 +1,7 @@
(ns babashka.run-all-libtests
- (:require [babashka.core :refer [windows?]]
+ (:require [babashka.classpath :as cp :refer [add-classpath]]
+ [babashka.core :refer [windows?]]
+ [babashka.fs :as fs]
[clojure.edn :as edn]
[clojure.java.io :as io]
[clojure.test :as t :refer [*report-counters*]]))
@@ -42,7 +44,13 @@
;; Standard test-runner for libtests
(let [lib-tests (edn/read-string (slurp (io/resource "bb-tested-libs.edn")))]
- (doseq [{tns :test-namespaces skip-windows :skip-windows} (vals lib-tests)]
+ (doseq [[libname {tns :test-namespaces skip-windows :skip-windows
+ :keys [test-paths
+ git-sha]}] lib-tests]
+ (let [git-dir (format ".gitlibs/libs/%s/%s" libname git-sha)
+ git-dir (fs/file (fs/home) git-dir)]
+ (doseq [p test-paths]
+ (add-classpath (str (fs/file git-dir p)))))
(when-not (and skip-windows (windows?))
(apply test-namespaces tns))))
diff --git a/test-resources/lib_tests/bb-tested-libs.edn b/test-resources/lib_tests/bb-tested-libs.edn
index bca25eba..99d4bdab 100644
--- a/test-resources/lib_tests/bb-tested-libs.edn
+++ b/test-resources/lib_tests/bb-tested-libs.edn
@@ -47,7 +47,7 @@
doric/doric {:git-sha "8747fdce565187a5c368c575cf4ca794084b0a5c", :git-url "https://github.com/joegallo/doric", :test-namespaces (doric.test.core doric.test.readme doric.test.doctest)}
com.github.seancorfield/honeysql {:git-sha "6e4e1f6928450788353c181f32474d930d6afe84", :git-url "https://github.com/seancorfield/honeysql", :test-namespaces (honey.sql-test honey.sql.helpers-test honey.sql.postgres-test), :branch "develop"}
honeysql/honeysql {:git-sha "1137dd12350afdc30ad4976c3718279581390b36", :git-url "https://github.com/seancorfield/honeysql", :test-namespaces (honeysql.format-test honeysql.core-test), :branch "v1"}
- ; skip tests on Windows because of the :compressed thing
+ ; skip tests on Windows because of the :compressed thing
babashka/babashka.curl {:git-url "https://github.com/babashka/babashka.curl", :test-namespaces [babashka.curl-test], :skip-windows true, :manually-added true}
http-kit/http-kit {:git-url "https://github.com/http-kit/http-kit", :test-namespaces [httpkit.client-test], :manually-added true}
org.clojure/core.match {:git-url "https://github.com/clojure/core.match", :test-namespaces [core-match.core-tests], :manually-added true}
@@ -112,4 +112,59 @@
com.layerware/hugsql-core {:test-namespaces (hugsql.babashka-test)}
com.github.seancorfield/expectations {:git-url "https://github.com/clojure-expectations/clojure-test", :test-namespaces (expectations.clojure.test-test), :git-sha "b30fefd97d9eb7d1f47e06956521f354cb926b03"}
com.rpl/specter {:git-url "https://github.com/redplanetlabs/specter", :test-namespaces (com.rpl.specter.cljs-test-helpers com.rpl.specter.test-helpers com.rpl.specter.core-test com.rpl.specter.zipper-test), :git-sha "67e86806020b9d02fbca8cdb1efad3002fc81a32"}
-}
+ com.github.askonomm/clarktown {:git-url "https://github.com/askonomm/clarktown", :test-namespaces (clarktown.core-test clarktown.parsers.horizontal-line-block-test clarktown.parsers.italic-test clarktown.parsers.link-and-image-test clarktown.parsers.empty-block-test clarktown.parsers.inline-code-test clarktown.parsers.heading-block-test clarktown.parsers.bold-test clarktown.parsers.quote-block-test clarktown.parsers.code-block-test clarktown.parsers.strikethrough-test), :git-sha "059bfa7bd9bfdde0c75646bf1dfc20d23da8a02c"}
+ org.clojure/math.numeric-tower {:git-url "https://github.com/clojure/math.numeric-tower", :test-namespaces (clojure.math.test-numeric-tower), :git-sha "97827be66f35feebc3c89ba81c546fef4adc7947"}
+ prismatic/schema {:test-namespaces [schema.core-test
+ schema.macros-test
+ schema.coerce-test
+ schema.experimental.abstract-map-test
+ schema.test-test
+ schema.utils-test]
+ :git-url "https://github.com/plumatic/schema"
+ :git-sha "6846dc7c3a9df5bfd718f68f183c683ce0f621ff"
+ :git-tag "schema-1.3.0"
+ ;; specify for adding tests to classpath
+ :test-paths ["test/clj" "test/cljc"]}
+ metosin/malli {:test-namespaces [malli.clj-kondo-test
+ malli.core-test
+ malli.destructure-test
+ malli.dot-test
+ malli.error-test
+ malli.experimental-test
+ ;; malli.generator-test
+ malli.instrument-test
+ malli.json-schema-test
+ malli.plantuml-test
+ malli.provider-test
+ malli.registry-test
+ malli.swagger-test
+ malli.transform-test
+ malli.util-test]
+ :git-url "https://github.com/metosin/malli"
+ :git-sha "588147ef49b2e41c7d12a8aa994b39c1c6fedd99"
+ :git-tag "0.8.9"
+ ;; specify for adding tests to classpath
+ :test-paths ["test"]}
+ meander/epsilon {:test-namespaces [meander.epsilon-test
+ meander.defsyntax-test
+ meander.syntax.epsilon-test
+ meander.substitute.epsilon-test
+ meander.strategy.epsilon-test
+ meander.matrix.epsilon-test
+ meander.match.epsilon-test
+ meander.match.ir.epsilon-test
+ meander.match.check.epsilon-test
+ meander.interpreter.epsilon-test
+ meander.defsyntax-test.gh-145]
+ :test-paths ["test"]
+ :git-url "https://github.com/noprompt/meander"
+ :git-sha "55f5ce70e6ef717e95c58260f6bc725d70c0cb6d"}
+ cc.qbits/auspex {:git-url "https://github.com/mpenet/auspex"
+ :git-sha "1a9d7427e60e1a434a764aa820d1c53f7e22504a"
+ :test-paths ["test"]
+ :test-namespaces [qbits.auspex-test]}
+ exoscale/interceptor {:git-url "https://github.com/exoscale/interceptor"
+ :git-sha "ca115fe00a0abf3a2f78452ab309c3aa4c00fc4e"
+ :test-paths ["test"]
+ :test-namespaces [exoscale.interceptor-test]}
+ }
diff --git a/test-resources/lib_tests/clarktown/core.md b/test-resources/lib_tests/clarktown/core.md
new file mode 100644
index 00000000..6a5b2f17
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/core.md
@@ -0,0 +1,93 @@
+Lorem ipsum dolor **sit** amet. Lorem ipsum *dolor* _sit_ __amet__.
+
+There's a [link here](https://example.com/that_has_things?!???!#in-it).
+
+1. List item
+2. Another list item
+ 1. Sub list item
+ 2. Another sub list item
+ 1. Sub sub list item
+ 3. Continuing sub list item
+3. Continuing list item
+
+```javascript
+// Detect horizontal line block
+function isHorizontalLineBlock(block) {
+ return block === "***";
+}
+
+// Render horizontal line block
+function horizontalLineBlock(block) {
+ return `
`;
+}
+
+// Compose an array of parsers
+const parsers = [{
+ matcher: isHorizontalLineBlock,
+ renderers: [horizontalLineBlock]
+}];
+
+// And finally, our parser itself
+function markdownToHTML(markdown) {
+ // Create blocks
+ const blocks = content.split(/\n\n/);
+
+ // Parse blocks
+ const parsedBlocks = blocks.map((block) => {
+ // Let's find a parser that has a matcher that matches
+ const parser = parsers.find((parser) => parser.matcher(block));
+
+ // If match was found, let's run our renderers over `block`
+ if (parser) {
+ for (const renderer of match.renderers) {
+ block = renderer(block);
+ }
+ }
+
+ return block;
+ });
+
+ // And at last, join the blocks together for one big block.
+ return parsedBlocks.join("");
+}
+```
+
+- Test 123
+- Test 223
+ - Test 334
+ 1. Test test
+
+This is ___bold italic text___ and ***this is also***. *What about italic text that **has bold text***?
+
+## Hi there, world!
+
+* List item
+* Another list ~~item~~
+ * Sub list item
+ * Another sub list item
+ * Sub sub list item
+ * Continuing sub list item
+* Continuing list item
+
+***
+
+* List item
+* Another list item
+ * Sub list item
+ * Another sub list item
+ 1. Sub sub list item
+ 2. Continuing sub list item
+* Continuing list item
+
+This is a H1 heading with settext
+=================================
+
+And this is a H2 heading with settext
+-------------------------------------
+
+Testing paragraph right before a code block
+```
+code goes here
+```
+# Heading goes here
+Paragraph right after heading
diff --git a/test-resources/lib_tests/clarktown/core_result.html b/test-resources/lib_tests/clarktown/core_result.html
new file mode 100644
index 00000000..d604bf58
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/core_result.html
@@ -0,0 +1,69 @@
+Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet.
+
+There's a link here.
+
+- List item
- Another list item
- Sub list item
- Another sub list item
- Sub sub list item
- Continuing sub list item
- Continuing list item
+
+// Detect horizontal line block
+function isHorizontalLineBlock(block) {
+ return block === "***";
+}
+
+// Render horizontal line block
+function horizontalLineBlock(block) {
+ return `<hr>`;
+}
+
+// Compose an array of parsers
+const parsers = [{
+ matcher: isHorizontalLineBlock,
+ renderers: [horizontalLineBlock]
+}];
+
+// And finally, our parser itself
+function markdownToHTML(markdown) {
+ // Create blocks
+ const blocks = content.split(/\n\n/);
+
+ // Parse blocks
+ const parsedBlocks = blocks.map((block) => {
+ // Let's find a parser that has a matcher that matches
+ const parser = parsers.find((parser) => parser.matcher(block));
+
+ // If match was found, let's run our renderers over `block`
+ if (parser) {
+ for (const renderer of match.renderers) {
+ block = renderer(block);
+ }
+ }
+
+ return block;
+ });
+
+ // And at last, join the blocks together for one big block.
+ return parsedBlocks.join("");
+}
+
+
+
+This is bold italic text and this is also. What about italic text that has bold text?
+
+Hi there, world!
+
+- List item
- Another list
item- Sub list item
- Another sub list item
- Sub sub list item
- Continuing sub list item
- Continuing list item
+
+
+
+- List item
- Another list item
- Sub list item
- Another sub list item
- Sub sub list item
- Continuing sub list item
- Continuing list item
+
+This is a H1 heading with settext
+
+And this is a H2 heading with settext
+
+Testing paragraph right before a code block
+
+code goes here
+
+Heading goes here
+
+Paragraph right after heading
diff --git a/test-resources/lib_tests/clarktown/core_test.clj b/test-resources/lib_tests/clarktown/core_test.clj
new file mode 100644
index 00000000..86ca1765
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/core_test.clj
@@ -0,0 +1,15 @@
+(ns clarktown.core-test
+ (:require
+ ;; BB-TEST-PATCH: require clojure.string for split-lines patch below
+ [clojure.string :as str]
+ [clojure.test :refer [deftest testing is]]
+ [clojure.java.io :as io]
+ [clarktown.core :as core]))
+
+
+(deftest overall-test
+ (testing "Overall"
+ ;; BB-TEST-PATCH: library uses hard-coded \n, so using split-lines for platform-agnostic testing
+ ;; BB-TEST-PATCH: change file paths to match bb folder structure (and copy resource files)
+ (is (= (str/split-lines (core/render (slurp (io/file (io/resource "clarktown/core.md")))))
+ (str/split-lines (slurp (io/file (io/resource "clarktown/core_result.html"))))))))
diff --git a/test-resources/lib_tests/clarktown/parsers/bold_test.clj b/test-resources/lib_tests/clarktown/parsers/bold_test.clj
new file mode 100644
index 00000000..a082d418
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/parsers/bold_test.clj
@@ -0,0 +1,18 @@
+(ns clarktown.parsers.bold-test
+ (:require
+ [clojure.test :refer [deftest testing is]]
+ [clarktown.parsers.bold :as bold]))
+
+
+(deftest bold-test
+ (testing "Creating bold text with two surrounding asterisk characters"
+ (is (= "This is bold."
+ (bold/render "**This is bold.**" nil))))
+
+ (testing "Creating bold text with two surrounding underscore characters"
+ (is (= "This is bold."
+ (bold/render "__This is bold.__" nil))))
+
+ (testing "Creating bold text with both underscores and asterisks mixed"
+ (is (= "Hi, my name is John, what is your name?"
+ (bold/render "Hi, my name is **John**, what is __your name?__" nil)))))
\ No newline at end of file
diff --git a/test-resources/lib_tests/clarktown/parsers/code_block.md b/test-resources/lib_tests/clarktown/parsers/code_block.md
new file mode 100644
index 00000000..8e6f863f
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/parsers/code_block.md
@@ -0,0 +1,41 @@
+```javascript
+// Detect horizontal line block
+function isHorizontalLineBlock(block) {
+ return block === "***";
+}
+
+// Render horizontal line block
+function horizontalLineBlock(block) {
+ return `
`;
+}
+
+// Compose an array of parsers
+const parsers = [{
+ matcher: isHorizontalLineBlock,
+ renderers: [horizontalLineBlock]
+}];
+
+// And finally, our parser itself
+function markdownToHTML(markdown) {
+ // Create blocks
+ const blocks = content.split(/\n\n/);
+
+ // Parse blocks
+ const parsedBlocks = blocks.map((block) => {
+ // Let's find a parser that has a matcher that matches
+ const parser = parsers.find((parser) => parser.matcher(block));
+
+ // If match was found, let's run our renderers over `block`
+ if (parser) {
+ for (const renderer of match.renderers) {
+ block = renderer(block);
+ }
+ }
+
+ return block;
+ });
+
+ // And at last, join the blocks together for one big block.
+ return parsedBlocks.join("");
+}
+```
\ No newline at end of file
diff --git a/test-resources/lib_tests/clarktown/parsers/code_block_no_language.md b/test-resources/lib_tests/clarktown/parsers/code_block_no_language.md
new file mode 100644
index 00000000..dbba1f11
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/parsers/code_block_no_language.md
@@ -0,0 +1,41 @@
+```
+// Detect horizontal line block
+function isHorizontalLineBlock(block) {
+ return block === "***";
+}
+
+// Render horizontal line block
+function horizontalLineBlock(block) {
+ return `
`;
+}
+
+// Compose an array of parsers
+const parsers = [{
+ matcher: isHorizontalLineBlock,
+ renderers: [horizontalLineBlock]
+}];
+
+// And finally, our parser itself
+function markdownToHTML(markdown) {
+ // Create blocks
+ const blocks = content.split(/\n\n/);
+
+ // Parse blocks
+ const parsedBlocks = blocks.map((block) => {
+ // Let's find a parser that has a matcher that matches
+ const parser = parsers.find((parser) => parser.matcher(block));
+
+ // If match was found, let's run our renderers over `block`
+ if (parser) {
+ for (const renderer of match.renderers) {
+ block = renderer(block);
+ }
+ }
+
+ return block;
+ });
+
+ // And at last, join the blocks together for one big block.
+ return parsedBlocks.join("");
+}
+```
\ No newline at end of file
diff --git a/test-resources/lib_tests/clarktown/parsers/code_block_no_language_result.html b/test-resources/lib_tests/clarktown/parsers/code_block_no_language_result.html
new file mode 100644
index 00000000..0512d3eb
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/parsers/code_block_no_language_result.html
@@ -0,0 +1,39 @@
+// Detect horizontal line block
+function isHorizontalLineBlock(block) {
+ return block === "***";
+}
+
+// Render horizontal line block
+function horizontalLineBlock(block) {
+ return `<hr>`;
+}
+
+// Compose an array of parsers
+const parsers = [{
+ matcher: isHorizontalLineBlock,
+ renderers: [horizontalLineBlock]
+}];
+
+// And finally, our parser itself
+function markdownToHTML(markdown) {
+ // Create blocks
+ const blocks = content.split(/\n\n/);
+
+ // Parse blocks
+ const parsedBlocks = blocks.map((block) => {
+ // Let's find a parser that has a matcher that matches
+ const parser = parsers.find((parser) => parser.matcher(block));
+
+ // If match was found, let's run our renderers over `block`
+ if (parser) {
+ for (const renderer of match.renderers) {
+ block = renderer(block);
+ }
+ }
+
+ return block;
+ });
+
+ // And at last, join the blocks together for one big block.
+ return parsedBlocks.join("");
+}
\ No newline at end of file
diff --git a/test-resources/lib_tests/clarktown/parsers/code_block_result.html b/test-resources/lib_tests/clarktown/parsers/code_block_result.html
new file mode 100644
index 00000000..21b92144
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/parsers/code_block_result.html
@@ -0,0 +1,39 @@
+// Detect horizontal line block
+function isHorizontalLineBlock(block) {
+ return block === "***";
+}
+
+// Render horizontal line block
+function horizontalLineBlock(block) {
+ return `<hr>`;
+}
+
+// Compose an array of parsers
+const parsers = [{
+ matcher: isHorizontalLineBlock,
+ renderers: [horizontalLineBlock]
+}];
+
+// And finally, our parser itself
+function markdownToHTML(markdown) {
+ // Create blocks
+ const blocks = content.split(/\n\n/);
+
+ // Parse blocks
+ const parsedBlocks = blocks.map((block) => {
+ // Let's find a parser that has a matcher that matches
+ const parser = parsers.find((parser) => parser.matcher(block));
+
+ // If match was found, let's run our renderers over `block`
+ if (parser) {
+ for (const renderer of match.renderers) {
+ block = renderer(block);
+ }
+ }
+
+ return block;
+ });
+
+ // And at last, join the blocks together for one big block.
+ return parsedBlocks.join("");
+}
\ No newline at end of file
diff --git a/test-resources/lib_tests/clarktown/parsers/code_block_test.clj b/test-resources/lib_tests/clarktown/parsers/code_block_test.clj
new file mode 100644
index 00000000..b965e5fa
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/parsers/code_block_test.clj
@@ -0,0 +1,18 @@
+(ns clarktown.parsers.code-block-test
+ (:require
+ ;; require clojure.string to accomodate line break hack below
+ [clojure.string :as str]
+ [clojure.test :refer [deftest testing is]]
+ [clojure.java.io :as io]
+ [clarktown.parsers.code-block :as code-block]))
+
+;; BB-TEST-PATCH: change paths to match folder structure (and copy resource files)
+;; BB-TEST-PATCH: use split-lines to make tests platform-agnostic
+(deftest code-block-test
+ (testing "Code block with language specification"
+ (is (= (str/split-lines (slurp (io/file (io/resource "clarktown/parsers/code_block_result.html"))))
+ (str/split-lines (code-block/render (slurp (io/file (io/resource "clarktown/parsers/code_block.md"))) nil)))))
+
+ (testing "Code block with NO language specification"
+ (is (= (str/split-lines (slurp (io/file (io/resource "clarktown/parsers/code_block_no_language_result.html"))))
+ (str/split-lines (code-block/render (slurp (io/file (io/resource "clarktown/parsers/code_block_no_language.md"))) nil))))))
diff --git a/test-resources/lib_tests/clarktown/parsers/empty_block_test.clj b/test-resources/lib_tests/clarktown/parsers/empty_block_test.clj
new file mode 100644
index 00000000..a8d89c48
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/parsers/empty_block_test.clj
@@ -0,0 +1,14 @@
+(ns clarktown.parsers.empty-block-test
+ (:require
+ [clojure.test :refer [deftest testing is]]
+ [clarktown.parsers.empty-block :as empty-block]))
+
+
+(deftest empty-block-test
+ (testing "Rendering an empty block"
+ (is (= (empty-block/render "" nil)
+ "")))
+
+ (testing "Checking an empty block"
+ (is (true? (empty-block/is? "")))
+ (is (true? (empty-block/is? " ")))))
diff --git a/test-resources/lib_tests/clarktown/parsers/heading_block_test.clj b/test-resources/lib_tests/clarktown/parsers/heading_block_test.clj
new file mode 100644
index 00000000..9bfff4fd
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/parsers/heading_block_test.clj
@@ -0,0 +1,44 @@
+(ns clarktown.parsers.heading-block-test
+ (:require
+ [clojure.test :refer [deftest testing is]]
+ [clarktown.parsers.heading-block :as heading-block]))
+
+
+(deftest hashbang-heading-test
+ (testing "Hashbang heading block that's a H1"
+ (is (= "This is a heading block.
"
+ (heading-block/render "# This is a heading block." nil))))
+
+ (testing "Hashbang heading block that's a H2"
+ (is (= "This is a heading block.
"
+ (heading-block/render "## This is a heading block." nil))))
+
+ (testing "Hashbang heading block that's a H3"
+ (is (= "This is a heading block.
"
+ (heading-block/render "### This is a heading block." nil))))
+
+ (testing "Hashbang heading block that's a H4"
+ (is (= "This is a heading block.
"
+ (heading-block/render "#### This is a heading block." nil))))
+
+ (testing "Hashbang heading block that's a H5"
+ (is (= "This is a heading block.
"
+ (heading-block/render "##### This is a heading block." nil)))))
+
+
+(deftest settext-heading-text
+ (testing "Settext heading block that's a H1"
+ (is (= "This is a heading block.
"
+ (heading-block/render "This is a heading block.\n=========" nil))))
+
+ (testing "Settext heading block that's a H1 spanning multiple lines"
+ (is (= "This is a \nheading block spanning multiple lines.
"
+ (heading-block/render "This is a \nheading block spanning multiple lines.\n========" nil))))
+
+ (testing "Settext heading block that's a H2"
+ (is (= "This is a heading block.
"
+ (heading-block/render "This is a heading block.\n---------" nil))))
+
+ (testing "Settext heading block that's a H2 spanning multiple lines"
+ (is (= "This is a \nheading block spanning multiple lines.
"
+ (heading-block/render "This is a \nheading block spanning multiple lines.\n--------" nil)))))
\ No newline at end of file
diff --git a/test-resources/lib_tests/clarktown/parsers/horizontal_line_block_test.clj b/test-resources/lib_tests/clarktown/parsers/horizontal_line_block_test.clj
new file mode 100644
index 00000000..21617b6c
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/parsers/horizontal_line_block_test.clj
@@ -0,0 +1,21 @@
+(ns clarktown.parsers.horizontal-line-block-test
+ (:require
+ [clojure.test :refer [deftest testing is]]
+ [clarktown.parsers.horizontal-line-block :as horizontal-line-block]))
+
+
+(deftest horizontal-line-block-test
+ (testing "Creating a horizontal line"
+ (is (= "
"
+ (horizontal-line-block/render "***" nil)))
+
+ (is (= "
"
+ (horizontal-line-block/render "---" nil))))
+
+ (testing "Is a horizontal line block"
+ (is (true? (horizontal-line-block/is? "***")))
+ (is (true? (horizontal-line-block/is? " ***")))
+ (is (false? (horizontal-line-block/is? "Test *** 123")))
+ (is (true? (horizontal-line-block/is? "---")))
+ (is (true? (horizontal-line-block/is? " ---")))
+ (is (false? (horizontal-line-block/is? "Test --- 123")))))
\ No newline at end of file
diff --git a/test-resources/lib_tests/clarktown/parsers/inline_code_test.clj b/test-resources/lib_tests/clarktown/parsers/inline_code_test.clj
new file mode 100644
index 00000000..028c4b7d
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/parsers/inline_code_test.clj
@@ -0,0 +1,14 @@
+(ns clarktown.parsers.inline-code-test
+ (:require
+ [clojure.test :refer [deftest testing is]]
+ [clarktown.parsers.inline-code :as inline-code]))
+
+
+(deftest inline-code-test
+ (testing "Creating inline code text"
+ (is (= "This is inline code."
+ (inline-code/render "`This is inline code.`" nil))))
+
+ (testing "Creating inline-code text in the middle of regular text"
+ (is (= "This is regular text, mixed with some inline code., and it's great."
+ (inline-code/render "This is regular text, mixed with `some inline code.`, and it's great." nil)))))
\ No newline at end of file
diff --git a/test-resources/lib_tests/clarktown/parsers/italic_test.clj b/test-resources/lib_tests/clarktown/parsers/italic_test.clj
new file mode 100644
index 00000000..8ab13698
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/parsers/italic_test.clj
@@ -0,0 +1,18 @@
+(ns clarktown.parsers.italic-test
+ (:require
+ [clojure.test :refer [deftest testing is]]
+ [clarktown.parsers.italic :as italic]))
+
+
+(deftest italic-test
+ (testing "Creating italic text with one surrounding asterisk character"
+ (is (= "This is italic."
+ (italic/render "*This is italic.*" nil))))
+
+ (testing "Creating italic text with one surrounding underscore character"
+ (is (= "This is italic."
+ (italic/render "_This is italic._" nil))))
+
+ (testing "Creating italic text with both underscores and asterisks mixed"
+ (is (= "Hi, my name is John, what is your name?"
+ (italic/render "Hi, my name is *John*, what is _your name?_" nil)))))
\ No newline at end of file
diff --git a/test-resources/lib_tests/clarktown/parsers/link_and_image_test.clj b/test-resources/lib_tests/clarktown/parsers/link_and_image_test.clj
new file mode 100644
index 00000000..348a8f90
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/parsers/link_and_image_test.clj
@@ -0,0 +1,23 @@
+(ns clarktown.parsers.link-and-image-test
+ (:require
+ [clojure.test :refer [deftest testing is]]
+ [clarktown.parsers.link-and-image :as link-and-image]))
+
+
+(deftest link-test
+ (testing "Creating a link"
+ (is (= (link-and-image/render "[This is a link](https://example.com)" nil)
+ "This is a link"))
+
+ (is (= (link-and-image/render "[This-is-a-link](https://example.com)" nil)
+ "This-is-a-link"))
+
+ (is (= (link-and-image/render "[x] [label](link)" nil)
+ "[x] label"))
+
+ (is (= (link-and-image/render "[ ] [label](link)" nil)
+ "[ ] label")))
+
+ (testing "Creating an image"
+ (is (= (link-and-image/render "" nil)
+ "
"))))
\ No newline at end of file
diff --git a/test-resources/lib_tests/clarktown/parsers/quote_block_test.clj b/test-resources/lib_tests/clarktown/parsers/quote_block_test.clj
new file mode 100644
index 00000000..94553cf1
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/parsers/quote_block_test.clj
@@ -0,0 +1,15 @@
+(ns clarktown.parsers.quote-block-test
+ (:require
+ [clojure.test :refer [deftest testing is]]
+ [clarktown.parsers.quote-block :as quote-block]))
+
+
+(deftest quote-block-block-test
+ (testing "Creating a quote block line"
+ (is (= (quote-block/render "> First line\n> second line" nil)
+ "First line\nsecond line
")))
+
+ (testing "Checking a quote block"
+ (is (true? (quote-block/is? "> Test")))
+ (is (true? (quote-block/is? " > Test")))
+ (is (true? (quote-block/is? ">")))))
\ No newline at end of file
diff --git a/test-resources/lib_tests/clarktown/parsers/strikethrough_test.clj b/test-resources/lib_tests/clarktown/parsers/strikethrough_test.clj
new file mode 100644
index 00000000..fdf61888
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/parsers/strikethrough_test.clj
@@ -0,0 +1,14 @@
+(ns clarktown.parsers.strikethrough-test
+ (:require
+ [clojure.test :refer [deftest testing is]]
+ [clarktown.parsers.strikethrough :as strikethrough]))
+
+
+(deftest strikethrough-test
+ (testing "Creating strikethrough text"
+ (is (= (strikethrough/render "~~This is strikethrough text.~~" nil)
+ "This is strikethrough text.")))
+
+ (testing "Creating strikethrough text mixed with regular text"
+ (is (= (strikethrough/render "Some other text, ~~This is strikethrough text.~~ And more text." nil)
+ "Some other text, This is strikethrough text. And more text."))))
\ No newline at end of file
diff --git a/test-resources/lib_tests/clojure/math/test_numeric_tower.clj b/test-resources/lib_tests/clojure/math/test_numeric_tower.clj
new file mode 100644
index 00000000..ba0ba266
--- /dev/null
+++ b/test-resources/lib_tests/clojure/math/test_numeric_tower.clj
@@ -0,0 +1,129 @@
+(ns clojure.math.test-numeric-tower
+ (:use clojure.test
+ clojure.math.numeric-tower))
+
+(deftest test-expt
+ (are [x y] (= x y)
+ (expt 2 3) 8
+ (expt (expt 2 16) 2) (expt 2 32)
+ (expt 4/3 2) 16/9
+ (expt 2 -10) 1/1024
+ (expt 0.5M 2) 0.25M
+ (expt 5 4.2) (Math/pow 5 4.2)
+ (expt 5.3 4) (Math/pow 5.3 4)
+ (expt 5.3 4) (Math/pow 5.3 4)
+ (expt 2 0) 1
+ (expt (java.math.BigInteger. "4") 0) (java.math.BigInteger. "1")
+ (expt 4M 0) 1M
+ (expt 8M 1) 8M
+ (expt 16M 16) 18446744073709551616M))
+
+(when-available clojure.lang.BigInt
+ (deftest test-expt-bigint
+ (are [x y] (= x y)
+ (expt (bigint 4) 0) (bigint 1))))
+
+(deftest test-abs
+ (are [x y] (= x y)
+ (abs -2) 2
+ (abs 0) 0
+ (abs 5) 5
+ (abs 123456789123456789) 123456789123456789
+ (abs -123456789123456789) 123456789123456789
+ (abs 5/3) 5/3
+ (abs -4/3) 4/3
+ (abs 4.3M) 4.3M
+ (abs -4.3M) 4.3M
+ (abs 2.8) 2.8
+ (abs -2.8) 2.8))
+
+(deftest test-gcd
+ (are [x y] (= x y)
+ (gcd 4 3) 1
+ (gcd 24 12) 12
+ (gcd 24 27) 3
+ (gcd 1 0) 1
+ (gcd 0 1) 1
+ (gcd 0 0) 0)
+ (is (thrown? IllegalArgumentException (gcd nil 0)))
+ (is (thrown? IllegalArgumentException (gcd 0 nil)))
+ (is (thrown? IllegalArgumentException (gcd 7.0 0))))
+
+(deftest test-lcm
+ (are [x y] (= x y)
+ (lcm 2 3) 6
+ (lcm 3 2) 6
+ (lcm -2 3) 6
+ (lcm 2 -3) 6
+ (lcm -2 -3) 6
+ (lcm 4 10) 20
+ (lcm 1 0) 0
+ (lcm 0 1) 0
+ (lcm 0 0) 0)
+ (is (thrown? IllegalArgumentException (lcm nil 0)))
+ (is (thrown? IllegalArgumentException (lcm 0 nil)))
+ (is (thrown? IllegalArgumentException (lcm 7.0 0))))
+
+(deftest test-floor
+ (are [x y] (== x y)
+ (floor 6) 6
+ (floor -6) -6
+ (floor 123456789123456789) 123456789123456789
+ (floor -123456789123456789) -123456789123456789
+ (floor 4/3) 1
+ (floor -4/3) -2
+ (floor 4.3M) 4
+ (floor -4.3M) -5
+ (floor 4.3) 4.0
+ (floor -4.3) -5.0))
+
+(deftest test-ceil
+ (are [x y] (== x y)
+ (ceil 6) 6
+ (ceil -6) -6
+ (ceil 123456789123456789) 123456789123456789
+ (ceil -123456789123456789) -123456789123456789
+ (ceil 4/3) 2
+ (ceil -4/3) -1
+ (ceil 4.3M) 5
+ (ceil -4.3M) -4
+ (ceil 4.3) 5.0
+ (ceil -4.3) -4.0))
+
+(deftest test-round
+ (are [x y] (== x y)
+ (round 6) 6
+ (round -6) -6
+ (round 123456789123456789) 123456789123456789
+ (round -123456789123456789) -123456789123456789
+ (round 4/3) 1
+ (round 5/3) 2
+ (round 5/2) 3
+ (round -4/3) -1
+ (round -5/3) -2
+ (round -5/2) -2
+ (round 4.3M) 4
+ (round 4.7M) 5
+ (round -4.3M) -4
+ (round -4.7M) -5
+ (round 4.5M) 5
+ (round -4.5M) -4
+ (round 4.3) 4
+ (round 4.7) 5
+ (round -4.3) -4
+ (round -4.7) -5
+ (round 4.5) 5
+ (round -4.5) -4))
+
+(deftest test-sqrt
+ (are [x y] (= x y)
+ (sqrt 9) 3
+ (sqrt 16/9) 4/3
+ (sqrt 0.25M) 0.5M
+ (sqrt 2) (Math/sqrt 2)))
+
+(deftest test-exact-integer-sqrt
+ (are [x y] (= x y)
+ (exact-integer-sqrt 15) [3 6]
+ (exact-integer-sqrt (inc (expt 2 32))) [(expt 2 16) 1]
+ (exact-integer-sqrt 1000000000000) [1000000 0]))
diff --git a/test-resources/lib_tests/honeysql/core_test.cljc b/test-resources/lib_tests/honeysql/core_test.cljc
index d1fc2f8b..c5736c6c 100644
--- a/test-resources/lib_tests/honeysql/core_test.cljc
+++ b/test-resources/lib_tests/honeysql/core_test.cljc
@@ -11,6 +11,17 @@
insert-into with merge-where merge-having]]
honeysql.format-test))
+;; BB_TEST_PATCH: must explicitly set data readers
+#?(:clj
+ (do
+ (require '[honeysql.types])
+ (set! *data-readers* {'sql/call honeysql.types/read-sql-call
+ 'sql/inline honeysql.types/read-sql-inline
+ 'sql/raw honeysql.types/read-sql-raw
+ 'sql/param honeysql.types/read-sql-param
+ 'sql/array honeysql.types/read-sql-array
+ 'sql/regularize honeysql.format/regularize})))
+
;; TODO: more tests
(deftest test-select
diff --git a/test-resources/lib_tests/slingshot/slingshot_test.clj b/test-resources/lib_tests/slingshot/slingshot_test.clj
index f787a862..f9e7c541 100644
--- a/test-resources/lib_tests/slingshot/slingshot_test.clj
+++ b/test-resources/lib_tests/slingshot/slingshot_test.clj
@@ -36,7 +36,8 @@
[:class-string e#])
;; by clojure record type
- (catch exception-record e#
+ ;; BB test patch, exception-record != class?, so this expands into incorrect code
+ #_(catch exception-record e#
[:class-exception-record e#])
;; by key-value
diff --git a/test-resources/line_number_test_test.clj b/test-resources/line_number_test_test.clj
new file mode 100644
index 00000000..e3d3fdba
--- /dev/null
+++ b/test-resources/line_number_test_test.clj
@@ -0,0 +1,7 @@
+(ns line-number-test-test
+ (:require [clojure.test :refer [is deftest run-tests]]))
+
+(deftest test-is
+ (is false))
+
+(run-tests 'line-number-test-test)
diff --git a/test/babashka/bb_edn_test.clj b/test/babashka/bb_edn_test.clj
index 69a3a7a7..0422fe2b 100644
--- a/test/babashka/bb_edn_test.clj
+++ b/test/babashka/bb_edn_test.clj
@@ -200,6 +200,12 @@
(is (thrown-with-msg?
Exception #"Cyclic task: b"
(bb "run" "b")))))
+ (testing "friendly regex literal error handling"
+ (test-utils/with-config
+ "{:tasks {something (clojure.string/split \"1-2\" #\"-\")}}"
+ (is (thrown-with-msg?
+ Exception #"Invalid regex literal"
+ (bb "run" "something")))))
(testing "doc"
(test-utils/with-config '{:tasks {b {:doc "Beautiful docstring"}}}
(let [s (test-utils/bb nil "doc" "b")]
@@ -439,3 +445,15 @@ even more stuff here\"
(pr-str '{:paths ["test-resources"]
:pods {pod/test-pod {:path "test-resources/pod"}}})
(is (= "42\n" (test-utils/bb nil "-m" "pod-tests.local")))))
+
+(deftest tag-test
+ (test-utils/with-config
+ "{:deps {}
+ :aliases {:foo {:env-vars {:dude #env \"DUDE\"}}}}"
+ (is (= 6 (bb "-e" "(+ 1 2 3)")))))
+
+(deftest merge-deps-test
+ (test-utils/with-config
+ "{:deps {}}"
+ (is (= {1 {:a 1}}
+ (bb "-Sdeps" "{:deps {medley/medley {:mvn/version \"1.4.0\"}}}" "-e" "(require 'medley.core) (medley.core/index-by :a [{:a 1}])")))))
diff --git a/test/babashka/classes_test.clj b/test/babashka/classes_test.clj
new file mode 100644
index 00000000..31b7872e
--- /dev/null
+++ b/test/babashka/classes_test.clj
@@ -0,0 +1,11 @@
+(ns babashka.classes-test
+ (:require [babashka.test-utils :as tu]
+ [clojure.edn :as edn]
+ [clojure.test :as t :refer [deftest is testing]]))
+
+(defn bb
+ [& args]
+ (edn/read-string (apply tu/bb nil (map pr-str args))))
+
+(deftest all-classes-test
+ (is (true? (bb '(let [classes (babashka.classes/all-classes)] (and (seq classes) (every? class? classes)))))))
diff --git a/test/babashka/deps_test.clj b/test/babashka/deps_test.clj
index 397a01f9..d3edade2 100644
--- a/test/babashka/deps_test.clj
+++ b/test/babashka/deps_test.clj
@@ -35,6 +35,7 @@
(bb (pr-str `(do (babashka.deps/add-deps '{:deps {babashka/process {:git/url "https://github.com/babashka/process" :sha "4c6699d06b49773d3e5c5b4c11d3334fb78cc996"}}}
{:force true
:env {"PATH" (System/getenv "PATH")
+ "JAVA_HOME" (System/getenv "JAVA_HOME")
"GITLIBS" ~(str libs-dir)}}) nil)))
(bb (pr-str `(do (babashka.deps/add-deps '{:deps {babashka/process {:git/url "https://github.com/babashka/process" :sha "4c6699d06b49773d3e5c5b4c11d3334fb78cc996"}}}
{:force true
@@ -86,6 +87,7 @@ true
libs-dir2 (fs/file tmp-dir ".gitlibs2")
template (pr-str '(do (babashka.deps/clojure ["-Sforce" "-Spath" "-Sdeps" "{:deps {babashka/process {:git/url \"https://github.com/babashka/process\" :sha \"4c6699d06b49773d3e5c5b4c11d3334fb78cc996\"}}}"]
{:out :string :env-key {"PATH" (System/getenv "PATH")
+ "JAVA_HOME" (System/getenv "JAVA_HOME")
"GITLIBS" :gitlibs}}) nil))]
(bb (-> template (str/replace ":gitlibs" (pr-str (str libs-dir)))
(str/replace ":env-key" ":env")))
diff --git a/test/babashka/exec_test.clj b/test/babashka/exec_test.clj
new file mode 100644
index 00000000..4493e356
--- /dev/null
+++ b/test/babashka/exec_test.clj
@@ -0,0 +1,36 @@
+(ns babashka.exec-test
+ (:require
+ [babashka.test-utils :as u]
+ [cheshire.core :as cheshire]
+ [clojure.edn :as edn]
+ [clojure.test :as t :refer [deftest is]]))
+
+(defn bb [& args]
+ (apply u/bb nil args))
+
+(deftest exec-test
+ (is (= {:foo 1} (edn/read-string (bb "-x" "prn" "--foo" "1"))))
+ (is (thrown? Exception (bb "-x" "json/generate-string" "--foo" "1")))
+ (is (= {:foo 1} (cheshire/parse-string
+ (edn/read-string
+ (bb "-x" "cheshire.core/generate-string" "--foo" "1")) true))))
+
+(deftest tasks-exec-test
+ (u/with-config
+ "{:deps {}
+ :tasks {foo (exec 'clojure.core/prn)}}"
+ (is (= {:dude 1} (edn/read-string (bb "run" "foo" "--dude" "1")))))
+ (u/with-config
+ "{:deps {}
+ :tasks {foo (exec 'clojure.core/prn)}}"
+ (is (= {:dude 1} (edn/read-string (bb "run" "foo" "--dude" "1")))))
+ (u/with-config
+ "{:deps {}
+ :tasks {foo {:org.babashka/cli {:coerce {:dude []}}
+ :task (exec 'clojure.core/prn)}}}"
+ (is (= {:dude [1]} (edn/read-string (bb "run" "foo" "--dude" "1")))))
+ (u/with-config
+ "{:deps {}
+ :tasks {foo {:task (exec 'babashka.exec-test/exec-test)}}}"
+ (is (= {:foo [1], :bar :yeah}
+ (edn/read-string (bb "-cp" "test-resources" "run" "foo" "--foo" "1" "--bar" "yeah"))))))
diff --git a/test/babashka/main_test.clj b/test/babashka/main_test.clj
index 976d2644..e645c82b 100644
--- a/test/babashka/main_test.clj
+++ b/test/babashka/main_test.clj
@@ -60,7 +60,12 @@
(is (:feature/xml v)))
(is (= {:force? true} (parse-opts ["--force"])))
(is (= {:main "foo", :command-line-args '("-h")} (parse-opts ["-m" "foo" "-h"])))
- (is (= {:main "foo", :command-line-args '("-h")} (parse-opts ["-m" "foo" "--" "-h"]))))
+ (is (= {:main "foo", :command-line-args '("-h")} (parse-opts ["-m" "foo" "--" "-h"])))
+ (is (= {:force? true :list-tasks true :command-line-args nil} (parse-opts ["--force" "tasks"])))
+ (is (= {:force? true :run "sometask" :command-line-args nil} (parse-opts ["--force" "run" "sometask"])))
+ (is (= {:force? true :repl true} (parse-opts ["--force" "repl"])))
+ (is (= {:force? true :clojure true :command-line-args '("-M" "-r")}
+ (parse-opts ["--force" "clojure" "-M" "-r"]))))
(deftest version-test
(is (= [1 0 0] (main/parse-version "1.0.0-SNAPSHOT")))
@@ -825,6 +830,13 @@ true")))
(is (= :f (bb nil "(first (into-array [:f]))")))
(is (= :f (bb nil "(first (first (into-array [(into-array [:f])])))"))))
+(deftest var-names-test
+ (testing "for all public vars, ns/symbol from ns map matches metadata"
+ (is (empty? (bb nil (.getPath (io/file "test" "babashka" "scripts" "check_var_names.bb")))))))
+
+(deftest clojure-lang-var-mapping-test
+ (is (= :var (bb nil "(defprotocol Foo (foo [_])) (extend-protocol Foo clojure.lang.Var (foo [_] :var)) (foo #'inc)"))))
+
;;;; Scratch
(comment
diff --git a/test/babashka/pprint_test.clj b/test/babashka/pprint_test.clj
new file mode 100644
index 00000000..4679db46
--- /dev/null
+++ b/test/babashka/pprint_test.clj
@@ -0,0 +1,12 @@
+(ns babashka.pprint-test
+ (:require
+ [babashka.test-utils :as test-utils]
+ [clojure.string :as str]
+ [clojure.test :as test :refer [deftest is]]))
+
+(defn bb [& args]
+ (str/trim (apply test-utils/bb (map str args))))
+
+(deftest print-length-test
+ (is (= "(0 1 2 3 4 5 6 7 8 9 ...)"
+ (bb "-e" "(set! *print-length* 10) (clojure.pprint/pprint (range 20))"))))
diff --git a/test/babashka/reify_test.clj b/test/babashka/reify_test.clj
index 72624c65..a9911dd7 100644
--- a/test/babashka/reify_test.clj
+++ b/test/babashka/reify_test.clj
@@ -2,6 +2,7 @@
(:require
[babashka.test-utils :as test-utils]
[clojure.edn :as edn]
+ [clojure.string :as str]
[clojure.test :as test :refer [deftest is testing]]))
(defn bb [input & args]
@@ -51,12 +52,22 @@
]")))))
(deftest reify-object
+ (testing "empty methods"
+ (is (str/starts-with?
+ (bb nil "
+(str (reify Object))")
+ "babashka.impl.reify")))
(testing "toString"
(is (= ":foo"
(bb nil "
(def m (reify Object
(toString [_] (str :foo))))
(str m)
+"))))
+ (testing "toString + protocol"
+ (is (= ":dude1:dude2"
+ (bb nil "
+(defprotocol Dude (dude [_])) (def obj (reify Object (toString [_] (str :dude1)) Dude (dude [_] :dude2))) (str (str obj) (dude obj))
"))))
(testing "Hashcode still works when only overriding toString"
(is (number?
@@ -97,3 +108,48 @@
[x y] (bb nil prog)]
(is (pos? x))
(is (zero? y))))
+
+(deftest reify-default-method-test
+ (let [prog '(do (def iter (let [coll [:a :b :c] idx (volatile! -1)]
+ (reify java.util.Iterator (hasNext [_] (< @idx 2))
+ (next [_] (nth coll (vswap! idx inc))))))
+ (def res (volatile! []))
+ (vswap! res conj (.hasNext iter))
+ (vswap! res conj (.next iter))
+ (.forEachRemaining
+ iter (reify java.util.function.Consumer (accept [_ x] (vswap! res conj x))))
+ (= [true :a :b :c] @res))]
+ (is (true? (bb nil prog)))))
+
+(deftest reify-multiple-interfaces-test
+ (testing "throws exception"
+ (is (thrown?
+ clojure.lang.ExceptionInfo
+ (bb nil "
+(reify
+ java.lang.Object (toString [_] \"foo\")
+ clojure.lang.Seqable (seq [_] '(1 2 3)))")))))
+
+(deftest reify-runnable-and-garbage-collection-test
+ (is (bb nil "
+(def cleaner (java.lang.ref.Cleaner/create))
+(def deleted? (atom false))
+(defn make-cleanable-ref []
+ (let [obj (Object.)]
+ (.register cleaner obj
+ (reify java.lang.Runnable
+ (run [_]
+ (reset! deleted? true))))
+ nil))
+(defn force-gc []
+ (let [t (atom (Object.))
+ wr (java.lang.ref.WeakReference. @t)]
+ (reset! t nil)
+ (while (or (.get wr)
+ (not @deleted?))
+ (System/gc)
+ (System/runFinalization))))
+(make-cleanable-ref)
+(force-gc)
+@deleted?
+")))
diff --git a/test/babashka/scripts/check_var_names.bb b/test/babashka/scripts/check_var_names.bb
new file mode 100644
index 00000000..a689cb05
--- /dev/null
+++ b/test/babashka/scripts/check_var_names.bb
@@ -0,0 +1,14 @@
+(require '[clojure.string :as str])
+(let [ns-maps (->> (all-ns)
+ (map (fn [nmspc] [(ns-name nmspc) (ns-publics nmspc)]))
+ (into {})) ; a map of { ns-name {symbol var, ...}}
+ ns-maps (update ns-maps 'user #(dissoc % '*input*))] ; *input* is a special case that we'll skip over
+ (->>
+ (for [[ns-nm _] ns-maps
+ [sym vr] (ns-maps ns-nm)
+ :let [{var-meta-ns :ns, var-meta-name :name} (meta vr)
+ var-meta-ns-name (some-> var-meta-ns ns-name)]]
+ ; build a seq of maps containing the ns/symbol from the ns and the ns/symbol from the var's metadata
+ {:actual-ns ns-nm :actual-ns-symbol sym :var-meta-ns var-meta-ns-name :var-meta-name var-meta-name})
+ ; and remove the matches
+ (remove #(and (= (:actual-ns %) (:var-meta-ns %)) (= (:actual-ns-symbol %) (:var-meta-name %))))))
diff --git a/test/babashka/test_test.clj b/test/babashka/test_test.clj
index d88c3fd5..08a58249 100644
--- a/test/babashka/test_test.clj
+++ b/test/babashka/test_test.clj
@@ -108,3 +108,7 @@
(t/with-test-out (t/run-tests *ns*)))
(str/includes? (str sw) \"Ran 1 tests containing 2 assertions.\"))")]
(is (str/includes? output "true"))))
+
+(deftest line-number-test
+ (is (str/includes? (bb "test-resources/line_number_test_test.clj")
+ "line_number_test_test.clj:4")))