Merge branch 'master' into clojure.data.xml
This commit is contained in:
commit
90d16fbb74
44 changed files with 1749 additions and 298 deletions
|
|
@ -38,6 +38,7 @@ jobs:
|
||||||
name: Run JVM tests
|
name: Run JVM tests
|
||||||
command: |
|
command: |
|
||||||
script/test
|
script/test
|
||||||
|
script/run_lib_tests
|
||||||
# - run:
|
# - run:
|
||||||
# name: Run as tools.deps dependency
|
# name: Run as tools.deps dependency
|
||||||
# command: |
|
# command: |
|
||||||
|
|
@ -227,16 +228,20 @@ jobs:
|
||||||
paths:
|
paths:
|
||||||
- ~/.m2
|
- ~/.m2
|
||||||
key: v1-dependencies-{{ checksum "project.clj" }}
|
key: v1-dependencies-{{ checksum "project.clj" }}
|
||||||
# docker:
|
docker:
|
||||||
# docker:
|
docker:
|
||||||
# - image: circleci/buildpack-deps:stretch
|
- image: circleci/buildpack-deps:stretch
|
||||||
# steps:
|
steps:
|
||||||
# - checkout
|
- checkout
|
||||||
# - setup_remote_docker:
|
- run:
|
||||||
# docker_layer_caching: true
|
name: "Pull Submodules"
|
||||||
# - run:
|
command: |
|
||||||
# name: Build Docker image
|
git submodule init
|
||||||
# command: .circleci/script/docker
|
git submodule update
|
||||||
|
- setup_remote_docker
|
||||||
|
- run:
|
||||||
|
name: Build Docker image
|
||||||
|
command: .circleci/script/docker
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
|
|
@ -253,11 +258,11 @@ workflows:
|
||||||
- jvm
|
- jvm
|
||||||
- linux
|
- linux
|
||||||
- mac
|
- mac
|
||||||
# - docker:
|
- docker:
|
||||||
# filters:
|
filters:
|
||||||
# branches:
|
branches:
|
||||||
# only: master
|
only: master
|
||||||
# requires:
|
requires:
|
||||||
# - jvm
|
- jvm
|
||||||
# - linux
|
- linux
|
||||||
# - mac
|
- mac
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
image_name="borkdude/clj-kondo"
|
set -eo pipefail
|
||||||
image_tag=$(cat resources/CLJ_KONDO_VERSION)
|
|
||||||
|
image_name="borkdude/babashka"
|
||||||
|
image_tag=$(cat resources/BABASHKA_VERSION)
|
||||||
latest_tag="latest"
|
latest_tag="latest"
|
||||||
|
|
||||||
if [[ $image_tag =~ SNAPSHOT$ ]]; then
|
if [[ $image_tag =~ SNAPSHOT$ ]]; then
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
(require '[clojure.java.shell :refer [sh]]
|
(require '[clojure.java.shell :refer [sh]]
|
||||||
'[cheshire.core :refer [generate-string]])
|
'[clojure.java.io :as io]
|
||||||
|
'[cheshire.core :refer [generate-string]]
|
||||||
|
'[clojure.string :as str])
|
||||||
|
|
||||||
(def channel "#babashka_circleci_builds")
|
(def channel "#babashka_circleci_builds")
|
||||||
#_(def channel "#_test")
|
#_(def channel "#_test")
|
||||||
|
(def babashka-version (str/trim (slurp (io/file "resources" "BABASHKA_VERSION"))))
|
||||||
|
|
||||||
(def text (format "[%s - %s@%s]: https://%s-201467090-gh.circle-artifacts.com/0/release/babashka-0.0.61-SNAPSHOT-%s-amd64.zip"
|
(def text (format "[%s - %s@%s]: https://%s-201467090-gh.circle-artifacts.com/0/release/babashka-%s-%s-amd64.zip"
|
||||||
(System/getenv "BABASHKA_PLATFORM")
|
(System/getenv "BABASHKA_PLATFORM")
|
||||||
(System/getenv "CIRCLE_BRANCH")
|
(System/getenv "CIRCLE_BRANCH")
|
||||||
(System/getenv "CIRCLE_SHA1")
|
(System/getenv "CIRCLE_SHA1")
|
||||||
(System/getenv "CIRCLE_BUILD_NUM")
|
(System/getenv "CIRCLE_BUILD_NUM")
|
||||||
|
babashka-version
|
||||||
(System/getenv "BABASHKA_PLATFORM")))
|
(System/getenv "BABASHKA_PLATFORM")))
|
||||||
|
|
||||||
(def slack-hook-url (System/getenv "SLACK_HOOK_URL"))
|
(def slack-hook-url (System/getenv "SLACK_HOOK_URL"))
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,5 @@
|
||||||
babashka.impl.Pattern/gen-wrapper-fn clojure.core/def
|
babashka.impl.Pattern/gen-wrapper-fn clojure.core/def
|
||||||
babashka.impl.File/gen-wrapper-fn-2 clojure.core/def
|
babashka.impl.File/gen-wrapper-fn-2 clojure.core/def
|
||||||
babashka.impl.Pattern/gen-wrapper-fn-2 clojure.core/def
|
babashka.impl.Pattern/gen-wrapper-fn-2 clojure.core/def
|
||||||
babashka.impl.Pattern/gen-constants clojure.core/declare}}
|
babashka.impl.Pattern/gen-constants clojure.core/declare}
|
||||||
|
:linters {:unsorted-namespaces {:level :warning}}}
|
||||||
|
|
|
||||||
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
|
|
@ -1,6 +1,6 @@
|
||||||
# These are supported funding model platforms
|
# These are supported funding model platforms
|
||||||
|
|
||||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
github: borkdude # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
patreon: borkdude
|
patreon: borkdude
|
||||||
open_collective: # Replace with a single Open Collective username
|
open_collective: # Replace with a single Open Collective username
|
||||||
ko_fi: borkdude
|
ko_fi: borkdude
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
## Breaking changes
|
## Breaking changes
|
||||||
|
|
||||||
|
## v0.0.71
|
||||||
|
- #267 Change behavior of reader conditionals: the `:clj` branch is taken when
|
||||||
|
it occurs before a `:bb` branch.
|
||||||
|
|
||||||
## v0.0.44 - 0.0.45
|
## v0.0.44 - 0.0.45
|
||||||
- #173: Rename `*in*` to `*input*` (in the `user` namespace). The reason for
|
- #173: Rename `*in*` to `*input*` (in the `user` namespace). The reason for
|
||||||
this is that itt shadowed `clojure.core/*in*` when used unqualified.
|
this is that itt shadowed `clojure.core/*in*` when used unqualified.
|
||||||
|
|
|
||||||
20
Dockerfile
Normal file
20
Dockerfile
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
FROM ubuntu AS BASE
|
||||||
|
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get install -yy curl unzip build-essential zlib1g-dev
|
||||||
|
WORKDIR "/opt"
|
||||||
|
RUN curl -sLO https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-19.3.1/graalvm-ce-java8-linux-amd64-19.3.1.tar.gz
|
||||||
|
RUN tar -xzf graalvm-ce-java8-linux-amd64-19.3.1.tar.gz
|
||||||
|
ENV GRAALVM_HOME="/opt/graalvm-ce-java8-19.3.1"
|
||||||
|
ENV JAVA_HOME="/opt/graalvm-ce-java8-19.3.1/bin"
|
||||||
|
ENV PATH="$PATH:$JAVA_HOME"
|
||||||
|
COPY . .
|
||||||
|
RUN apt install -y sudo
|
||||||
|
RUN ./.circleci/script/install-leiningen
|
||||||
|
RUN ./script/compile
|
||||||
|
RUN cp bb /usr/local/bin
|
||||||
|
|
||||||
|
|
||||||
|
FROM ubuntu:bionic
|
||||||
|
COPY --from=BASE /usr/local/bin/bb /usr/local/bin
|
||||||
|
CMD ["bb"]
|
||||||
235
README.md
235
README.md
|
|
@ -109,6 +109,11 @@ Upgrade:
|
||||||
|
|
||||||
yay -S babashka-bin
|
yay -S babashka-bin
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
On Windows you can install using [scoop](https://scoop.sh/) and the
|
||||||
|
[scoop-clojure](https://github.com/littleli/scoop-clojure) bucket.
|
||||||
|
|
||||||
### Installer script
|
### Installer script
|
||||||
|
|
||||||
Install via the installer script:
|
Install via the installer script:
|
||||||
|
|
@ -153,9 +158,10 @@ Options:
|
||||||
-f, --file <path> Evaluate a file.
|
-f, --file <path> Evaluate a file.
|
||||||
-cp, --classpath Classpath to use.
|
-cp, --classpath Classpath to use.
|
||||||
-m, --main <ns> Call the -main function from namespace with args.
|
-m, --main <ns> Call the -main function from namespace with args.
|
||||||
--repl Start REPL
|
--repl Start REPL. Use rlwrap for history.
|
||||||
--socket-repl Start socket REPL. Specify port (e.g. 1666) or host and port separated by colon (e.g. 127.0.0.1:1666).
|
--socket-repl Start socket REPL. Specify port (e.g. 1666) or host and port separated by colon (e.g. 127.0.0.1:1666).
|
||||||
--time Print execution time before exiting.
|
--time Print execution time before exiting.
|
||||||
|
-- Stop parsing args and pass everything after -- to *command-line-args*
|
||||||
|
|
||||||
If neither -e, -f, or --socket-repl are specified, then the first argument that is not parsed as a option is treated as a file if it exists, or as an expression otherwise.
|
If neither -e, -f, or --socket-repl are specified, then the first argument that is not parsed as a option is treated as a file if it exists, or as an expression otherwise.
|
||||||
Everything after that is bound to *command-line-args*.
|
Everything after that is bound to *command-line-args*.
|
||||||
|
|
@ -172,7 +178,7 @@ enumerated explicitly.
|
||||||
- `clojure.set` aliased as `set`
|
- `clojure.set` aliased as `set`
|
||||||
- `clojure.edn` aliased as `edn`:
|
- `clojure.edn` aliased as `edn`:
|
||||||
- `read-string`
|
- `read-string`
|
||||||
- `clojure.java.shell` aliases as `shell`
|
- `clojure.java.shell` aliased as `shell`
|
||||||
- `clojure.java.io` aliased as `io`:
|
- `clojure.java.io` aliased as `io`:
|
||||||
- `as-relative-path`, `as-url`, `copy`, `delete-file`, `file`, `input-stream`,
|
- `as-relative-path`, `as-url`, `copy`, `delete-file`, `file`, `input-stream`,
|
||||||
`make-parents`, `output-stream`, `reader`, `resource`, `writer`
|
`make-parents`, `output-stream`, `reader`, `resource`, `writer`
|
||||||
|
|
@ -181,6 +187,8 @@ enumerated explicitly.
|
||||||
`async`. The `alt` and `go` macros are not available but `alts!!` does work as
|
`async`. The `alt` and `go` macros are not available but `alts!!` does work as
|
||||||
it is a function.
|
it is a function.
|
||||||
- `clojure.stacktrace`
|
- `clojure.stacktrace`
|
||||||
|
- `clojure.test`
|
||||||
|
- `clojure.pprint`: `pprint` (currently backed by [fipp](https://github.com/brandonbloom/fipp)'s `fipp.edn/pprint`)
|
||||||
- [`clojure.tools.cli`](https://github.com/clojure/tools.cli) aliased as `tools.cli`
|
- [`clojure.tools.cli`](https://github.com/clojure/tools.cli) aliased as `tools.cli`
|
||||||
- [`clojure.data.csv`](https://github.com/clojure/data.csv) aliased as `csv`
|
- [`clojure.data.csv`](https://github.com/clojure/data.csv) aliased as `csv`
|
||||||
- [`cheshire.core`](https://github.com/dakrone/cheshire) aliased as `json`
|
- [`cheshire.core`](https://github.com/dakrone/cheshire) aliased as `json`
|
||||||
|
|
@ -241,11 +249,29 @@ $ bb example.clj
|
||||||
|
|
||||||
Command-line arguments can be retrieved using `*command-line-args*`.
|
Command-line arguments can be retrieved using `*command-line-args*`.
|
||||||
|
|
||||||
### Additional functions
|
### Additional namespaces
|
||||||
|
|
||||||
Additionally, babashka adds the following functions:
|
#### babashka.classpath
|
||||||
|
|
||||||
- `wait/wait-for-port`. Usage:
|
Contains the function `add-classpath` which can be used to add to the classpath
|
||||||
|
dynamically:
|
||||||
|
|
||||||
|
``` clojure
|
||||||
|
(require '[babashka.classpath :refer [add-classpath]]
|
||||||
|
'[clojure.java.shell :refer [sh]])
|
||||||
|
(def medley-dep '{:deps {medley {:git/url "https://github.com/borkdude/medley"
|
||||||
|
:sha "91adfb5da33f8d23f75f0894da1defe567a625c0"}}})
|
||||||
|
(def cp (:out (sh "clojure" "-Spath" "-Sdeps" (str medley-dep))))
|
||||||
|
(add-classpath cp)
|
||||||
|
(require '[medley.core :as m])
|
||||||
|
(m/index-by :id [{:id 1} {:id 2}]) ;;=> {1 {:id 1}, 2 {:id 2}}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### babashka.wait
|
||||||
|
|
||||||
|
Contains the functions: `wait-for-port` and `wait-for-path`.
|
||||||
|
|
||||||
|
Usage of `wait-for-port`:
|
||||||
|
|
||||||
``` clojure
|
``` clojure
|
||||||
(wait/wait-for-port "localhost" 8080)
|
(wait/wait-for-port "localhost" 8080)
|
||||||
|
|
@ -254,29 +280,37 @@ Additionally, babashka adds the following functions:
|
||||||
|
|
||||||
Waits for TCP connection to be available on host and port. Options map supports `:timeout` and `:pause`. If `:timeout` is provided and reached, `:default`'s value (if any) is returned. The `:pause` option determines the time waited between retries.
|
Waits for TCP connection to be available on host and port. Options map supports `:timeout` and `:pause`. If `:timeout` is provided and reached, `:default`'s value (if any) is returned. The `:pause` option determines the time waited between retries.
|
||||||
|
|
||||||
- `wait/wait-for-path`. Usage:
|
Usage of `wait-for-path`:
|
||||||
|
|
||||||
``` clojure
|
``` clojure
|
||||||
(wait/wait-for-path "/tmp/wait-path-test")
|
(wait/wait-for-path "/tmp/wait-path-test")
|
||||||
(wait/wait-for-path "/tmp/wait-path-test" {:timeout 1000 :pause 1000})
|
(wait/wait-for-path "/tmp/wait-path-test" {:timeout 1000 :pause 1000})
|
||||||
```
|
```
|
||||||
|
|
||||||
Waits for file path to be available. Options map supports `:default`, `:timeout` and `:pause`. If `:timeout` is provided and reached, `:default`'s value (if any) is returned. The `:pause` option determines the time waited between retries.
|
Waits for file path to be available. Options map supports `:default`, `:timeout`
|
||||||
|
and `:pause`. If `:timeout` is provided and reached, `:default`'s value (if any)
|
||||||
|
is returned. The `:pause` option determines the time waited between retries.
|
||||||
|
|
||||||
- `sig/pipe-signal-received?`. Usage:
|
The namespace `babashka.wait` is aliased as `wait` in the `user` namespace.
|
||||||
|
|
||||||
|
#### babashka.signal
|
||||||
|
|
||||||
|
Contains the function `signal/pipe-signal-received?`. Usage:
|
||||||
|
|
||||||
``` clojure
|
``` clojure
|
||||||
(sig/pipe-signal-received?)
|
(signal/pipe-signal-received?)
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns true if `PIPE` signal was received. Example:
|
Returns true if `PIPE` signal was received. Example:
|
||||||
|
|
||||||
``` shellsession
|
``` shellsession
|
||||||
$ bb '((fn [x] (println x) (when (not (sig/pipe-signal-received?)) (recur (inc x)))) 0)' | head -n2
|
$ bb '((fn [x] (println x) (when (not (signal/pipe-signal-received?)) (recur (inc x)))) 0)' | head -n2
|
||||||
1
|
1
|
||||||
2
|
2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The namespace `babashka.signal` is aliased as `signal` in the `user` namespace.
|
||||||
|
|
||||||
## Running a file
|
## Running a file
|
||||||
|
|
||||||
Scripts may be executed from a file using `-f` or `--file`:
|
Scripts may be executed from a file using `-f` or `--file`:
|
||||||
|
|
@ -485,19 +519,49 @@ $ bb script.clj -h
|
||||||
|
|
||||||
## Reader conditionals
|
## Reader conditionals
|
||||||
|
|
||||||
Babashka supports reader conditionals using the `:bb` feature:
|
Babashka supports reader conditionals by taking either the `:bb` or `:clj`
|
||||||
|
branch, whichever comes first. NOTE: the `:clj` branch behavior was added in
|
||||||
|
version 0.0.71, before that version the `:clj` branch was ignored.
|
||||||
|
|
||||||
``` clojure
|
``` clojure
|
||||||
$ cat example.clj
|
$ bb "#?(:bb :hello :clj :bye)"
|
||||||
#?(:clj (in-ns 'foo) :bb (println "babashka doesn't support in-ns yet!"))
|
:hello
|
||||||
|
|
||||||
$ ./bb example.clj
|
$ bb "#?(:clj :bye :bb :hello)"
|
||||||
babashka doesn't support in-ns yet!
|
:bye
|
||||||
|
|
||||||
|
$ bb "[1 2 #?@(:bb [] :clj [1])]"
|
||||||
|
[1 2]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Socket REPL
|
## Running tests
|
||||||
|
|
||||||
Start the socket REPL like this:
|
Babashka bundles `clojure.test`. To make CI scripts fail you can use a simple
|
||||||
|
runner like this:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
bb -cp "src:test:resources" \
|
||||||
|
-e "(require '[clojure.test :as t] '[borkdude.deps-test])
|
||||||
|
(let [{:keys [:fail :error]} (t/run-tests 'borkdude.deps-test)]
|
||||||
|
(System/exit (+ fail error)))"
|
||||||
|
```
|
||||||
|
|
||||||
|
## REPL
|
||||||
|
|
||||||
|
Babashka supports both a REPL and socket REPL. To start the REPL, type:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
$ bb --repl
|
||||||
|
```
|
||||||
|
|
||||||
|
To get history with up and down arrows, use `rlwrap`:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
$ rlwrap bb --repl
|
||||||
|
```
|
||||||
|
|
||||||
|
To start the socket REPL you can do this:
|
||||||
|
|
||||||
``` shellsession
|
``` shellsession
|
||||||
$ bb --socket-repl 1666
|
$ bb --socket-repl 1666
|
||||||
|
|
@ -571,8 +635,10 @@ Differences with Clojure:
|
||||||
|
|
||||||
- A subset of Java classes are supported.
|
- A subset of Java classes are supported.
|
||||||
|
|
||||||
- Only the `clojure.core`, `clojure.set`, `clojure.string` and `clojure.walk`
|
- Only the `clojure.core`, `clojure.edn`, `clojue.java.io`,
|
||||||
namespaces are available from Clojure.
|
`clojure.java.shell`, `clojure.set`, `clojure.stacktrace`, `clojure.string`,
|
||||||
|
`clojure.template`, `clojure.test` and `clojure.walk` namespaces are available
|
||||||
|
from Clojure.
|
||||||
|
|
||||||
- Interpretation comes with overhead. Therefore tight loops are likely slower
|
- Interpretation comes with overhead. Therefore tight loops are likely slower
|
||||||
than in Clojure on the JVM.
|
than in Clojure on the JVM.
|
||||||
|
|
@ -590,14 +656,43 @@ The following libraries are known to work with Babashka:
|
||||||
A port of the [clojure](https://github.com/clojure/brew-install/) bash script to
|
A port of the [clojure](https://github.com/clojure/brew-install/) bash script to
|
||||||
Clojure / babashka.
|
Clojure / babashka.
|
||||||
|
|
||||||
#### [spartan.test](https://github.com/borkdude/spartan.test/)
|
#### [spartan.spec](https://github.com/borkdude/spartan.spec/)
|
||||||
|
|
||||||
A minimal test framework compatible with babashka.
|
An babashka-compatible implementation of `clojure.spec.alpha`.
|
||||||
|
|
||||||
#### [medley](https://github.com/borkdude/medley/)
|
#### [missing.test.assertions](https://github.com/borkdude/missing.test.assertions)
|
||||||
|
|
||||||
A fork of [medley](https://github.com/weavejester/medley) made compatible with
|
This library checks if no assertions have been made in a test:
|
||||||
babashka. Requires `bb` >= v0.0.58.
|
|
||||||
|
``` shell
|
||||||
|
$ export BABASHKA_CLASSPATH=$(clojure -Spath -Sdeps '{:deps {borkdude/missing.test.assertions {:git/url "https://github.com/borkdude/missing.test.assertions" :sha "603cb01bee72fb17addacc53c34c85612684ad70"}}}')
|
||||||
|
|
||||||
|
$ lein bb "(require '[missing.test.assertions] '[clojure.test :as t]) (t/deftest foo) (t/run-tests)"
|
||||||
|
|
||||||
|
Testing user
|
||||||
|
WARNING: no assertions made in test foo
|
||||||
|
|
||||||
|
Ran 1 tests containing 0 assertions.
|
||||||
|
0 failures, 0 errors.
|
||||||
|
{:test 1, :pass 0, :fail 0, :error 0, :type :summary}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### [medley](https://github.com/weavejester/medley/)
|
||||||
|
|
||||||
|
Requires `bb` >= v0.0.71. Latest coordinates checked with with bb:
|
||||||
|
|
||||||
|
``` clojure
|
||||||
|
{:git/url "https://github.com/weavejester" :sha "a4e5fb5383f5c0d83cb2d005181a35b76d8a136d"}
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
$ export BABASHKA_CLASSPATH=$(clojure -Spath -Sdeps '{:deps {medley {:git/url "https://github.com/weavejester" :sha "a4e5fb5383f5c0d83cb2d005181a35b76d8a136d"}}}')
|
||||||
|
|
||||||
|
$ bb -e "(require '[medley.core :as m]) (m/index-by :id [{:id 1} {:id 2}])"
|
||||||
|
{1 {:id 1}, 2 {:id 2}}
|
||||||
|
```
|
||||||
|
|
||||||
#### [clj-http-lite](https://github.com/borkdude/clj-http-lite)
|
#### [clj-http-lite](https://github.com/borkdude/clj-http-lite)
|
||||||
|
|
||||||
|
|
@ -612,7 +707,15 @@ $ bb "(require '[clj-http.lite.client :as client]) (:status (client/get \"https:
|
||||||
|
|
||||||
#### [limit-break](https://github.com/technomancy/limit-break)
|
#### [limit-break](https://github.com/technomancy/limit-break)
|
||||||
|
|
||||||
A debug REPL library. Example:
|
A debug REPL library.
|
||||||
|
|
||||||
|
Latest coordinates checked with with bb:
|
||||||
|
|
||||||
|
``` clojure
|
||||||
|
{:git/url "https://github.com/technomancy/limit-break" :sha "050fcfa0ea29fe3340927533a6fa6fffe23bfc2f" :deps/manifest :deps}
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
``` shell
|
``` shell
|
||||||
$ export BABASHKA_CLASSPATH="$(clojure -Sdeps '{:deps {limit-break {:git/url "https://github.com/technomancy/limit-break" :sha "050fcfa0ea29fe3340927533a6fa6fffe23bfc2f" :deps/manifest :deps}}}' -Spath)"
|
$ export BABASHKA_CLASSPATH="$(clojure -Sdeps '{:deps {limit-break {:git/url "https://github.com/technomancy/limit-break" :sha "050fcfa0ea29fe3340927533a6fa6fffe23bfc2f" :deps/manifest :deps}}}' -Spath)"
|
||||||
|
|
@ -626,8 +729,47 @@ break> x
|
||||||
1
|
1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### [clojure-csv](https://github.com/davidsantiago/clojure-csv)
|
||||||
|
|
||||||
|
A library for reading and writing CSV files. Note that babashka already comes
|
||||||
|
with `clojure.data.csv`, but in case you need this other library, this is how
|
||||||
|
you can use it:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
export BABASHKA_CLASSPATH="$(clojure -Sdeps '{:deps {clojure-csv {:mvn/version "RELEASE"}}}' -Spath)"
|
||||||
|
|
||||||
|
./bb -e "
|
||||||
|
(require '[clojure-csv.core :as csv])
|
||||||
|
(csv/write-csv (csv/parse-csv \"a,b,c\n1,2,3\"))
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### [regal](https://github.com/lambdaisland/regal)
|
||||||
|
|
||||||
|
Requires `bb` >= v0.0.71. Latest coordinates checked with with bb:
|
||||||
|
|
||||||
|
``` clojure
|
||||||
|
{:git/url "https://github.com/lambdaisland/regal" :sha "8d300f8e15f43480801766b7762530b6d412c1e6"}
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
$ export BABASHKA_CLASSPATH=$(clojure -Spath -Sdeps '{:deps {regal {:git/url "https://github.com/lambdaisland/regal" :sha "8d300f8e15f43480801766b7762530b6d412c1e6"}}}')
|
||||||
|
|
||||||
|
$ bb -e "(require '[lambdaisland.regal :as regal]) (regal/regex [:* \"ab\"])"
|
||||||
|
#"(?:\Qab\E)*"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### [spartan.test](https://github.com/borkdude/spartan.test/)
|
||||||
|
|
||||||
|
A minimal test framework compatible with babashka. This library is deprecated
|
||||||
|
since babashka v0.0.68 which has `clojure.test` built-in.
|
||||||
|
|
||||||
|
|
||||||
### Blogs
|
### Blogs
|
||||||
|
|
||||||
|
- [Babashka: a quick example](https://juxt.pro/blog/posts/babashka.html) by Malcolm Sparks
|
||||||
- [Clojure Start Time in 2019](https://stuartsierra.com/2019/12/21/clojure-start-time-in-2019) by Stuart Sierra
|
- [Clojure Start Time in 2019](https://stuartsierra.com/2019/12/21/clojure-start-time-in-2019) by Stuart Sierra
|
||||||
- [Advent of Random
|
- [Advent of Random
|
||||||
Hacks](https://lambdaisland.com/blog/2019-12-19-advent-of-parens-19-advent-of-random-hacks)
|
Hacks](https://lambdaisland.com/blog/2019-12-19-advent-of-parens-19-advent-of-random-hacks)
|
||||||
|
|
@ -704,14 +846,31 @@ bb '(-> *input* first :name (subs 1))'
|
||||||
"0.0.4"
|
"0.0.4"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Get latest OS-specific download url from Github
|
### Generate deps.edn entry for a gitlib
|
||||||
|
|
||||||
``` shellsession
|
``` clojure
|
||||||
$ curl -s https://api.github.com/repos/borkdude/babashka/releases |
|
#!/usr/bin/env bb
|
||||||
jet --from json --keywordize |
|
|
||||||
bb '(-> *input* first :assets)' |
|
(require '[clojure.java.shell :refer [sh]]
|
||||||
bb '(some #(re-find #".*linux.*" (:browser_download_url %)) *input*)'
|
'[clojure.string :as str])
|
||||||
"https://github.com/borkdude/babashka/releases/download/v0.0.4/babashka-0.0.4-linux-amd64.zip"
|
|
||||||
|
(let [[username project branch] *command-line-args*
|
||||||
|
branch (or branch "master")
|
||||||
|
url (str "https://github.com/" username "/" project)
|
||||||
|
sha (-> (sh "git" "ls-remote" url branch)
|
||||||
|
:out
|
||||||
|
(str/split #"\s")
|
||||||
|
first)]
|
||||||
|
{:git/url url
|
||||||
|
:sha sha})
|
||||||
|
```
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
$ gitlib.clj nate fs
|
||||||
|
{:git/url "https://github.com/nate/fs", :sha "75b9fcd399ac37cb4f9752a4c7a6755f3fbbc000"}
|
||||||
|
$ clj -Sdeps "{:deps {fs $(gitlib.clj nate fs)}}" \
|
||||||
|
-e "(require '[nate.fs :as fs]) (fs/creation-time \".\")"
|
||||||
|
#object[java.nio.file.attribute.FileTime 0x5c748168 "2019-07-05T14:06:26Z"]
|
||||||
```
|
```
|
||||||
|
|
||||||
### View download statistics from Clojars
|
### View download statistics from Clojars
|
||||||
|
|
@ -789,6 +948,18 @@ See [examples/http_server.clj](https://github.com/borkdude/babashka/blob/master/
|
||||||
|
|
||||||
Original by [@souenzzo](https://gist.github.com/souenzzo/a959a4c5b8c0c90df76fe33bb7dfe201)
|
Original by [@souenzzo](https://gist.github.com/souenzzo/a959a4c5b8c0c90df76fe33bb7dfe201)
|
||||||
|
|
||||||
|
### Print random docstring
|
||||||
|
|
||||||
|
See [examples/random_doc.clj](https://github.com/borkdude/babashka/blob/master/examples/random_doc.clj)
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
$ examples/random_doc.clj
|
||||||
|
-------------------------
|
||||||
|
clojure.core/ffirst
|
||||||
|
([x])
|
||||||
|
Same as (first (first x))
|
||||||
|
```
|
||||||
|
|
||||||
## Thanks
|
## Thanks
|
||||||
|
|
||||||
- [adgoji](https://www.adgoji.com/) for financial support
|
- [adgoji](https://www.adgoji.com/) for financial support
|
||||||
|
|
|
||||||
61
appveyor.yml
Normal file
61
appveyor.yml
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
version: "v-{build}"
|
||||||
|
|
||||||
|
image: Visual Studio 2015
|
||||||
|
|
||||||
|
clone_folder: C:\projects\babashka
|
||||||
|
|
||||||
|
environment:
|
||||||
|
GRAALVM_HOME: C:\projects\babashka\graalvm\graalvm-ce-java8-19.3.0
|
||||||
|
|
||||||
|
cache:
|
||||||
|
- C:\ProgramData\chocolatey\lib -> project.clj, appveyor.yml
|
||||||
|
- '%USERPROFILE%\.m2 -> project.clj'
|
||||||
|
- 'graalvm -> appveyor.yml'
|
||||||
|
|
||||||
|
clone_script:
|
||||||
|
- ps: >-
|
||||||
|
if(-not $env:APPVEYOR_PULL_REQUEST_NUMBER) {
|
||||||
|
git clone -q --branch=$env:APPVEYOR_REPO_BRANCH https://github.com/$env:APPVEYOR_REPO_NAME.git $env:APPVEYOR_BUILD_FOLDER
|
||||||
|
cd $env:APPVEYOR_BUILD_FOLDER
|
||||||
|
git checkout -qf $env:APPVEYOR_REPO_COMMIT
|
||||||
|
} else {
|
||||||
|
git clone -q https://github.com/$env:APPVEYOR_REPO_NAME.git $env:APPVEYOR_BUILD_FOLDER
|
||||||
|
cd $env:APPVEYOR_BUILD_FOLDER
|
||||||
|
git fetch -q origin +refs/pull/$env:APPVEYOR_PULL_REQUEST_NUMBER/merge:
|
||||||
|
git checkout -qf FETCH_HEAD
|
||||||
|
}
|
||||||
|
- cmd: git submodule update --init --recursive
|
||||||
|
|
||||||
|
build_script:
|
||||||
|
- cmd: >-
|
||||||
|
powershell -Command "(New-Object Net.WebClient).DownloadFile('https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein.bat', 'lein.bat')"
|
||||||
|
|
||||||
|
call lein self-install
|
||||||
|
|
||||||
|
# set CLJ_KONDO_TEST_ENV=jvm
|
||||||
|
|
||||||
|
# call script/test.bat
|
||||||
|
|
||||||
|
- cmd: >-
|
||||||
|
choco install windows-sdk-7.1
|
||||||
|
|
||||||
|
call "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd"
|
||||||
|
|
||||||
|
powershell -Command "if (Test-Path('graalvm')) { return } else { (New-Object Net.WebClient).DownloadFile('https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-19.3.0/graalvm-ce-java8-windows-amd64-19.3.0.zip', 'graalvm.zip') }"
|
||||||
|
|
||||||
|
powershell -Command "if (Test-Path('graalvm')) { return } else { Expand-Archive graalvm.zip graalvm }"
|
||||||
|
|
||||||
|
# call script/compile.bat
|
||||||
|
|
||||||
|
# - cmd: >-
|
||||||
|
# lein clean
|
||||||
|
|
||||||
|
# set CLJ_KONDO_TEST_ENV=native
|
||||||
|
|
||||||
|
# call script/test.bat
|
||||||
|
|
||||||
|
# artifacts:
|
||||||
|
# - path: babashka-*-windows-amd64.zip
|
||||||
|
# name: babashka
|
||||||
19
deps.edn
19
deps.edn
|
|
@ -1,13 +1,20 @@
|
||||||
{:paths ["src" "sci/src" "resources" "sci/resources"],
|
{:paths ["src" "sci/src" "resources" "sci/resources"],
|
||||||
:deps {org.clojure/clojure {:mvn/version "1.10.1"},
|
:deps {org.clojure/clojure {:mvn/version "1.10.1"},
|
||||||
org.clojure/tools.reader {:mvn/version "1.3.2"},
|
org.clojure/tools.reader {:mvn/version "1.3.2"},
|
||||||
borkdude/edamame {:mvn/version "0.0.10-alpha.4"},
|
borkdude/edamame {:mvn/version "0.0.10"},
|
||||||
borkdude/graal.locking {:mvn/version "0.0.2"},
|
borkdude/graal.locking {:mvn/version "0.0.2"},
|
||||||
borkdude/sci.impl.reflector {:mvn/version "0.0.1"}
|
borkdude/sci.impl.reflector {:mvn/version "0.0.1"}
|
||||||
org.clojure/core.async {:mvn/version "0.4.500"},
|
org.clojure/core.async {:mvn/version "1.0.567"},
|
||||||
org.clojure/tools.cli {:mvn/version "0.4.2"},
|
org.clojure/tools.cli {:mvn/version "0.4.2"},
|
||||||
org.clojure/data.csv {:mvn/version "0.1.4"},
|
org.clojure/data.csv {:mvn/version "1.0.0"},
|
||||||
org.clojure/data.xml {:mvn/version "0.2.0-alpha6"},
|
cheshire {:mvn/version "5.10.0"}
|
||||||
cheshire {:mvn/version "5.9.0"}}
|
org.clojure/data.xml {:mvn/version "0.2.0-alpha6"}
|
||||||
|
fipp {:mvn/version "0.6.22"}}
|
||||||
:aliases {:main
|
:aliases {:main
|
||||||
{:main-opts ["-m" "babashka.main"]}}}
|
{:main-opts ["-m" "babashka.main"]}
|
||||||
|
:profile
|
||||||
|
{:extra-deps
|
||||||
|
{com.clojure-goes-fast/clj-async-profiler {:mvn/version "0.4.0"}}
|
||||||
|
:extra-paths ["test"]
|
||||||
|
:jvm-opts ["-Djdk.attach.allowAttachSelf"]
|
||||||
|
:main-opts ["-m" "babashka.profile"]}}}
|
||||||
|
|
|
||||||
20
doc/dev.md
20
doc/dev.md
|
|
@ -43,7 +43,7 @@ Test the native version:
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
To build this project, set `$GRAALVM_HOME` to the GraalVM distribution directory.
|
To build this project, set `$GRAALVM_HOME` to the GraalVM distribution directory. Currently we are using GraalVM JDK8.
|
||||||
|
|
||||||
Then run:
|
Then run:
|
||||||
|
|
||||||
|
|
@ -58,6 +58,22 @@ We're only registering the size of the macOS binary (as built on CircleCI).
|
||||||
2020/01/08, ..., 38.7mb / 11.3mb zipped
|
2020/01/08, ..., 38.7mb / 11.3mb zipped
|
||||||
Added: `clojure.data.xml`. Growth: 1.8mb / 0.4mb zipped.
|
Added: `clojure.data.xml`. Growth: 1.8mb / 0.4mb zipped.
|
||||||
|
|
||||||
2020/01/08, 303ca9e825d76a4a45bc4240a59139d342c13964: 36.9mb / 10.8mb zipped.
|
2020/02/19, e43727955a2cdabd2bb0189c20dd7f9a18156fc9
|
||||||
|
Added fipp.edn/pprint
|
||||||
|
40598268 - 39744804 = 853kb added.
|
||||||
|
|
||||||
|
2020/02/09, c8fd1c7931d7842ebaec1fa8faf06d4ab58573bd
|
||||||
|
Added java.lang.BigInteger and java.security.MessageDigest.
|
||||||
|
39281972 - 39072764 = 209kb added.
|
||||||
|
|
||||||
|
2020/04/02 v0.0.69 38883676
|
||||||
|
|
||||||
|
2020/01/24, 43eef7075f9dac038d8d28a5ee4e49b6affd9864: 38.3mb, 11.1mb zipped
|
||||||
|
Added hierarchies (derive, isa?, etc).
|
||||||
|
|
||||||
|
2020/01/23, 485fef7df54d6701936704573468a1ec4c66d221: 37.4mb / 10.9mb zipped
|
||||||
|
Added: StringBuilder, java.io.{Reader,Writer,PrinterWriter,PushbackReader}
|
||||||
|
|
||||||
|
2020/01/08, 303ca9e825d76a4a45bc4240a59139d342c13964: 36.9mb / 10.8mb zipped
|
||||||
|
|
||||||
Removing cheshire from bb: 36.2mb / 10.5mb zipped.
|
Removing cheshire from bb: 36.2mb / 10.5mb zipped.
|
||||||
|
|
|
||||||
11
examples/random_doc.clj
Executable file
11
examples/random_doc.clj
Executable file
|
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/env bb
|
||||||
|
|
||||||
|
(require '[clojure.repl])
|
||||||
|
|
||||||
|
(defmacro random-doc []
|
||||||
|
(let [sym (-> (ns-publics 'clojure.core) keys rand-nth)]
|
||||||
|
(if (:doc (meta (resolve sym)))
|
||||||
|
`(clojure.repl/doc ~sym)
|
||||||
|
`(random-doc))))
|
||||||
|
|
||||||
|
(random-doc)
|
||||||
12
project.clj
12
project.clj
|
|
@ -11,15 +11,17 @@
|
||||||
:resource-paths ["resources" "sci/resources"]
|
:resource-paths ["resources" "sci/resources"]
|
||||||
:dependencies [[org.clojure/clojure "1.10.1"]
|
:dependencies [[org.clojure/clojure "1.10.1"]
|
||||||
[org.clojure/tools.reader "1.3.2"]
|
[org.clojure/tools.reader "1.3.2"]
|
||||||
[borkdude/edamame "0.0.10-alpha.4"]
|
[borkdude/edamame "0.0.10"]
|
||||||
[borkdude/graal.locking "0.0.2"]
|
[borkdude/graal.locking "0.0.2"]
|
||||||
[borkdude/sci.impl.reflector "0.0.1"]
|
[borkdude/sci.impl.reflector "0.0.1"]
|
||||||
[org.clojure/core.async "0.4.500"]
|
[org.clojure/core.async "1.0.567"]
|
||||||
[org.clojure/tools.cli "0.4.2"]
|
[org.clojure/tools.cli "0.4.2"]
|
||||||
[org.clojure/data.csv "0.1.4"]
|
[org.clojure/data.csv "1.0.0"]
|
||||||
[org.clojure/data.xml "0.2.0-alpha6"]
|
[org.clojure/data.xml "0.2.0-alpha6"]
|
||||||
[cheshire "5.9.0"]]
|
[cheshire "5.10.0"]
|
||||||
:profiles {:test {:dependencies [[clj-commons/conch "0.9.2"]]}
|
[fipp "0.6.22"]]
|
||||||
|
:profiles {:test {:dependencies [[clj-commons/conch "0.9.2"]
|
||||||
|
[com.clojure-goes-fast/clj-async-profiler "0.4.0"]]}
|
||||||
:uberjar {:global-vars {*assert* false}
|
:uberjar {:global-vars {*assert* false}
|
||||||
:jvm-opts ["-Dclojure.compiler.direct-linking=true"
|
:jvm-opts ["-Dclojure.compiler.direct-linking=true"
|
||||||
"-Dclojure.spec.skip-macros=true"]
|
"-Dclojure.spec.skip-macros=true"]
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
0.0.60
|
0.0.71
|
||||||
|
|
@ -1 +1 @@
|
||||||
0.0.61-SNAPSHOT
|
0.0.72-SNAPSHOT
|
||||||
2
sci
2
sci
|
|
@ -1 +1 @@
|
||||||
Subproject commit 57b584ba0a6a1f74a887d350463a700976dd37d8
|
Subproject commit eebb456628beb2ac0d1e31c2be46ee0683b9ee7a
|
||||||
|
|
@ -2,19 +2,17 @@
|
||||||
|
|
||||||
set -eo pipefail
|
set -eo pipefail
|
||||||
|
|
||||||
NATIVE_IMAGE=`which native-image` || true
|
|
||||||
|
|
||||||
if [ -z "$NATIVE_IMAGE" ]; then
|
|
||||||
if [ -z "$GRAALVM_HOME" ]; then
|
if [ -z "$GRAALVM_HOME" ]; then
|
||||||
echo "Please set GRAALVM_HOME"
|
echo "Please set GRAALVM_HOME"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
"$GRAALVM_HOME/bin/gu" install native-image || true
|
if [ -z "$BABASHKA_XMX" ]; then
|
||||||
|
export BABASHKA_XMX="-J-Xmx3g"
|
||||||
NATIVE_IMAGE="$GRAALVM_HOME/bin/native-image"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
"$GRAALVM_HOME/bin/gu" install native-image || true
|
||||||
|
|
||||||
BABASHKA_VERSION=$(cat resources/BABASHKA_VERSION)
|
BABASHKA_VERSION=$(cat resources/BABASHKA_VERSION)
|
||||||
|
|
||||||
# # We also need to AOT sci, else something didn't work in the Mac build on CircleCI
|
# # We also need to AOT sci, else something didn't work in the Mac build on CircleCI
|
||||||
|
|
@ -23,10 +21,12 @@ BABASHKA_VERSION=$(cat resources/BABASHKA_VERSION)
|
||||||
# mkdir -p src/sci
|
# mkdir -p src/sci
|
||||||
# cp -R /tmp/sci/src/* src
|
# cp -R /tmp/sci/src/* src
|
||||||
|
|
||||||
|
export JAVA_HOME=$GRAALVM_HOME
|
||||||
|
|
||||||
lein with-profiles +reflection do run
|
lein with-profiles +reflection do run
|
||||||
lein do clean, uberjar
|
lein do clean, uberjar
|
||||||
|
|
||||||
$NATIVE_IMAGE \
|
$GRAALVM_HOME/bin/native-image \
|
||||||
-jar target/babashka-$BABASHKA_VERSION-standalone.jar \
|
-jar target/babashka-$BABASHKA_VERSION-standalone.jar \
|
||||||
-H:Name=bb \
|
-H:Name=bb \
|
||||||
-H:+ReportExceptionStackTraces \
|
-H:+ReportExceptionStackTraces \
|
||||||
|
|
@ -44,6 +44,6 @@ $NATIVE_IMAGE \
|
||||||
--verbose \
|
--verbose \
|
||||||
--no-fallback \
|
--no-fallback \
|
||||||
--no-server \
|
--no-server \
|
||||||
"-J-Xmx3g"
|
"$BABASHKA_XMX"
|
||||||
|
|
||||||
lein clean
|
lein clean
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,13 @@ set -eo pipefail
|
||||||
|
|
||||||
export BABASHKA_CLASSPATH=$(clojure -Sdeps '{:deps {clj-http-lite {:git/url "https://github.com/borkdude/clj-http-lite" :sha "f44ebe45446f0f44f2b73761d102af3da6d0a13e"}}}' -Spath)
|
export BABASHKA_CLASSPATH=$(clojure -Sdeps '{:deps {clj-http-lite {:git/url "https://github.com/borkdude/clj-http-lite" :sha "f44ebe45446f0f44f2b73761d102af3da6d0a13e"}}}' -Spath)
|
||||||
|
|
||||||
./bb -e "
|
if [ "$BABASHKA_TEST_ENV" = "native" ]; then
|
||||||
|
BB_CMD="./bb"
|
||||||
|
else
|
||||||
|
BB_CMD="lein bb"
|
||||||
|
fi
|
||||||
|
|
||||||
|
$BB_CMD -e "
|
||||||
(require '[clj-http.lite.client :as client])
|
(require '[clj-http.lite.client :as client])
|
||||||
|
|
||||||
(prn (:status (client/get \"https://www.clojure.org\")))
|
(prn (:status (client/get \"https://www.clojure.org\")))
|
||||||
|
|
@ -13,5 +19,15 @@ export BABASHKA_CLASSPATH=$(clojure -Sdeps '{:deps {clj-http-lite {:git/url "htt
|
||||||
|
|
||||||
(prn (:status (client/post \"https://postman-echo.com/post\")))
|
(prn (:status (client/post \"https://postman-echo.com/post\")))
|
||||||
|
|
||||||
(prn (:status (client/put \"https://postman-echo.com/put\")))
|
(prn (:status (client/post \"https://postman-echo.com/post\"
|
||||||
|
{:body (json/generate-string {:a 1})
|
||||||
|
:headers {\"X-Hasura-Role\" \"admin\"}
|
||||||
|
:content-type :json
|
||||||
|
:accept :json})))
|
||||||
|
|
||||||
|
(prn (:status (client/put \"https://postman-echo.com/put\"
|
||||||
|
{:body (json/generate-string {:a 1})
|
||||||
|
:headers {\"X-Hasura-Role\" \"admin\"}
|
||||||
|
:content-type :json
|
||||||
|
:accept :json})))
|
||||||
"
|
"
|
||||||
|
|
|
||||||
16
script/lib_tests/clojure_csv_test
Executable file
16
script/lib_tests/clojure_csv_test
Executable file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
export BABASHKA_CLASSPATH="$(clojure -Sdeps '{:deps {clojure-csv {:mvn/version "RELEASE"}}}' -Spath)"
|
||||||
|
|
||||||
|
if [ "$BABASHKA_TEST_ENV" = "native" ]; then
|
||||||
|
BB_CMD="./bb"
|
||||||
|
else
|
||||||
|
BB_CMD="lein bb"
|
||||||
|
fi
|
||||||
|
|
||||||
|
$BB_CMD -e "
|
||||||
|
(require '[clojure-csv.core :as csv])
|
||||||
|
(prn (csv/write-csv (csv/parse-csv \"a,b,c\n1,2,3\")))
|
||||||
|
"
|
||||||
|
|
@ -2,8 +2,14 @@
|
||||||
|
|
||||||
set -eo pipefail
|
set -eo pipefail
|
||||||
|
|
||||||
|
if [ "$BABASHKA_TEST_ENV" = "native" ]; then
|
||||||
|
BB_CMD="./bb"
|
||||||
|
else
|
||||||
|
BB_CMD="lein bb"
|
||||||
|
fi
|
||||||
|
|
||||||
curl -sL https://raw.githubusercontent.com/borkdude/deps.clj/master/deps.clj -o deps_test.clj
|
curl -sL https://raw.githubusercontent.com/borkdude/deps.clj/master/deps.clj -o deps_test.clj
|
||||||
chmod +x deps_test.clj
|
chmod +x deps_test.clj
|
||||||
./bb deps_test.clj -Sdescribe
|
$BB_CMD deps_test.clj -Sdescribe
|
||||||
rm deps_test.clj
|
rm deps_test.clj
|
||||||
|
|
||||||
|
|
|
||||||
17
script/lib_tests/medley_test
Executable file
17
script/lib_tests/medley_test
Executable file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
export BABASHKA_CLASSPATH="$(clojure -Sdeps '{:deps {medley {:git/url "https://github.com/weavejester/medley" :sha "a4e5fb5383f5c0d83cb2d005181a35b76d8a136d"}}}' -Spath)"
|
||||||
|
|
||||||
|
if [ "$BABASHKA_TEST_ENV" = "native" ]; then
|
||||||
|
BB_CMD="./bb"
|
||||||
|
else
|
||||||
|
BB_CMD="lein bb"
|
||||||
|
fi
|
||||||
|
|
||||||
|
$BB_CMD "
|
||||||
|
(require '[medley.core :refer [index-by random-uuid]])
|
||||||
|
(prn (index-by :id [{:id 1} {:id 2}]))
|
||||||
|
(prn (random-uuid))
|
||||||
|
"
|
||||||
13
script/lib_tests/regal_test
Executable file
13
script/lib_tests/regal_test
Executable file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
export BABASHKA_CLASSPATH="$(clojure -Sdeps '{:deps {regal {:git/url "https://github.com/lambdaisland/regal" :sha "8d300f8e15f43480801766b7762530b6d412c1e6"}}}' -Spath)"
|
||||||
|
|
||||||
|
if [ "$BABASHKA_TEST_ENV" = "native" ]; then
|
||||||
|
BB_CMD="./bb"
|
||||||
|
else
|
||||||
|
BB_CMD="lein bb"
|
||||||
|
fi
|
||||||
|
|
||||||
|
$BB_CMD "(require '[lambdaisland.regal :as re]) (re/regex [:range \a \z])"
|
||||||
|
|
@ -4,8 +4,14 @@ set -eo pipefail
|
||||||
|
|
||||||
export BABASHKA_CLASSPATH=$(clojure -Sdeps '{:deps {spartan.spec {:git/url "https://github.com/borkdude/spartan.spec" :sha "16f7eec4b6589c77c96c9fcf989f78fffcee7c4c"}}}' -Spath)
|
export BABASHKA_CLASSPATH=$(clojure -Sdeps '{:deps {spartan.spec {:git/url "https://github.com/borkdude/spartan.spec" :sha "16f7eec4b6589c77c96c9fcf989f78fffcee7c4c"}}}' -Spath)
|
||||||
|
|
||||||
./bb -e "
|
if [ "$BABASHKA_TEST_ENV" = "native" ]; then
|
||||||
(require '[spartan.spec :as s])
|
BB_CMD="./bb"
|
||||||
(s/explain (s/cat :i int? :s string?) [1 :foo])
|
else
|
||||||
(s/conform (s/cat :i int? :s string?) [1 \"foo\"])
|
BB_CMD="lein bb"
|
||||||
|
fi
|
||||||
|
|
||||||
|
$BB_CMD -e "
|
||||||
|
(time (require '[spartan.spec :as s]))
|
||||||
|
(time (s/explain (s/cat :i int? :s string?) [1 :foo]))
|
||||||
|
(time (s/conform (s/cat :i int? :s string?) [1 \"foo\"]))
|
||||||
"
|
"
|
||||||
|
|
|
||||||
|
|
@ -5,3 +5,6 @@ set -eo pipefail
|
||||||
script/lib_tests/clj_http_lite_test
|
script/lib_tests/clj_http_lite_test
|
||||||
script/lib_tests/deps_clj_test
|
script/lib_tests/deps_clj_test
|
||||||
script/lib_tests/spartan_spec_test
|
script/lib_tests/spartan_spec_test
|
||||||
|
script/lib_tests/clojure_csv_test
|
||||||
|
script/lib_tests/regal_test
|
||||||
|
script/lib_tests/medley_test
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
(ns babashka.impl.async
|
(ns babashka.impl.async
|
||||||
{:no-doc true}
|
{:no-doc true}
|
||||||
(:require [clojure.core.async :as async]))
|
(:require [clojure.core.async :as async]
|
||||||
|
[clojure.core.async.impl.protocols :as protocols]))
|
||||||
|
|
||||||
(defn thread
|
(defn thread
|
||||||
[_ _ & body]
|
[_ _ & body]
|
||||||
|
|
@ -66,3 +67,5 @@
|
||||||
'untap async/untap
|
'untap async/untap
|
||||||
'untap-all async/untap-all})
|
'untap-all async/untap-all})
|
||||||
|
|
||||||
|
(def async-protocols-namespace
|
||||||
|
{'ReadPort protocols/ReadPort})
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,10 @@
|
||||||
java.io.InputStream
|
java.io.InputStream
|
||||||
java.io.IOException
|
java.io.IOException
|
||||||
java.io.OutputStream
|
java.io.OutputStream
|
||||||
|
java.io.Reader
|
||||||
java.io.StringReader
|
java.io.StringReader
|
||||||
java.io.StringWriter
|
java.io.StringWriter
|
||||||
|
java.io.Writer
|
||||||
java.lang.ArithmeticException
|
java.lang.ArithmeticException
|
||||||
java.lang.AssertionError
|
java.lang.AssertionError
|
||||||
java.lang.Boolean
|
java.lang.Boolean
|
||||||
|
|
@ -30,15 +32,18 @@
|
||||||
java.lang.Double
|
java.lang.Double
|
||||||
java.lang.Exception
|
java.lang.Exception
|
||||||
java.lang.Integer
|
java.lang.Integer
|
||||||
|
java.lang.Long
|
||||||
java.lang.Math
|
java.lang.Math
|
||||||
java.util.concurrent.LinkedBlockingQueue
|
java.util.concurrent.LinkedBlockingQueue
|
||||||
java.lang.Object
|
java.lang.Object
|
||||||
java.lang.String
|
java.lang.String
|
||||||
|
java.lang.StringBuilder
|
||||||
java.lang.System
|
java.lang.System
|
||||||
java.lang.Throwable
|
java.lang.Throwable
|
||||||
java.lang.Process
|
java.lang.Process
|
||||||
java.lang.ProcessBuilder
|
java.lang.ProcessBuilder
|
||||||
java.lang.ProcessBuilder$Redirect
|
java.lang.ProcessBuilder$Redirect
|
||||||
|
java.math.BigInteger
|
||||||
java.net.URI
|
java.net.URI
|
||||||
java.net.HttpURLConnection
|
java.net.HttpURLConnection
|
||||||
java.net.ServerSocket
|
java.net.ServerSocket
|
||||||
|
|
@ -58,6 +63,7 @@
|
||||||
java.nio.file.attribute.FileTime
|
java.nio.file.attribute.FileTime
|
||||||
java.nio.file.attribute.PosixFilePermission
|
java.nio.file.attribute.PosixFilePermission
|
||||||
java.nio.file.attribute.PosixFilePermissions
|
java.nio.file.attribute.PosixFilePermissions
|
||||||
|
java.security.MessageDigest
|
||||||
java.time.format.DateTimeFormatter
|
java.time.format.DateTimeFormatter
|
||||||
java.time.Clock
|
java.time.Clock
|
||||||
java.time.DateTimeException
|
java.time.DateTimeException
|
||||||
|
|
@ -91,7 +97,10 @@
|
||||||
java.util.zip.GZIPOutputStream]
|
java.util.zip.GZIPOutputStream]
|
||||||
:constructors [clojure.lang.Delay
|
:constructors [clojure.lang.Delay
|
||||||
clojure.lang.MapEntry
|
clojure.lang.MapEntry
|
||||||
clojure.lang.LineNumberingPushbackReader]
|
clojure.lang.LineNumberingPushbackReader
|
||||||
|
java.io.EOFException
|
||||||
|
java.io.PrintWriter
|
||||||
|
java.io.PushbackReader]
|
||||||
:methods [borkdude.graal.LockFix ;; support for locking
|
:methods [borkdude.graal.LockFix ;; support for locking
|
||||||
]
|
]
|
||||||
:fields [clojure.lang.PersistentQueue]
|
:fields [clojure.lang.PersistentQueue]
|
||||||
|
|
@ -183,7 +192,12 @@
|
||||||
(cond (instance? java.nio.file.Path v)
|
(cond (instance? java.nio.file.Path v)
|
||||||
java.nio.file.Path
|
java.nio.file.Path
|
||||||
(instance? java.lang.Process v)
|
(instance? java.lang.Process v)
|
||||||
java.lang.Process)))))
|
java.lang.Process
|
||||||
|
;; added for issue #239 regarding clj-http-lite
|
||||||
|
(instance? java.io.ByteArrayOutputStream v)
|
||||||
|
java.io.ByteArrayOutputStream
|
||||||
|
(instance? java.security.MessageDigest v)
|
||||||
|
java.security.MessageDigest)))))
|
||||||
|
|
||||||
(def class-map (gen-class-map))
|
(def class-map (gen-class-map))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,15 @@
|
||||||
(defn locking* [form bindings v f & args]
|
(defn locking* [form bindings v f & args]
|
||||||
(apply @#'locking/locking form bindings v f args))
|
(apply @#'locking/locking form bindings v f args))
|
||||||
|
|
||||||
|
(defn time*
|
||||||
|
"Evaluates expr and prints the time it took. Returns the value of
|
||||||
|
expr."
|
||||||
|
[_ _ expr]
|
||||||
|
`(let [start# (. System (nanoTime))
|
||||||
|
ret# ~expr]
|
||||||
|
(prn (str "Elapsed time: " (/ (double (- (. System (nanoTime)) start#)) 1000000.0) " msecs"))
|
||||||
|
ret#))
|
||||||
|
|
||||||
(def core-extras
|
(def core-extras
|
||||||
{'file-seq file-seq
|
{'file-seq file-seq
|
||||||
'agent agent
|
'agent agent
|
||||||
|
|
@ -18,5 +27,6 @@
|
||||||
'shutdown-agents shutdown-agents
|
'shutdown-agents shutdown-agents
|
||||||
'slurp slurp
|
'slurp slurp
|
||||||
'spit spit
|
'spit spit
|
||||||
|
'time (with-meta time* {:sci/macro true})
|
||||||
'Throwable->map Throwable->map
|
'Throwable->map Throwable->map
|
||||||
'compare-and-set! compare-and-set!})
|
'compare-and-set! compare-and-set!})
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@
|
||||||
:no-doc true}
|
:no-doc true}
|
||||||
babashka.impl.clojure.core.server
|
babashka.impl.clojure.core.server
|
||||||
(:refer-clojure :exclude [locking])
|
(:refer-clojure :exclude [locking])
|
||||||
(:require [sci.core :as sci])
|
(:require [sci.core :as sci]
|
||||||
|
[sci.impl.vars :as vars])
|
||||||
(:import
|
(:import
|
||||||
[clojure.lang LineNumberingPushbackReader]
|
[clojure.lang LineNumberingPushbackReader]
|
||||||
[java.net InetAddress Socket ServerSocket SocketException]
|
[java.net InetAddress Socket ServerSocket SocketException]
|
||||||
|
|
@ -44,7 +45,8 @@
|
||||||
(try
|
(try
|
||||||
(sci/with-bindings {sci/in in
|
(sci/with-bindings {sci/in in
|
||||||
sci/out out
|
sci/out out
|
||||||
sci/err err}
|
sci/err err
|
||||||
|
vars/current-ns (vars/->SciNamespace 'user nil)}
|
||||||
(swap! server assoc-in [:sessions client-id] {})
|
(swap! server assoc-in [:sessions client-id] {})
|
||||||
(apply accept args))
|
(apply accept args))
|
||||||
(catch SocketException _disconnect)
|
(catch SocketException _disconnect)
|
||||||
|
|
|
||||||
787
src/babashka/impl/clojure/test.clj
Normal file
787
src/babashka/impl/clojure/test.clj
Normal file
|
|
@ -0,0 +1,787 @@
|
||||||
|
; Copyright (c) Rich Hickey. All rights reserved.
|
||||||
|
; The use and distribution terms for this software are covered by the
|
||||||
|
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
||||||
|
; which can be found in the file epl-v10.html at the root of this distribution.
|
||||||
|
; By using this software in any fashion, you are agreeing to be bound by
|
||||||
|
; the terms of this license.
|
||||||
|
; You must not remove this notice, or any other, from this software.
|
||||||
|
|
||||||
|
;;; test.clj: test framework for Clojure
|
||||||
|
|
||||||
|
;; by Stuart Sierra
|
||||||
|
;; March 28, 2009
|
||||||
|
|
||||||
|
;; Thanks to Chas Emerick, Allen Rohner, and Stuart Halloway for
|
||||||
|
;; contributions and suggestions.
|
||||||
|
|
||||||
|
(ns
|
||||||
|
^{:author "Stuart Sierra, with contributions and suggestions by
|
||||||
|
Chas Emerick, Allen Rohner, and Stuart Halloway",
|
||||||
|
:doc "A unit testing framework.
|
||||||
|
|
||||||
|
ASSERTIONS
|
||||||
|
|
||||||
|
The core of the library is the \"is\" macro, which lets you make
|
||||||
|
assertions of any arbitrary expression:
|
||||||
|
|
||||||
|
(is (= 4 (+ 2 2)))
|
||||||
|
(is (instance? Integer 256))
|
||||||
|
(is (.startsWith \"abcde\" \"ab\"))
|
||||||
|
|
||||||
|
You can type an \"is\" expression directly at the REPL, which will
|
||||||
|
print a message if it fails.
|
||||||
|
|
||||||
|
user> (is (= 5 (+ 2 2)))
|
||||||
|
|
||||||
|
FAIL in (:1)
|
||||||
|
expected: (= 5 (+ 2 2))
|
||||||
|
actual: (not (= 5 4))
|
||||||
|
false
|
||||||
|
|
||||||
|
The \"expected:\" line shows you the original expression, and the
|
||||||
|
\"actual:\" shows you what actually happened. In this case, it
|
||||||
|
shows that (+ 2 2) returned 4, which is not = to 5. Finally, the
|
||||||
|
\"false\" on the last line is the value returned from the
|
||||||
|
expression. The \"is\" macro always returns the result of the
|
||||||
|
inner expression.
|
||||||
|
|
||||||
|
There are two special assertions for testing exceptions. The
|
||||||
|
\"(is (thrown? c ...))\" form tests if an exception of class c is
|
||||||
|
thrown:
|
||||||
|
|
||||||
|
(is (thrown? ArithmeticException (/ 1 0)))
|
||||||
|
|
||||||
|
\"(is (thrown-with-msg? c re ...))\" does the same thing and also
|
||||||
|
tests that the message on the exception matches the regular
|
||||||
|
expression re:
|
||||||
|
|
||||||
|
(is (thrown-with-msg? ArithmeticException #\"Divide by zero\"
|
||||||
|
(/ 1 0)))
|
||||||
|
|
||||||
|
DOCUMENTING TESTS
|
||||||
|
|
||||||
|
\"is\" takes an optional second argument, a string describing the
|
||||||
|
assertion. This message will be included in the error report.
|
||||||
|
|
||||||
|
(is (= 5 (+ 2 2)) \"Crazy arithmetic\")
|
||||||
|
|
||||||
|
In addition, you can document groups of assertions with the
|
||||||
|
\"testing\" macro, which takes a string followed by any number of
|
||||||
|
assertions. The string will be included in failure reports.
|
||||||
|
Calls to \"testing\" may be nested, and all of the strings will be
|
||||||
|
joined together with spaces in the final report, in a style
|
||||||
|
similar to RSpec <http://rspec.info/>
|
||||||
|
|
||||||
|
(testing \"Arithmetic\"
|
||||||
|
(testing \"with positive integers\"
|
||||||
|
(is (= 4 (+ 2 2)))
|
||||||
|
(is (= 7 (+ 3 4))))
|
||||||
|
(testing \"with negative integers\"
|
||||||
|
(is (= -4 (+ -2 -2)))
|
||||||
|
(is (= -1 (+ 3 -4)))))
|
||||||
|
|
||||||
|
Note that, unlike RSpec, the \"testing\" macro may only be used
|
||||||
|
INSIDE a \"deftest\" or \"with-test\" form (see below).
|
||||||
|
|
||||||
|
|
||||||
|
DEFINING TESTS
|
||||||
|
|
||||||
|
There are two ways to define tests. The \"with-test\" macro takes
|
||||||
|
a defn or def form as its first argument, followed by any number
|
||||||
|
of assertions. The tests will be stored as metadata on the
|
||||||
|
definition.
|
||||||
|
|
||||||
|
(with-test
|
||||||
|
(defn my-function [x y]
|
||||||
|
(+ x y))
|
||||||
|
(is (= 4 (my-function 2 2)))
|
||||||
|
(is (= 7 (my-function 3 4))))
|
||||||
|
|
||||||
|
As of Clojure SVN rev. 1221, this does not work with defmacro.
|
||||||
|
See http://code.google.com/p/clojure/issues/detail?id=51
|
||||||
|
|
||||||
|
The other way lets you define tests separately from the rest of
|
||||||
|
your code, even in a different namespace:
|
||||||
|
|
||||||
|
(deftest addition
|
||||||
|
(is (= 4 (+ 2 2)))
|
||||||
|
(is (= 7 (+ 3 4))))
|
||||||
|
|
||||||
|
(deftest subtraction
|
||||||
|
(is (= 1 (- 4 3)))
|
||||||
|
(is (= 3 (- 7 4))))
|
||||||
|
|
||||||
|
This creates functions named \"addition\" and \"subtraction\", which
|
||||||
|
can be called like any other function. Therefore, tests can be
|
||||||
|
grouped and composed, in a style similar to the test framework in
|
||||||
|
Peter Seibel's \"Practical Common Lisp\"
|
||||||
|
<http://www.gigamonkeys.com/book/practical-building-a-unit-test-framework.html>
|
||||||
|
|
||||||
|
(deftest arithmetic
|
||||||
|
(addition)
|
||||||
|
(subtraction))
|
||||||
|
|
||||||
|
The names of the nested tests will be joined in a list, like
|
||||||
|
\"(arithmetic addition)\", in failure reports. You can use nested
|
||||||
|
tests to set up a context shared by several tests.
|
||||||
|
|
||||||
|
|
||||||
|
RUNNING TESTS
|
||||||
|
|
||||||
|
Run tests with the function \"(run-tests namespaces...)\":
|
||||||
|
|
||||||
|
(run-tests 'your.namespace 'some.other.namespace)
|
||||||
|
|
||||||
|
If you don't specify any namespaces, the current namespace is
|
||||||
|
used. To run all tests in all namespaces, use \"(run-all-tests)\".
|
||||||
|
|
||||||
|
By default, these functions will search for all tests defined in
|
||||||
|
a namespace and run them in an undefined order. However, if you
|
||||||
|
are composing tests, as in the \"arithmetic\" example above, you
|
||||||
|
probably do not want the \"addition\" and \"subtraction\" tests run
|
||||||
|
separately. In that case, you must define a special function
|
||||||
|
named \"test-ns-hook\" that runs your tests in the correct order:
|
||||||
|
|
||||||
|
(defn test-ns-hook []
|
||||||
|
(arithmetic))
|
||||||
|
|
||||||
|
Note: test-ns-hook prevents execution of fixtures (see below).
|
||||||
|
|
||||||
|
|
||||||
|
OMITTING TESTS FROM PRODUCTION CODE
|
||||||
|
|
||||||
|
You can bind the variable \"*load-tests*\" to false when loading or
|
||||||
|
compiling code in production. This will prevent any tests from
|
||||||
|
being created by \"with-test\" or \"deftest\".
|
||||||
|
|
||||||
|
|
||||||
|
FIXTURES
|
||||||
|
|
||||||
|
Fixtures allow you to run code before and after tests, to set up
|
||||||
|
the context in which tests should be run.
|
||||||
|
|
||||||
|
A fixture is just a function that calls another function passed as
|
||||||
|
an argument. It looks like this:
|
||||||
|
|
||||||
|
(defn my-fixture [f]
|
||||||
|
Perform setup, establish bindings, whatever.
|
||||||
|
(f) Then call the function we were passed.
|
||||||
|
Tear-down / clean-up code here.
|
||||||
|
)
|
||||||
|
|
||||||
|
Fixtures are attached to namespaces in one of two ways. \"each\"
|
||||||
|
fixtures are run repeatedly, once for each test function created
|
||||||
|
with \"deftest\" or \"with-test\". \"each\" fixtures are useful for
|
||||||
|
establishing a consistent before/after state for each test, like
|
||||||
|
clearing out database tables.
|
||||||
|
|
||||||
|
\"each\" fixtures can be attached to the current namespace like this:
|
||||||
|
(use-fixtures :each fixture1 fixture2 ...)
|
||||||
|
The fixture1, fixture2 are just functions like the example above.
|
||||||
|
They can also be anonymous functions, like this:
|
||||||
|
(use-fixtures :each (fn [f] setup... (f) cleanup...))
|
||||||
|
|
||||||
|
The other kind of fixture, a \"once\" fixture, is only run once,
|
||||||
|
around ALL the tests in the namespace. \"once\" fixtures are useful
|
||||||
|
for tasks that only need to be performed once, like establishing
|
||||||
|
database connections, or for time-consuming tasks.
|
||||||
|
|
||||||
|
Attach \"once\" fixtures to the current namespace like this:
|
||||||
|
(use-fixtures :once fixture1 fixture2 ...)
|
||||||
|
|
||||||
|
Note: Fixtures and test-ns-hook are mutually incompatible. If you
|
||||||
|
are using test-ns-hook, fixture functions will *never* be run.
|
||||||
|
|
||||||
|
|
||||||
|
SAVING TEST OUTPUT TO A FILE
|
||||||
|
|
||||||
|
All the test reporting functions write to the var *test-out*. By
|
||||||
|
default, this is the same as *out*, but you can rebind it to any
|
||||||
|
PrintWriter. For example, it could be a file opened with
|
||||||
|
clojure.java.io/writer.
|
||||||
|
|
||||||
|
|
||||||
|
EXTENDING TEST-IS (ADVANCED)
|
||||||
|
|
||||||
|
You can extend the behavior of the \"is\" macro by defining new
|
||||||
|
methods for the \"assert-expr\" multimethod. These methods are
|
||||||
|
called during expansion of the \"is\" macro, so they should return
|
||||||
|
quoted forms to be evaluated.
|
||||||
|
|
||||||
|
You can plug in your own test-reporting framework by rebinding
|
||||||
|
the \"report\" function: (report event)
|
||||||
|
|
||||||
|
The 'event' argument is a map. It will always have a :type key,
|
||||||
|
whose value will be a keyword signaling the type of event being
|
||||||
|
reported. Standard events with :type value of :pass, :fail, and
|
||||||
|
:error are called when an assertion passes, fails, and throws an
|
||||||
|
exception, respectively. In that case, the event will also have
|
||||||
|
the following keys:
|
||||||
|
|
||||||
|
:expected The form that was expected to be true
|
||||||
|
:actual A form representing what actually occurred
|
||||||
|
:message The string message given as an argument to 'is'
|
||||||
|
|
||||||
|
The \"testing\" strings will be a list in \"*testing-contexts*\", and
|
||||||
|
the vars being tested will be a list in \"*testing-vars*\".
|
||||||
|
|
||||||
|
Your \"report\" function should wrap any printing calls in the
|
||||||
|
\"with-test-out\" macro, which rebinds *out* to the current value
|
||||||
|
of *test-out*.
|
||||||
|
|
||||||
|
For additional event types, see the examples in the code.
|
||||||
|
"}
|
||||||
|
babashka.impl.clojure.test
|
||||||
|
(:require [babashka.impl.clojure.stacktrace :as stack]
|
||||||
|
[babashka.impl.common :refer [ctx]]
|
||||||
|
[clojure.string :as str]
|
||||||
|
[clojure.template :as temp]
|
||||||
|
[sci.core :as sci]
|
||||||
|
[sci.impl.analyzer :as ana]
|
||||||
|
[sci.impl.namespaces :as sci-namespaces]
|
||||||
|
[sci.impl.vars :as vars]))
|
||||||
|
|
||||||
|
;; Nothing is marked "private" here, so you can rebind things to plug
|
||||||
|
;; in your own testing or reporting frameworks.
|
||||||
|
|
||||||
|
|
||||||
|
;;; USER-MODIFIABLE GLOBALS
|
||||||
|
|
||||||
|
(defonce
|
||||||
|
^{:doc "True by default. If set to false, no test functions will
|
||||||
|
be created by deftest, set-test, or with-test. Use this to omit
|
||||||
|
tests when compiling or loading production code."}
|
||||||
|
load-tests
|
||||||
|
(sci/new-dynamic-var '*load-tests* true))
|
||||||
|
|
||||||
|
(def
|
||||||
|
^{:doc "The maximum depth of stack traces to print when an Exception
|
||||||
|
is thrown during a test. Defaults to nil, which means print the
|
||||||
|
complete stack trace."}
|
||||||
|
stack-trace-depth
|
||||||
|
(sci/new-dynamic-var '*stack-trace-depth* nil))
|
||||||
|
|
||||||
|
|
||||||
|
;;; GLOBALS USED BY THE REPORTING FUNCTIONS
|
||||||
|
|
||||||
|
(def report-counters (sci/new-dynamic-var '*report-counters* nil)) ; bound to a ref of a map in test-ns
|
||||||
|
|
||||||
|
(def initial-report-counters ; used to initialize *report-counters*
|
||||||
|
(sci/new-dynamic-var '*initial-report-counters* {:test 0, :pass 0, :fail 0, :error 0}))
|
||||||
|
|
||||||
|
(def testing-vars (sci/new-dynamic-var '*testing-vars* (list))) ; bound to hierarchy of vars being tested
|
||||||
|
|
||||||
|
(def testing-contexts (sci/new-dynamic-var '*testing-contexts* (list))) ; bound to hierarchy of "testing" strings
|
||||||
|
|
||||||
|
(def test-out (sci/new-dynamic-var '*test-out* sci/out)) ; PrintWriter for test reporting output
|
||||||
|
|
||||||
|
(defmacro with-test-out-internal
|
||||||
|
"Runs body with *out* bound to the value of *test-out*."
|
||||||
|
{:added "1.1"}
|
||||||
|
[& body]
|
||||||
|
`(sci/binding [sci/out @test-out]
|
||||||
|
~@body))
|
||||||
|
|
||||||
|
;;; UTILITIES FOR REPORTING FUNCTIONS
|
||||||
|
|
||||||
|
(defn file-position
|
||||||
|
"Returns a vector [filename line-number] for the nth call up the
|
||||||
|
stack.
|
||||||
|
|
||||||
|
Deprecated in 1.2: The information needed for test reporting is
|
||||||
|
now on :file and :line keys in the result map."
|
||||||
|
{:added "1.1"
|
||||||
|
:deprecated "1.2"}
|
||||||
|
[n]
|
||||||
|
(let [^StackTraceElement s (nth (.getStackTrace (new java.lang.Throwable)) n)]
|
||||||
|
[(.getFileName s) (.getLineNumber s)]))
|
||||||
|
|
||||||
|
(defn testing-vars-str
|
||||||
|
"Returns a string representation of the current test. Renders names
|
||||||
|
in *testing-vars* as a list, then the source file and line of
|
||||||
|
current assertion."
|
||||||
|
{:added "1.1"}
|
||||||
|
[m]
|
||||||
|
(let [{:keys [:file :line]} (meta (first @testing-vars))]
|
||||||
|
(str
|
||||||
|
;; Uncomment to include namespace in failure report:
|
||||||
|
;;(ns-name (:ns (meta (first *testing-vars*)))) "/ "
|
||||||
|
(reverse (map #(:name (meta %)) @testing-vars))
|
||||||
|
" (" file ":" line ")")))
|
||||||
|
|
||||||
|
(defn testing-contexts-str
|
||||||
|
"Returns a string representation of the current test context. Joins
|
||||||
|
strings in *testing-contexts* with spaces."
|
||||||
|
{:added "1.1"}
|
||||||
|
[]
|
||||||
|
(apply str (interpose " " (reverse @testing-contexts))))
|
||||||
|
|
||||||
|
(defn inc-report-counter
|
||||||
|
"Increments the named counter in *report-counters*, a ref to a map.
|
||||||
|
Does nothing if *report-counters* is nil."
|
||||||
|
{:added "1.1"}
|
||||||
|
[name]
|
||||||
|
(when @report-counters
|
||||||
|
(swap! @report-counters update-in [name] (fnil inc 0))))
|
||||||
|
|
||||||
|
;;; TEST RESULT REPORTING
|
||||||
|
|
||||||
|
(defmulti
|
||||||
|
^{:doc "Generic reporting function, may be overridden to plug in
|
||||||
|
different report formats (e.g., TAP, JUnit). Assertions such as
|
||||||
|
'is' call 'report' to indicate results. The argument given to
|
||||||
|
'report' will be a map with a :type key. See the documentation at
|
||||||
|
the top of test_is.clj for more information on the types of
|
||||||
|
arguments for 'report'."
|
||||||
|
:dynamic true
|
||||||
|
:added "1.1"}
|
||||||
|
report :type)
|
||||||
|
|
||||||
|
(defn- stacktrace-file-and-line
|
||||||
|
[stacktrace]
|
||||||
|
(if (seq stacktrace)
|
||||||
|
(let [^StackTraceElement s (first stacktrace)]
|
||||||
|
{:file (.getFileName s) :line (.getLineNumber s)})
|
||||||
|
{:file nil :line nil}))
|
||||||
|
|
||||||
|
(defn do-report
|
||||||
|
"Add file and line information to a test result and call report.
|
||||||
|
If you are writing a custom assert-expr method, call this function
|
||||||
|
to pass test results to report."
|
||||||
|
{:added "1.2"}
|
||||||
|
[m]
|
||||||
|
(report
|
||||||
|
(case
|
||||||
|
(:type m)
|
||||||
|
:fail m
|
||||||
|
:error (merge (stacktrace-file-and-line (.getStackTrace ^Throwable (:actual m))) m)
|
||||||
|
m)))
|
||||||
|
|
||||||
|
(defmethod report :default [m]
|
||||||
|
(with-test-out-internal (prn m)))
|
||||||
|
|
||||||
|
(defmethod report :pass [m]
|
||||||
|
(with-test-out-internal (inc-report-counter :pass)))
|
||||||
|
|
||||||
|
(defmethod report :fail [m]
|
||||||
|
(with-test-out-internal
|
||||||
|
(inc-report-counter :fail)
|
||||||
|
(println "\nFAIL in" (testing-vars-str m))
|
||||||
|
(when (seq @testing-contexts) (println (testing-contexts-str)))
|
||||||
|
(when-let [message (:message m)] (println message))
|
||||||
|
(println "expected:" (pr-str (:expected m)))
|
||||||
|
(println " actual:" (pr-str (:actual m)))))
|
||||||
|
|
||||||
|
(defmethod report :error [m]
|
||||||
|
(with-test-out-internal
|
||||||
|
(inc-report-counter :error)
|
||||||
|
(println "\nERROR in" (testing-vars-str m))
|
||||||
|
(when (seq @testing-contexts) (println (testing-contexts-str)))
|
||||||
|
(when-let [message (:message m)] (println message))
|
||||||
|
(println "expected:" (pr-str (:expected m)))
|
||||||
|
(print " actual: ")
|
||||||
|
(let [actual (:actual m)]
|
||||||
|
(if (instance? Throwable actual)
|
||||||
|
(stack/print-cause-trace actual @stack-trace-depth)
|
||||||
|
(prn actual)))))
|
||||||
|
|
||||||
|
(defmethod report :summary [m]
|
||||||
|
(with-test-out-internal
|
||||||
|
(println "\nRan" (:test m) "tests containing"
|
||||||
|
(+ (:pass m) (:fail m) (:error m)) "assertions.")
|
||||||
|
(println (:fail m) "failures," (:error m) "errors.")))
|
||||||
|
|
||||||
|
(defmethod report :begin-test-ns [m]
|
||||||
|
(with-test-out-internal
|
||||||
|
(println "\nTesting" (sci-namespaces/sci-ns-name (:ns m)))))
|
||||||
|
|
||||||
|
;; Ignore these message types:
|
||||||
|
(defmethod report :end-test-ns [m])
|
||||||
|
(defmethod report :begin-test-var [m])
|
||||||
|
(defmethod report :end-test-var [m])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;; UTILITIES FOR ASSERTIONS
|
||||||
|
|
||||||
|
(defn function?
|
||||||
|
"Returns true if argument is a function or a symbol that resolves to
|
||||||
|
a function (not a macro)."
|
||||||
|
{:added "1.1"}
|
||||||
|
[x]
|
||||||
|
(if (symbol? x) ;; TODO
|
||||||
|
(when-let [v (second (ana/lookup @ctx x false))]
|
||||||
|
(when-let [value (if (vars/var? v) @v v)]
|
||||||
|
(and (fn? value)
|
||||||
|
(not (:sci/macro (meta v))))))
|
||||||
|
(fn? x)))
|
||||||
|
|
||||||
|
(defn assert-predicate
|
||||||
|
"Returns generic assertion code for any functional predicate. The
|
||||||
|
'expected' argument to 'report' will contains the original form, the
|
||||||
|
'actual' argument will contain the form with all its sub-forms
|
||||||
|
evaluated. If the predicate returns false, the 'actual' form will
|
||||||
|
be wrapped in (not...)."
|
||||||
|
{:added "1.1"}
|
||||||
|
[msg form]
|
||||||
|
(let [args (rest form)
|
||||||
|
pred (first form)]
|
||||||
|
`(let [values# (list ~@args)
|
||||||
|
result# (apply ~pred values#)]
|
||||||
|
(if result#
|
||||||
|
(clojure.test/do-report {:type :pass, :message ~msg,
|
||||||
|
:expected '~form, :actual (cons ~pred values#)})
|
||||||
|
(clojure.test/do-report {:type :fail, :message ~msg,
|
||||||
|
:expected '~form, :actual (list '~'not (cons '~pred values#))}))
|
||||||
|
result#)))
|
||||||
|
|
||||||
|
(defn assert-any
|
||||||
|
"Returns generic assertion code for any test, including macros, Java
|
||||||
|
method calls, or isolated symbols."
|
||||||
|
{:added "1.1"}
|
||||||
|
[msg form]
|
||||||
|
`(let [value# ~form]
|
||||||
|
(if value#
|
||||||
|
(clojure.test/do-report {:type :pass, :message ~msg,
|
||||||
|
:expected '~form, :actual value#})
|
||||||
|
(clojure.test/do-report {:type :fail, :message ~msg,
|
||||||
|
:expected '~form, :actual value#}))
|
||||||
|
value#))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;; ASSERTION METHODS
|
||||||
|
|
||||||
|
;; You don't call these, but you can add methods to extend the 'is'
|
||||||
|
;; macro. These define different kinds of tests, based on the first
|
||||||
|
;; symbol in the test expression.
|
||||||
|
|
||||||
|
(defmulti assert-expr
|
||||||
|
(fn [msg form]
|
||||||
|
(cond
|
||||||
|
(nil? form) :always-fail
|
||||||
|
(seq? form) (first form)
|
||||||
|
:else :default)))
|
||||||
|
|
||||||
|
(defmethod assert-expr :always-fail [msg form]
|
||||||
|
;; nil test: always fail
|
||||||
|
`(clojure.test/do-report {:type :fail, :message ~msg}))
|
||||||
|
|
||||||
|
(defmethod assert-expr :default [msg form]
|
||||||
|
(if (and (sequential? form) (function? (first form)))
|
||||||
|
(assert-predicate msg form)
|
||||||
|
(assert-any msg form)))
|
||||||
|
|
||||||
|
(defmethod assert-expr 'instance? [msg form]
|
||||||
|
;; Test if x is an instance of y.
|
||||||
|
`(let [klass# ~(nth form 1)
|
||||||
|
object# ~(nth form 2)]
|
||||||
|
(let [result# (instance? klass# object#)]
|
||||||
|
(if result#
|
||||||
|
(clojure.test/do-report {:type :pass, :message ~msg,
|
||||||
|
:expected '~form, :actual (class object#)})
|
||||||
|
(clojure.test/do-report {:type :fail, :message ~msg,
|
||||||
|
:expected '~form, :actual (class object#)}))
|
||||||
|
result#)))
|
||||||
|
|
||||||
|
(defmethod assert-expr 'thrown? [msg form]
|
||||||
|
;; (is (thrown? c expr))
|
||||||
|
;; Asserts that evaluating expr throws an exception of class c.
|
||||||
|
;; Returns the exception thrown.
|
||||||
|
(let [klass (second form)
|
||||||
|
body (nthnext form 2)]
|
||||||
|
`(try ~@body
|
||||||
|
(clojure.test/do-report {:type :fail, :message ~msg,
|
||||||
|
:expected '~form, :actual nil})
|
||||||
|
(catch ~klass e#
|
||||||
|
(clojure.test/do-report {:type :pass, :message ~msg,
|
||||||
|
:expected '~form, :actual e#})
|
||||||
|
e#))))
|
||||||
|
|
||||||
|
(defmethod assert-expr 'thrown-with-msg? [msg form]
|
||||||
|
;; (is (thrown-with-msg? c re expr))
|
||||||
|
;; Asserts that evaluating expr throws an exception of class c.
|
||||||
|
;; Also asserts that the message string of the exception matches
|
||||||
|
;; (with re-find) the regular expression re.
|
||||||
|
(let [klass (nth form 1)
|
||||||
|
re (nth form 2)
|
||||||
|
body (nthnext form 3)]
|
||||||
|
`(try ~@body
|
||||||
|
(clojure.test/do-report {:type :fail, :message ~msg, :expected '~form, :actual nil})
|
||||||
|
(catch ~klass e#
|
||||||
|
(let [m# (.getMessage e#)]
|
||||||
|
(if (re-find ~re m#)
|
||||||
|
(clojure.test/do-report {:type :pass, :message ~msg,
|
||||||
|
:expected '~form, :actual e#})
|
||||||
|
(clojure.test/do-report {:type :fail, :message ~msg,
|
||||||
|
:expected '~form, :actual e#})))
|
||||||
|
e#))))
|
||||||
|
|
||||||
|
|
||||||
|
(defmacro try-expr
|
||||||
|
"Used by the 'is' macro to catch unexpected exceptions.
|
||||||
|
You don't call this."
|
||||||
|
{:added "1.1"}
|
||||||
|
[msg form]
|
||||||
|
`(try ~(assert-expr msg form)
|
||||||
|
(catch Throwable t#
|
||||||
|
(clojure.test/do-report {:type :error, :message ~msg,
|
||||||
|
:expected '~form, :actual t#}))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;; ASSERTION MACROS
|
||||||
|
|
||||||
|
;; You use these in your tests.
|
||||||
|
|
||||||
|
(defmacro is
|
||||||
|
"Generic assertion macro. 'form' is any predicate test.
|
||||||
|
'msg' is an optional message to attach to the assertion.
|
||||||
|
|
||||||
|
Example: (is (= 4 (+ 2 2)) \"Two plus two should be 4\")
|
||||||
|
|
||||||
|
Special forms:
|
||||||
|
|
||||||
|
(is (thrown? c body)) checks that an instance of c is thrown from
|
||||||
|
body, fails if not; then returns the thing thrown.
|
||||||
|
|
||||||
|
(is (thrown-with-msg? c re body)) checks that an instance of c is
|
||||||
|
thrown AND that the message on the exception matches (with
|
||||||
|
re-find) the regular expression re."
|
||||||
|
{:added "1.1"}
|
||||||
|
([form]
|
||||||
|
`(clojure.test/is ~form nil))
|
||||||
|
([form msg] `(clojure.test/try-expr ~msg ~form)))
|
||||||
|
|
||||||
|
(defmacro are
|
||||||
|
"Checks multiple assertions with a template expression.
|
||||||
|
See clojure.template/do-template for an explanation of
|
||||||
|
templates.
|
||||||
|
|
||||||
|
Example: (are [x y] (= x y)
|
||||||
|
2 (+ 1 1)
|
||||||
|
4 (* 2 2))
|
||||||
|
Expands to:
|
||||||
|
(do (is (= 2 (+ 1 1)))
|
||||||
|
(is (= 4 (* 2 2))))
|
||||||
|
|
||||||
|
Note: This breaks some reporting features, such as line numbers."
|
||||||
|
{:added "1.1"}
|
||||||
|
[argv expr & args]
|
||||||
|
(if (or
|
||||||
|
;; (are [] true) is meaningless but ok
|
||||||
|
(and (empty? argv) (empty? args))
|
||||||
|
;; Catch wrong number of args
|
||||||
|
(and (pos? (count argv))
|
||||||
|
(pos? (count args))
|
||||||
|
(zero? (mod (count args) (count argv)))))
|
||||||
|
`(temp/do-template ~argv (clojure.test/is ~expr) ~@args)
|
||||||
|
(throw (IllegalArgumentException. "The number of args doesn't match are's argv."))))
|
||||||
|
|
||||||
|
(defmacro testing
|
||||||
|
"Adds a new string to the list of testing contexts. May be nested,
|
||||||
|
but must occur inside a test function (deftest)."
|
||||||
|
{:added "1.1"}
|
||||||
|
[string & body]
|
||||||
|
`(binding [clojure.test/*testing-contexts* (conj clojure.test/*testing-contexts* ~string)]
|
||||||
|
~@body))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;; DEFINING TESTS
|
||||||
|
|
||||||
|
(defmacro with-test
|
||||||
|
"Takes any definition form (that returns a Var) as the first argument.
|
||||||
|
Remaining body goes in the :test metadata function for that Var.
|
||||||
|
|
||||||
|
When *load-tests* is false, only evaluates the definition, ignoring
|
||||||
|
the tests."
|
||||||
|
{:added "1.1"}
|
||||||
|
[definition & body]
|
||||||
|
(if @load-tests
|
||||||
|
`(doto ~definition (alter-meta! assoc :test (fn [] ~@body)))
|
||||||
|
definition))
|
||||||
|
|
||||||
|
|
||||||
|
(defmacro deftest
|
||||||
|
"Defines a test function with no arguments. Test functions may call
|
||||||
|
other tests, so tests may be composed. If you compose tests, you
|
||||||
|
should also define a function named test-ns-hook; run-tests will
|
||||||
|
call test-ns-hook instead of testing all vars.
|
||||||
|
|
||||||
|
Note: Actually, the test body goes in the :test metadata on the var,
|
||||||
|
and the real function (the value of the var) calls test-var on
|
||||||
|
itself.
|
||||||
|
|
||||||
|
When *load-tests* is false, deftest is ignored."
|
||||||
|
{:added "1.1"}
|
||||||
|
[name & body]
|
||||||
|
(when @load-tests
|
||||||
|
`(def ~(vary-meta name assoc :test `(fn [] ~@body))
|
||||||
|
(fn [] (clojure.test/test-var (var ~name))))))
|
||||||
|
|
||||||
|
(defmacro deftest-
|
||||||
|
"Like deftest but creates a private var."
|
||||||
|
{:added "1.1"}
|
||||||
|
[name & body]
|
||||||
|
(when @load-tests
|
||||||
|
`(def ~(vary-meta name assoc :test `(fn [] ~@body) :private true)
|
||||||
|
(fn [] (test-var (var ~name))))))
|
||||||
|
|
||||||
|
|
||||||
|
(defmacro set-test
|
||||||
|
"Experimental.
|
||||||
|
Sets :test metadata of the named var to a fn with the given body.
|
||||||
|
The var must already exist. Does not modify the value of the var.
|
||||||
|
|
||||||
|
When *load-tests* is false, set-test is ignored."
|
||||||
|
{:added "1.1"}
|
||||||
|
[name & body]
|
||||||
|
(when @load-tests
|
||||||
|
`(alter-meta! (var ~name) assoc :test (fn [] ~@body))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;; DEFINING FIXTURES
|
||||||
|
|
||||||
|
(def ^:private ns->fixtures (atom {}))
|
||||||
|
|
||||||
|
(defn- add-ns-meta
|
||||||
|
"Adds elements in coll to the current namespace metadata as the
|
||||||
|
value of key."
|
||||||
|
{:added "1.1"}
|
||||||
|
[key coll]
|
||||||
|
(swap! ns->fixtures assoc-in [(sci-namespaces/sci-ns-name @vars/current-ns) key] coll))
|
||||||
|
|
||||||
|
(defmulti use-fixtures
|
||||||
|
"Wrap test runs in a fixture function to perform setup and
|
||||||
|
teardown. Using a fixture-type of :each wraps every test
|
||||||
|
individually, while :once wraps the whole run in a single function."
|
||||||
|
{:added "1.1"}
|
||||||
|
(fn [fixture-type & args] fixture-type))
|
||||||
|
|
||||||
|
(defmethod use-fixtures :each [fixture-type & args]
|
||||||
|
(add-ns-meta ::each-fixtures args))
|
||||||
|
|
||||||
|
(defmethod use-fixtures :once [fixture-type & args]
|
||||||
|
(add-ns-meta ::once-fixtures args))
|
||||||
|
|
||||||
|
(defn- default-fixture
|
||||||
|
"The default, empty, fixture function. Just calls its argument."
|
||||||
|
{:added "1.1"}
|
||||||
|
[f]
|
||||||
|
(f))
|
||||||
|
|
||||||
|
(defn compose-fixtures
|
||||||
|
"Composes two fixture functions, creating a new fixture function
|
||||||
|
that combines their behavior."
|
||||||
|
{:added "1.1"}
|
||||||
|
[f1 f2]
|
||||||
|
(fn [g] (f1 (fn [] (f2 g)))))
|
||||||
|
|
||||||
|
(defn join-fixtures
|
||||||
|
"Composes a collection of fixtures, in order. Always returns a valid
|
||||||
|
fixture function, even if the collection is empty."
|
||||||
|
{:added "1.1"}
|
||||||
|
[fixtures]
|
||||||
|
(reduce compose-fixtures default-fixture fixtures))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;; RUNNING TESTS: LOW-LEVEL FUNCTIONS
|
||||||
|
|
||||||
|
(defn test-var
|
||||||
|
"If v has a function in its :test metadata, calls that function,
|
||||||
|
with *testing-vars* bound to (conj *testing-vars* v)."
|
||||||
|
{:dynamic true, :added "1.1"}
|
||||||
|
[v]
|
||||||
|
(when-let [t (:test (meta v))]
|
||||||
|
(sci/binding [testing-vars (conj @testing-vars v)]
|
||||||
|
(do-report {:type :begin-test-var, :var v})
|
||||||
|
(inc-report-counter :test)
|
||||||
|
(try (t)
|
||||||
|
(catch Throwable e
|
||||||
|
(do-report {:type :error, :message "Uncaught exception, not in assertion."
|
||||||
|
:expected nil, :actual e})))
|
||||||
|
(do-report {:type :end-test-var, :var v}))))
|
||||||
|
|
||||||
|
(defn test-vars
|
||||||
|
"Groups vars by their namespace and runs test-vars on them with
|
||||||
|
appropriate fixtures applied."
|
||||||
|
{:added "1.6"}
|
||||||
|
[vars]
|
||||||
|
(doseq [[ns vars] (group-by (comp :ns meta) vars)
|
||||||
|
:when ns]
|
||||||
|
(let [ns-name (sci-namespaces/sci-ns-name ns)
|
||||||
|
fixtures (get @ns->fixtures ns-name)
|
||||||
|
once-fixture-fn (join-fixtures (::once-fixtures fixtures))
|
||||||
|
each-fixture-fn (join-fixtures (::each-fixtures fixtures))]
|
||||||
|
(once-fixture-fn
|
||||||
|
(fn []
|
||||||
|
(doseq [v vars]
|
||||||
|
(when (:test (meta v))
|
||||||
|
(each-fixture-fn (fn [] (test-var v))))))))))
|
||||||
|
|
||||||
|
(defn test-all-vars
|
||||||
|
"Calls test-vars on every var interned in the namespace, with fixtures."
|
||||||
|
{:added "1.1"}
|
||||||
|
[ctx ns]
|
||||||
|
(test-vars (vals (sci-namespaces/sci-ns-interns ctx ns))))
|
||||||
|
|
||||||
|
(defn test-ns
|
||||||
|
"If the namespace defines a function named test-ns-hook, calls that.
|
||||||
|
Otherwise, calls test-all-vars on the namespace. 'ns' is a
|
||||||
|
namespace object or a symbol.
|
||||||
|
|
||||||
|
Internally binds *report-counters* to a ref initialized to
|
||||||
|
*initial-report-counters*. Returns the final, dereferenced state of
|
||||||
|
*report-counters*."
|
||||||
|
{:added "1.1"}
|
||||||
|
[ctx ns]
|
||||||
|
(sci/binding [report-counters (atom @initial-report-counters)]
|
||||||
|
(let [ns-obj (sci-namespaces/sci-the-ns ctx ns)]
|
||||||
|
(do-report {:type :begin-test-ns, :ns ns-obj})
|
||||||
|
;; If the namespace has a test-ns-hook function, call that:
|
||||||
|
(let [ns-sym (sci-namespaces/sci-ns-name ns-obj)]
|
||||||
|
(if-let [v (get-in @(:env ctx) [:namespaces ns-sym 'test-ns-hook])]
|
||||||
|
(@v)
|
||||||
|
;; Otherwise, just test every var in the namespace.
|
||||||
|
(test-all-vars ctx ns-obj)))
|
||||||
|
(do-report {:type :end-test-ns, :ns ns-obj}))
|
||||||
|
@@report-counters))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;; RUNNING TESTS: HIGH-LEVEL FUNCTIONS
|
||||||
|
|
||||||
|
(defn run-tests
|
||||||
|
"Runs all tests in the given namespaces; prints results.
|
||||||
|
Defaults to current namespace if none given. Returns a map
|
||||||
|
summarizing test results."
|
||||||
|
{:added "1.1"}
|
||||||
|
([ctx] (run-tests ctx @vars/current-ns))
|
||||||
|
([ctx & namespaces]
|
||||||
|
(let [summary (assoc (apply merge-with + (map #(test-ns ctx %) namespaces))
|
||||||
|
:type :summary)]
|
||||||
|
(do-report summary)
|
||||||
|
summary)))
|
||||||
|
|
||||||
|
(defn run-all-tests
|
||||||
|
"Runs all tests in all namespaces; prints results.
|
||||||
|
Optional argument is a regular expression; only namespaces with
|
||||||
|
names matching the regular expression (with re-matches) will be
|
||||||
|
tested."
|
||||||
|
{:added "1.1"}
|
||||||
|
([ctx] (apply run-tests ctx (sci-namespaces/sci-all-ns ctx)))
|
||||||
|
([ctx re] (apply run-tests ctx
|
||||||
|
(filter #(re-matches re (name (sci-namespaces/sci-ns-name %)))
|
||||||
|
(sci-namespaces/sci-all-ns ctx)))))
|
||||||
|
|
||||||
|
(defn successful?
|
||||||
|
"Returns true if the given test summary indicates all tests
|
||||||
|
were successful, false otherwise."
|
||||||
|
{:added "1.1"}
|
||||||
|
[summary]
|
||||||
|
(and (zero? (:fail summary 0))
|
||||||
|
(zero? (:error summary 0))))
|
||||||
4
src/babashka/impl/common.clj
Normal file
4
src/babashka/impl/common.clj
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
(ns babashka.impl.common)
|
||||||
|
|
||||||
|
;; placeholder for ctx
|
||||||
|
(def ctx (volatile! nil))
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
[clojure.tools.reader.reader-types :as r]
|
[clojure.tools.reader.reader-types :as r]
|
||||||
[sci.impl.interpreter :refer [eval-form]]
|
[sci.impl.interpreter :refer [eval-form]]
|
||||||
[sci.impl.parser :as parser]
|
[sci.impl.parser :as parser]
|
||||||
|
[sci.impl.vars :as vars]
|
||||||
[sci.core :as sci]
|
[sci.core :as sci]
|
||||||
[sci.impl.io :as sio]))
|
[sci.impl.io :as sio]))
|
||||||
|
|
||||||
|
|
@ -29,7 +30,8 @@
|
||||||
"REPL.")
|
"REPL.")
|
||||||
(sio/println "Use :repl/quit or :repl/exit to quit the REPL.")
|
(sio/println "Use :repl/quit or :repl/exit to quit the REPL.")
|
||||||
(sio/println "Clojure rocks, Bash reaches.")
|
(sio/println "Clojure rocks, Bash reaches.")
|
||||||
(sio/println)))
|
(sio/println)
|
||||||
|
(eval-form sci-ctx '(require '[clojure.repl :refer [dir doc]]))))
|
||||||
:read (or read
|
:read (or read
|
||||||
(fn [_request-prompt request-exit]
|
(fn [_request-prompt request-exit]
|
||||||
;; (prn "PEEK" @sci/in (r/peek-char @sci/in))
|
;; (prn "PEEK" @sci/in (r/peek-char @sci/in))
|
||||||
|
|
@ -57,7 +59,7 @@
|
||||||
expr)]
|
expr)]
|
||||||
ret)))
|
ret)))
|
||||||
:need-prompt (or need-prompt (fn [] true))
|
:need-prompt (or need-prompt (fn [] true))
|
||||||
:prompt (or prompt #(sio/printf "%s=> " (-> sci-ctx :env deref :current-ns)))
|
:prompt (or prompt #(sio/printf "%s=> " (vars/current-ns-name)))
|
||||||
:flush (or flush sio/flush)
|
:flush (or flush sio/flush)
|
||||||
:print (or print sio/prn)
|
:print (or print sio/prn)
|
||||||
:caught (or caught repl-caught)))))
|
:caught (or caught repl-caught)))))
|
||||||
|
|
|
||||||
56
src/babashka/impl/test.clj
Normal file
56
src/babashka/impl/test.clj
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
(ns babashka.impl.test
|
||||||
|
(:require [babashka.impl.clojure.test :as t]))
|
||||||
|
|
||||||
|
(defn macrofy [v]
|
||||||
|
(with-meta v {:sci/macro true}))
|
||||||
|
|
||||||
|
(defn contextualize [v]
|
||||||
|
(with-meta v {:sci.impl/op :needs-ctx}))
|
||||||
|
|
||||||
|
(def clojure-test-namespace
|
||||||
|
{'*load-tests* t/load-tests
|
||||||
|
'*stack-trace-depth* t/stack-trace-depth
|
||||||
|
'*report-counters* t/report-counters
|
||||||
|
'*initial-report-counters* t/initial-report-counters
|
||||||
|
'*testing-vars* t/testing-vars
|
||||||
|
'*testing-contexts* t/testing-contexts
|
||||||
|
'*test-out* t/test-out
|
||||||
|
;; 'with-test-out (macrofy @#'t/with-test-out)
|
||||||
|
;; 'file-position t/file-position
|
||||||
|
'testing-vars-str t/testing-vars-str
|
||||||
|
'testing-contexts-str t/testing-contexts-str
|
||||||
|
'inc-report-counter t/inc-report-counter
|
||||||
|
'report t/report
|
||||||
|
'do-report t/do-report
|
||||||
|
;; assertion utilities
|
||||||
|
'function? t/function?
|
||||||
|
'assert-predicate t/assert-predicate
|
||||||
|
'assert-any t/assert-any
|
||||||
|
;; assertion methods
|
||||||
|
'assert-expr t/assert-expr
|
||||||
|
'try-expr (with-meta @#'t/try-expr
|
||||||
|
{:sci/macro true})
|
||||||
|
;; assertion macros
|
||||||
|
'is (with-meta @#'t/is
|
||||||
|
{;; :sci.impl/op :needs-ctx
|
||||||
|
:sci/macro true})
|
||||||
|
'are (macrofy @#'t/are)
|
||||||
|
'testing (macrofy @#'t/testing)
|
||||||
|
;; defining tests
|
||||||
|
'with-test (macrofy @#'t/with-test)
|
||||||
|
'deftest (macrofy @#'t/deftest)
|
||||||
|
'deftest- (macrofy @#'t/deftest-)
|
||||||
|
'set-test (macrofy @#'t/set-test)
|
||||||
|
;; fixtures
|
||||||
|
'use-fixtures t/use-fixtures
|
||||||
|
'compose-fixtures t/compose-fixtures
|
||||||
|
'join-fixtures t/join-fixtures
|
||||||
|
;; running tests: low level
|
||||||
|
'test-var t/test-var
|
||||||
|
'test-vars t/test-vars
|
||||||
|
'test-all-vars (with-meta t/test-all-vars {:sci.impl/op :needs-ctx})
|
||||||
|
'test-ns (with-meta t/test-ns {:sci.impl/op :needs-ctx})
|
||||||
|
;; running tests: high level
|
||||||
|
'run-tests (contextualize t/run-tests)
|
||||||
|
'run-all-tests (contextualize t/run-all-tests)
|
||||||
|
'successful? t/successful?})
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
(ns babashka.main
|
(ns babashka.main
|
||||||
{:no-doc true}
|
{:no-doc true}
|
||||||
(:require
|
(:require
|
||||||
[babashka.impl.async :refer [async-namespace]]
|
[babashka.impl.async :refer [async-namespace async-protocols-namespace]]
|
||||||
[babashka.impl.cheshire :refer [cheshire-core-namespace]]
|
[babashka.impl.cheshire :refer [cheshire-core-namespace]]
|
||||||
[babashka.impl.classes :as classes]
|
[babashka.impl.classes :as classes]
|
||||||
[babashka.impl.classpath :as cp]
|
[babashka.impl.classpath :as cp]
|
||||||
|
|
@ -10,26 +10,31 @@
|
||||||
[babashka.impl.clojure.java.shell :refer [shell-namespace]]
|
[babashka.impl.clojure.java.shell :refer [shell-namespace]]
|
||||||
[babashka.impl.clojure.main :refer [demunge]]
|
[babashka.impl.clojure.main :refer [demunge]]
|
||||||
[babashka.impl.clojure.stacktrace :refer [stacktrace-namespace print-stack-trace]]
|
[babashka.impl.clojure.stacktrace :refer [stacktrace-namespace print-stack-trace]]
|
||||||
|
[babashka.impl.common :as common]
|
||||||
[babashka.impl.csv :as csv]
|
[babashka.impl.csv :as csv]
|
||||||
[babashka.impl.pipe-signal-handler :refer [handle-pipe! pipe-signal-received?]]
|
[babashka.impl.pipe-signal-handler :refer [handle-pipe! pipe-signal-received?]]
|
||||||
[babashka.impl.repl :as repl]
|
[babashka.impl.repl :as repl]
|
||||||
[babashka.impl.socket-repl :as socket-repl]
|
[babashka.impl.socket-repl :as socket-repl]
|
||||||
|
[babashka.impl.test :as t]
|
||||||
[babashka.impl.tools.cli :refer [tools-cli-namespace]]
|
[babashka.impl.tools.cli :refer [tools-cli-namespace]]
|
||||||
[babashka.impl.xml :as xml]
|
[babashka.impl.xml :as xml]
|
||||||
[babashka.wait :as wait]
|
[babashka.wait :as wait]
|
||||||
[clojure.edn :as edn]
|
[clojure.edn :as edn]
|
||||||
[clojure.java.io :as io]
|
[clojure.java.io :as io]
|
||||||
[clojure.string :as str]
|
[clojure.string :as str]
|
||||||
|
[fipp.edn :as fipp]
|
||||||
[sci.addons :as addons]
|
[sci.addons :as addons]
|
||||||
[sci.core :as sci]
|
[sci.core :as sci]
|
||||||
[sci.impl.interpreter :refer [eval-string*]]
|
[sci.impl.interpreter :refer [eval-string*]]
|
||||||
[sci.impl.opts :as sci-opts]
|
[sci.impl.opts :as sci-opts]
|
||||||
|
[sci.impl.unrestrict :refer [*unrestricted*]]
|
||||||
[sci.impl.vars :as vars])
|
[sci.impl.vars :as vars])
|
||||||
(:gen-class))
|
(:gen-class))
|
||||||
|
|
||||||
|
(binding [*unrestricted* true]
|
||||||
(sci/alter-var-root sci/in (constantly *in*))
|
(sci/alter-var-root sci/in (constantly *in*))
|
||||||
(sci/alter-var-root sci/out (constantly *out*))
|
(sci/alter-var-root sci/out (constantly *out*))
|
||||||
(sci/alter-var-root sci/err (constantly *err*))
|
(sci/alter-var-root sci/err (constantly *err*)))
|
||||||
|
|
||||||
(set! *warn-on-reflection* true)
|
(set! *warn-on-reflection* true)
|
||||||
;; To detect problems when generating the image, run:
|
;; To detect problems when generating the image, run:
|
||||||
|
|
@ -42,6 +47,7 @@
|
||||||
(if options
|
(if options
|
||||||
(let [opt (first options)]
|
(let [opt (first options)]
|
||||||
(case opt
|
(case opt
|
||||||
|
("--") (assoc opts-map :command-line-args (next options))
|
||||||
("--version") {:version true}
|
("--version") {:version true}
|
||||||
("--help" "-h" "-?") {:help? true}
|
("--help" "-h" "-?") {:help? true}
|
||||||
("--verbose")(recur (next options)
|
("--verbose")(recur (next options)
|
||||||
|
|
@ -69,6 +75,14 @@
|
||||||
(assoc opts-map
|
(assoc opts-map
|
||||||
:shell-in true
|
:shell-in true
|
||||||
:shell-out true))
|
:shell-out true))
|
||||||
|
("-iO") (recur (next options)
|
||||||
|
(assoc opts-map
|
||||||
|
:shell-in true
|
||||||
|
:edn-out true))
|
||||||
|
("-Io") (recur (next options)
|
||||||
|
(assoc opts-map
|
||||||
|
:edn-in true
|
||||||
|
:shell-out true))
|
||||||
("-IO") (recur (next options)
|
("-IO") (recur (next options)
|
||||||
(assoc opts-map
|
(assoc opts-map
|
||||||
:edn-in true
|
:edn-in true
|
||||||
|
|
@ -100,22 +114,24 @@
|
||||||
("--eval", "-e")
|
("--eval", "-e")
|
||||||
(let [options (next options)]
|
(let [options (next options)]
|
||||||
(recur (next options)
|
(recur (next options)
|
||||||
(assoc opts-map :expression (first options))))
|
(update opts-map :expressions (fnil conj []) (first options))))
|
||||||
("--main", "-m")
|
("--main", "-m")
|
||||||
(let [options (next options)]
|
(let [options (next options)]
|
||||||
(recur (next options)
|
(recur (next options)
|
||||||
(assoc opts-map :main (first options))))
|
(assoc opts-map :main (first options))))
|
||||||
(if (some opts-map [:file :socket-repl :expression :main])
|
(if (some opts-map [:file :socket-repl :expressions :main])
|
||||||
(assoc opts-map
|
(assoc opts-map
|
||||||
:command-line-args options)
|
:command-line-args options)
|
||||||
(if (and (not= \( (first (str/trim opt)))
|
(let [trimmed-opt (str/triml opt)
|
||||||
(.exists (io/file opt)))
|
c (.charAt trimmed-opt 0)]
|
||||||
|
(case c
|
||||||
|
(\( \{ \[ \* \@ \#)
|
||||||
|
(-> opts-map
|
||||||
|
(update :expressions (fnil conj []) (first options))
|
||||||
|
(assoc :command-line-args (next options)))
|
||||||
(assoc opts-map
|
(assoc opts-map
|
||||||
:file opt
|
:file opt
|
||||||
:command-line-args (next options))
|
:command-line-args (next options)))))))
|
||||||
(assoc opts-map
|
|
||||||
:expression opt
|
|
||||||
:command-line-args (next options))))))
|
|
||||||
opts-map))]
|
opts-map))]
|
||||||
opts))
|
opts))
|
||||||
|
|
||||||
|
|
@ -166,9 +182,10 @@
|
||||||
-f, --file <path> Evaluate a file.
|
-f, --file <path> Evaluate a file.
|
||||||
-cp, --classpath Classpath to use.
|
-cp, --classpath Classpath to use.
|
||||||
-m, --main <ns> Call the -main function from namespace with args.
|
-m, --main <ns> Call the -main function from namespace with args.
|
||||||
--repl Start REPL
|
--repl Start REPL. Use rlwrap for history.
|
||||||
--socket-repl Start socket REPL. Specify port (e.g. 1666) or host and port separated by colon (e.g. 127.0.0.1:1666).
|
--socket-repl Start socket REPL. Specify port (e.g. 1666) or host and port separated by colon (e.g. 127.0.0.1:1666).
|
||||||
--time Print execution time before exiting.
|
--time Print execution time before exiting.
|
||||||
|
-- Stop parsing args and pass everything after -- to *command-line-args*
|
||||||
|
|
||||||
If neither -e, -f, or --socket-repl are specified, then the first argument that is not parsed as a option is treated as a file if it exists, or as an expression otherwise.
|
If neither -e, -f, or --socket-repl are specified, then the first argument that is not parsed as a option is treated as a file if it exists, or as an expression otherwise.
|
||||||
Everything after that is bound to *command-line-args*."))
|
Everything after that is bound to *command-line-args*."))
|
||||||
|
|
@ -190,7 +207,7 @@ Everything after that is bound to *command-line-args*."))
|
||||||
(defn load-file* [sci-ctx f]
|
(defn load-file* [sci-ctx f]
|
||||||
(let [f (io/file f)
|
(let [f (io/file f)
|
||||||
s (slurp f)]
|
s (slurp f)]
|
||||||
(sci/with-bindings {vars/file-var (.getCanonicalPath f)}
|
(sci/with-bindings {vars/current-file (.getCanonicalPath f)}
|
||||||
(eval-string* sci-ctx s))))
|
(eval-string* sci-ctx s))))
|
||||||
|
|
||||||
(defn eval* [sci-ctx form]
|
(defn eval* [sci-ctx form]
|
||||||
|
|
@ -204,15 +221,11 @@ Everything after that is bound to *command-line-args*."))
|
||||||
(defn exit [n]
|
(defn exit [n]
|
||||||
(throw (ex-info "" {:bb/exit-code n})))
|
(throw (ex-info "" {:bb/exit-code n})))
|
||||||
|
|
||||||
;; (sci/set-var-root! sci/*in* *in*)
|
|
||||||
;; (sci/set-var-root! sci/*out* *out*)
|
|
||||||
;; (sci/set-var-root! sci/*err* *err*)
|
|
||||||
|
|
||||||
(def aliases
|
(def aliases
|
||||||
'{tools.cli 'clojure.tools.cli
|
'{tools.cli clojure.tools.cli
|
||||||
edn clojure.edn
|
edn clojure.edn
|
||||||
wait babashka.wait
|
wait babashka.wait
|
||||||
sig babashka.signal
|
signal babashka.signal
|
||||||
shell clojure.java.shell
|
shell clojure.java.shell
|
||||||
io clojure.java.io
|
io clojure.java.io
|
||||||
async clojure.core.async
|
async clojure.core.async
|
||||||
|
|
@ -220,21 +233,35 @@ Everything after that is bound to *command-line-args*."))
|
||||||
json cheshire.core
|
json cheshire.core
|
||||||
xml clojure.data.xml})
|
xml clojure.data.xml})
|
||||||
|
|
||||||
|
(def cp-state (atom nil))
|
||||||
|
|
||||||
|
(defn add-classpath* [add-to-cp]
|
||||||
|
(swap! cp-state
|
||||||
|
(fn [{:keys [:cp]}]
|
||||||
|
(let [new-cp
|
||||||
|
(if-not cp add-to-cp
|
||||||
|
(str cp (System/getProperty "path.separator") add-to-cp))]
|
||||||
|
{:loader (cp/loader new-cp)
|
||||||
|
:cp new-cp})))
|
||||||
|
nil)
|
||||||
|
|
||||||
(def namespaces
|
(def namespaces
|
||||||
{'clojure.tools.cli tools-cli-namespace
|
{'clojure.tools.cli tools-cli-namespace
|
||||||
'clojure.edn {'read edn/read
|
|
||||||
'read-string edn/read-string}
|
|
||||||
'clojure.java.shell shell-namespace
|
'clojure.java.shell shell-namespace
|
||||||
'babashka.wait {'wait-for-port wait/wait-for-port
|
'babashka.wait {'wait-for-port wait/wait-for-port
|
||||||
'wait-for-path wait/wait-for-path}
|
'wait-for-path wait/wait-for-path}
|
||||||
'babashka.signal {'pipe-signal-received? pipe-signal-received?}
|
'babashka.signal {'pipe-signal-received? pipe-signal-received?}
|
||||||
'clojure.java.io io-namespace
|
'clojure.java.io io-namespace
|
||||||
'clojure.core.async async-namespace
|
'clojure.core.async async-namespace
|
||||||
|
'clojure.core.async.impl.protocols async-protocols-namespace
|
||||||
'clojure.data.csv csv/csv-namespace
|
'clojure.data.csv csv/csv-namespace
|
||||||
'cheshire.core cheshire-core-namespace
|
'cheshire.core cheshire-core-namespace
|
||||||
'clojure.stacktrace stacktrace-namespace
|
'clojure.stacktrace stacktrace-namespace
|
||||||
'clojure.main {'demunge demunge}
|
'clojure.main {'demunge demunge}
|
||||||
'clojure.repl {'demunge demunge}
|
'clojure.repl {'demunge demunge}
|
||||||
|
'clojure.test t/clojure-test-namespace
|
||||||
|
'babashka.classpath {'add-classpath add-classpath*}
|
||||||
|
'clojure.pprint {'pprint fipp/pprint}
|
||||||
'clojure.data.xml xml/xml-namespace})
|
'clojure.data.xml xml/xml-namespace})
|
||||||
|
|
||||||
(def bindings
|
(def bindings
|
||||||
|
|
@ -248,7 +275,9 @@ Everything after that is bound to *command-line-args*."))
|
||||||
(if exit-code [nil exit-code]
|
(if exit-code [nil exit-code]
|
||||||
(do (if verbose?
|
(do (if verbose?
|
||||||
(print-stack-trace e)
|
(print-stack-trace e)
|
||||||
(println (.getMessage e)))
|
(println (str (.. e getClass getName)
|
||||||
|
(when-let [m (.getMessage e)]
|
||||||
|
(str ": " m)) )))
|
||||||
(flush)
|
(flush)
|
||||||
[nil 1])))))
|
[nil 1])))))
|
||||||
|
|
||||||
|
|
@ -257,10 +286,13 @@ Everything after that is bound to *command-line-args*."))
|
||||||
(handle-pipe!)
|
(handle-pipe!)
|
||||||
#_(binding [*out* *err*]
|
#_(binding [*out* *err*]
|
||||||
(prn "M" (meta (get bindings 'future))))
|
(prn "M" (meta (get bindings 'future))))
|
||||||
|
(binding [*unrestricted* true]
|
||||||
|
(sci/binding [reflection-var false
|
||||||
|
vars/current-ns (vars/->SciNamespace 'user nil)]
|
||||||
(let [t0 (System/currentTimeMillis)
|
(let [t0 (System/currentTimeMillis)
|
||||||
{:keys [:version :shell-in :edn-in :shell-out :edn-out
|
{:keys [:version :shell-in :edn-in :shell-out :edn-out
|
||||||
:help? :file :command-line-args
|
:help? :file :command-line-args
|
||||||
:expression :stream? :time?
|
:expressions :stream? :time?
|
||||||
:repl :socket-repl
|
:repl :socket-repl
|
||||||
:verbose? :classpath
|
:verbose? :classpath
|
||||||
:main :uberscript] :as _opts}
|
:main :uberscript] :as _opts}
|
||||||
|
|
@ -281,27 +313,26 @@ Everything after that is bound to *command-line-args*."))
|
||||||
env (atom {})
|
env (atom {})
|
||||||
classpath (or classpath
|
classpath (or classpath
|
||||||
(System/getenv "BABASHKA_CLASSPATH"))
|
(System/getenv "BABASHKA_CLASSPATH"))
|
||||||
loader (when classpath
|
_ (when classpath
|
||||||
(cp/loader classpath))
|
(add-classpath* classpath))
|
||||||
load-fn (when classpath
|
load-fn (fn [{:keys [:namespace]}]
|
||||||
(fn [{:keys [:namespace]}]
|
(when-let [{:keys [:loader]} @cp-state]
|
||||||
(let [res (cp/source-for-namespace loader namespace nil)]
|
(let [res (cp/source-for-namespace loader namespace nil)]
|
||||||
(when uberscript (swap! uberscript-sources conj (:source res)))
|
(when uberscript (swap! uberscript-sources conj (:source res)))
|
||||||
res)))
|
res)))
|
||||||
_ (when file (vars/bindRoot vars/file-var (.getCanonicalPath (io/file file))))
|
_ (when file (vars/bindRoot vars/current-file (.getCanonicalPath (io/file file))))
|
||||||
ctx {:aliases aliases
|
ctx {:aliases aliases
|
||||||
:namespaces (-> namespaces
|
:namespaces (-> namespaces
|
||||||
(assoc 'clojure.core
|
(assoc 'clojure.core
|
||||||
(assoc core-extras
|
(assoc core-extras
|
||||||
'*command-line-args*
|
'*command-line-args*
|
||||||
(sci/new-dynamic-var '*command-line-args* command-line-args)
|
(sci/new-dynamic-var '*command-line-args* command-line-args)
|
||||||
'*file* vars/file-var
|
|
||||||
'*warn-on-reflection* reflection-var))
|
'*warn-on-reflection* reflection-var))
|
||||||
(assoc-in ['clojure.java.io 'resource]
|
(assoc-in ['clojure.java.io 'resource]
|
||||||
#(when classpath (cp/getResource loader % {:url? true}))))
|
#(when-let [{:keys [:loader]} @cp-state] (cp/getResource loader % {:url? true}))))
|
||||||
:bindings bindings
|
:bindings bindings
|
||||||
:env env
|
:env env
|
||||||
:features #{:bb}
|
:features #{:bb :clj}
|
||||||
:classes classes/class-map
|
:classes classes/class-map
|
||||||
:imports '{ArithmeticException java.lang.ArithmeticException
|
:imports '{ArithmeticException java.lang.ArithmeticException
|
||||||
AssertionError java.lang.AssertionError
|
AssertionError java.lang.AssertionError
|
||||||
|
|
@ -312,16 +343,20 @@ Everything after that is bound to *command-line-args*."))
|
||||||
IllegalArgumentException java.lang.IllegalArgumentException
|
IllegalArgumentException java.lang.IllegalArgumentException
|
||||||
Integer java.lang.Integer
|
Integer java.lang.Integer
|
||||||
File java.io.File
|
File java.io.File
|
||||||
|
Long java.lang.Long
|
||||||
Math java.lang.Math
|
Math java.lang.Math
|
||||||
Object java.lang.Object
|
Object java.lang.Object
|
||||||
ProcessBuilder java.lang.ProcessBuilder
|
ProcessBuilder java.lang.ProcessBuilder
|
||||||
String java.lang.String
|
String java.lang.String
|
||||||
|
StringBuilder java.lang.StringBuilder
|
||||||
System java.lang.System
|
System java.lang.System
|
||||||
Thread java.lang.Thread}
|
Thread java.lang.Thread
|
||||||
|
Throwable java.lang.Throwable}
|
||||||
:load-fn load-fn
|
:load-fn load-fn
|
||||||
:dry-run uberscript}
|
:dry-run uberscript}
|
||||||
ctx (addons/future ctx)
|
ctx (addons/future ctx)
|
||||||
sci-ctx (sci-opts/init ctx)
|
sci-ctx (sci-opts/init ctx)
|
||||||
|
_ (vreset! common/ctx sci-ctx)
|
||||||
_ (swap! (:env sci-ctx)
|
_ (swap! (:env sci-ctx)
|
||||||
(fn [env]
|
(fn [env]
|
||||||
(update-in env [:namespaces 'clojure.core] assoc
|
(update-in env [:namespaces 'clojure.core] assoc
|
||||||
|
|
@ -334,26 +369,25 @@ Everything after that is bound to *command-line-args*."))
|
||||||
(let [opts (apply hash-map opts)]
|
(let [opts (apply hash-map opts)]
|
||||||
(repl/start-repl! sci-ctx opts))))))
|
(repl/start-repl! sci-ctx opts))))))
|
||||||
preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim))
|
preloads (some-> (System/getenv "BABASHKA_PRELOADS") (str/trim))
|
||||||
[expression exit-code]
|
[expressions exit-code]
|
||||||
(cond expression [expression nil]
|
(cond expressions [expressions nil]
|
||||||
main [(format "(ns user (:require [%1$s])) (apply %1$s/-main *command-line-args*)"
|
main [[(format "(ns user (:require [%1$s])) (apply %1$s/-main *command-line-args*)"
|
||||||
main) nil]
|
main)] nil]
|
||||||
file (try [(read-file file) nil]
|
file (try [[(read-file file)] nil]
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
(error-handler* e verbose?))))
|
(error-handler* e verbose?))))
|
||||||
|
expression (str/join " " expressions) ;; this might mess with the locations...
|
||||||
exit-code
|
exit-code
|
||||||
;; handle preloads
|
;; handle preloads
|
||||||
(if exit-code exit-code
|
(if exit-code exit-code
|
||||||
(do (when preloads (try (eval-string* sci-ctx preloads)
|
(do (when preloads
|
||||||
|
(try
|
||||||
|
(eval-string* sci-ctx preloads)
|
||||||
(catch Throwable e
|
(catch Throwable e
|
||||||
(error-handler* e verbose?))))
|
(error-handler* e verbose?))))
|
||||||
nil))
|
nil))
|
||||||
exit-code
|
exit-code
|
||||||
(or exit-code
|
(or exit-code
|
||||||
(sci/with-bindings {reflection-var false}
|
|
||||||
(or
|
|
||||||
#_(binding [*out* *err*]
|
|
||||||
(prn ">>" _opts))
|
|
||||||
(second
|
(second
|
||||||
(cond version
|
(cond version
|
||||||
[(print-version) 0]
|
[(print-version) 0]
|
||||||
|
|
@ -388,7 +422,7 @@ Everything after that is bound to *command-line-args*."))
|
||||||
(error-handler* e verbose?)))
|
(error-handler* e verbose?)))
|
||||||
uberscript [nil 0]
|
uberscript [nil 0]
|
||||||
:else [(repl/start-repl! sci-ctx) 0]))
|
:else [(repl/start-repl! sci-ctx) 0]))
|
||||||
1)))
|
1)
|
||||||
t1 (System/currentTimeMillis)]
|
t1 (System/currentTimeMillis)]
|
||||||
(flush)
|
(flush)
|
||||||
(when uberscript
|
(when uberscript
|
||||||
|
|
@ -401,12 +435,21 @@ Everything after that is bound to *command-line-args*."))
|
||||||
(spit uberscript-out expression :append true)))
|
(spit uberscript-out expression :append true)))
|
||||||
(when time? (binding [*out* *err*]
|
(when time? (binding [*out* *err*]
|
||||||
(println "bb took" (str (- t1 t0) "ms."))))
|
(println "bb took" (str (- t1 t0) "ms."))))
|
||||||
exit-code))
|
exit-code))))
|
||||||
|
|
||||||
(defn -main
|
(defn -main
|
||||||
[& args]
|
[& args]
|
||||||
|
(if-let [dev-opts (System/getenv "BABASHKA_DEV")]
|
||||||
|
(let [{:keys [:n]} (edn/read-string dev-opts)
|
||||||
|
last-iteration (dec n)]
|
||||||
|
(dotimes [i n]
|
||||||
|
(if (< i last-iteration)
|
||||||
|
(with-out-str (apply main args))
|
||||||
|
(do (apply main args)
|
||||||
|
(binding [*out* *err*]
|
||||||
|
(println "ran" n "times"))))))
|
||||||
(let [exit-code (apply main args)]
|
(let [exit-code (apply main args)]
|
||||||
(System/exit exit-code)))
|
(System/exit exit-code))))
|
||||||
|
|
||||||
;;;; Scratch
|
;;;; Scratch
|
||||||
|
|
||||||
|
|
|
||||||
21
test-resources/babashka/assert_expr.clj
Normal file
21
test-resources/babashka/assert_expr.clj
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
(require '[clojure.test :refer [is deftest] :as t])
|
||||||
|
|
||||||
|
(defmethod t/assert-expr 'roughly [msg form]
|
||||||
|
`(let [op1# ~(nth form 1)
|
||||||
|
op2# ~(nth form 2)
|
||||||
|
tolerance# (if (= 4 ~(count form)) ~(last form) 2)
|
||||||
|
decimals# (/ 1. (Math/pow 10 tolerance#))
|
||||||
|
result# (< (Math/abs (- op1# op2#)) decimals#)]
|
||||||
|
(t/do-report
|
||||||
|
{:type (if result# :pass :fail)
|
||||||
|
:message ~msg
|
||||||
|
:expected (format "%s should be roughly %s with %s tolerance"
|
||||||
|
op1# op2# decimals#)
|
||||||
|
:actual result#})
|
||||||
|
result#))
|
||||||
|
|
||||||
|
(deftest PI-test
|
||||||
|
(is (roughly 3.14 Math/PI 2))
|
||||||
|
(is (roughly 3.14 Math/PI 3)))
|
||||||
|
|
||||||
|
(t/test-var #'PI-test)
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
(:require
|
(:require
|
||||||
[babashka.test-utils :as tu]
|
[babashka.test-utils :as tu]
|
||||||
[clojure.edn :as edn]
|
[clojure.edn :as edn]
|
||||||
[clojure.test :as t :refer [deftest is]]
|
[clojure.java.io :as io]
|
||||||
[clojure.java.io :as io]))
|
[clojure.test :as t :refer [deftest is]]))
|
||||||
|
|
||||||
(defn bb [input & args]
|
(defn bb [input & args]
|
||||||
(edn/read-string (apply tu/bb (when (some? input) (str input)) (map str args))))
|
(edn/read-string (apply tu/bb (when (some? input) (str input)) (map str args))))
|
||||||
|
|
@ -14,7 +14,10 @@
|
||||||
"(require '[my-script :as ms]) (ms/foo)")))
|
"(require '[my-script :as ms]) (ms/foo)")))
|
||||||
(is (= "hello from foo\n"
|
(is (= "hello from foo\n"
|
||||||
(tu/bb nil "--classpath" "test-resources/babashka/src_for_classpath_test/foo.jar"
|
(tu/bb nil "--classpath" "test-resources/babashka/src_for_classpath_test/foo.jar"
|
||||||
"(require '[foo :as f]) (f/foo)"))))
|
"(require '[foo :as f]) (f/foo)")))
|
||||||
|
(is (thrown-with-msg? Exception #"not require"
|
||||||
|
(tu/bb nil
|
||||||
|
"(require '[foo :as f])"))))
|
||||||
|
|
||||||
(deftest classpath-env-test
|
(deftest classpath-env-test
|
||||||
;; for this test you have to set `BABASHKA_CLASSPATH` to test-resources/babashka/src_for_classpath_test/env
|
;; for this test you have to set `BABASHKA_CLASSPATH` to test-resources/babashka/src_for_classpath_test/env
|
||||||
|
|
@ -30,7 +33,7 @@
|
||||||
(deftest uberscript-test
|
(deftest uberscript-test
|
||||||
(let [tmp-file (java.io.File/createTempFile "uberscript" ".clj")]
|
(let [tmp-file (java.io.File/createTempFile "uberscript" ".clj")]
|
||||||
(.deleteOnExit tmp-file)
|
(.deleteOnExit tmp-file)
|
||||||
(tu/bb nil "--classpath" "test-resources/babashka/src_for_classpath_test" "-m" "my.main" "--uberscript" (.getPath tmp-file))
|
(is (empty? (tu/bb nil "--classpath" "test-resources/babashka/src_for_classpath_test" "-m" "my.main" "--uberscript" (.getPath tmp-file))))
|
||||||
(is (= "(\"1\" \"2\" \"3\" \"4\")\n"
|
(is (= "(\"1\" \"2\" \"3\" \"4\")\n"
|
||||||
(tu/bb nil "--file" (.getPath tmp-file) "1" "2" "3" "4")))))
|
(tu/bb nil "--file" (.getPath tmp-file) "1" "2" "3" "4")))))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,11 @@
|
||||||
(apply tu/bb (when (some? input) (str input)) (map str args)))
|
(apply tu/bb (when (some? input) (str input)) (map str args)))
|
||||||
|
|
||||||
(deftest file-var-test
|
(deftest file-var-test
|
||||||
(let [[f1 f2 f3]
|
(let [[f1 f2 f3 f4]
|
||||||
(str/split (bb nil "--classpath" "test/babashka/scripts"
|
(str/split (bb nil "--classpath" "test/babashka/scripts"
|
||||||
"test/babashka/scripts/file_var.bb")
|
"test/babashka/scripts/file_var.bb")
|
||||||
#"\n")]
|
#"\n")]
|
||||||
(is (str/ends-with? f1 "file_var_classpath.bb"))
|
(is (str/ends-with? f1 "file_var_classpath.bb"))
|
||||||
(is (str/ends-with? f2 "loaded_by_file_var.bb"))
|
(is (str/ends-with? f2 "loaded_by_file_var.bb"))
|
||||||
(is (str/ends-with? f3 "file_var.bb"))))
|
(is (str/ends-with? f3 "file_var.bb"))
|
||||||
|
(is (str/ends-with? f4 "file_var.bb"))))
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,10 @@
|
||||||
(vars/bindRoot sci/err *err*)
|
(vars/bindRoot sci/err *err*)
|
||||||
|
|
||||||
(defn repl! []
|
(defn repl! []
|
||||||
|
(sci/with-bindings {vars/current-ns (vars/->SciNamespace 'user nil)}
|
||||||
(start-repl! (init {:bindings {'*command-line-args*
|
(start-repl! (init {:bindings {'*command-line-args*
|
||||||
["a" "b" "c"]}
|
["a" "b" "c"]}
|
||||||
:env (atom {})})))
|
:env (atom {})}))))
|
||||||
|
|
||||||
(defn assert-repl [expr expected]
|
(defn assert-repl [expr expected]
|
||||||
(is (str/includes? (sci/with-out-str
|
(is (str/includes? (sci/with-out-str
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,8 @@
|
||||||
(is (socket-command "#?(:bb 1337 :clj 8888)" "1337")))
|
(is (socket-command "#?(:bb 1337 :clj 8888)" "1337")))
|
||||||
(testing "*1, *2, *3, *e"
|
(testing "*1, *2, *3, *e"
|
||||||
(is (socket-command "1\n*1" "1")))
|
(is (socket-command "1\n*1" "1")))
|
||||||
|
(testing "*ns*"
|
||||||
|
(is (socket-command "(ns foo.bar) (ns-name *ns*)" "foo.bar")))
|
||||||
(finally
|
(finally
|
||||||
(if tu/jvm?
|
(if tu/jvm?
|
||||||
(stop-repl!)
|
(stop-repl!)
|
||||||
|
|
|
||||||
20
test/babashka/java_security_test.clj
Normal file
20
test/babashka/java_security_test.clj
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
(ns babashka.java-security-test
|
||||||
|
(:require
|
||||||
|
[babashka.test-utils :as test-utils]
|
||||||
|
[clojure.edn :as edn]
|
||||||
|
[clojure.test :as test :refer [deftest is]]))
|
||||||
|
|
||||||
|
(defn bb [expr]
|
||||||
|
(edn/read-string (apply test-utils/bb nil [(str expr)])))
|
||||||
|
|
||||||
|
(defn signature [algo]
|
||||||
|
(clojure.walk/postwalk-replace {::algo algo}
|
||||||
|
'(defn signature [^String s]
|
||||||
|
(let [algorithm (java.security.MessageDigest/getInstance ::algo)
|
||||||
|
digest (.digest algorithm (.getBytes s))]
|
||||||
|
(format "%032x" (java.math.BigInteger. 1 digest))))))
|
||||||
|
|
||||||
|
(deftest java-security-test
|
||||||
|
(is (= "49f68a5c8493ec2c0bf489821c21fc3b" (bb (list 'do (signature "MD5") '(signature "hi")))))
|
||||||
|
(is (= "c22b5f9178342609428d6f51b2c5af4c0bde6a42" (bb (list 'do (signature "SHA-1") '(signature "hi")))))
|
||||||
|
(is (= "8f434346648f6b96df89dda901c5176b10a6d83961dd3c1ac88b59b2dc327aa4" (bb (list 'do (signature "SHA-256") '(signature "hi"))))))
|
||||||
|
|
@ -13,24 +13,19 @@
|
||||||
(edn/read-string (apply test-utils/bb (when (some? input) (str input)) (map str args))))
|
(edn/read-string (apply test-utils/bb (when (some? input) (str input)) (map str args))))
|
||||||
|
|
||||||
(deftest parse-opts-test
|
(deftest parse-opts-test
|
||||||
(is (= {:expression "(println 123)"}
|
(is (= 123 (bb nil "(println 123)")))
|
||||||
(main/parse-opts ["-e" "(println 123)"])))
|
(is (= 123 (bb nil "-e" "(println 123)")))
|
||||||
|
(is (= 123 (bb nil "--eval" "(println 123)")))
|
||||||
(is (= {:expression "(println 123)"}
|
|
||||||
(main/parse-opts ["--eval" "(println 123)"])))
|
|
||||||
|
|
||||||
(testing "distinguish automatically between expression or file name"
|
(testing "distinguish automatically between expression or file name"
|
||||||
(is (= {:expression "(println 123)"
|
(is (= {:result 8080} (bb nil "test/babashka/scripts/tools.cli.bb")))
|
||||||
:command-line-args nil}
|
(is (thrown-with-msg? Exception #"does not exist" (bb nil "foo.clj")))
|
||||||
(main/parse-opts ["(println 123)"])))
|
(is (thrown-with-msg? Exception #"does not exist" (bb nil "-help"))))
|
||||||
|
(is (= "1 2 3" (bb nil "-e" "(require '[clojure.string :as str1])" "-e" "(str1/join \" \" [1 2 3])")))
|
||||||
|
(is (= '("-e" "1") (bb nil "-e" "*command-line-args*" "--" "-e" "1"))))
|
||||||
|
|
||||||
(is (= {:file "src/babashka/main.clj"
|
(deftest print-error-test
|
||||||
:command-line-args nil}
|
(is (thrown-with-msg? Exception #"java.lang.NullPointerException"
|
||||||
(main/parse-opts ["src/babashka/main.clj"])))
|
(bb nil "(subs nil 0 0)"))))
|
||||||
|
|
||||||
(is (= {:expression "does-not-exist"
|
|
||||||
:command-line-args nil}
|
|
||||||
(main/parse-opts ["does-not-exist"])))))
|
|
||||||
|
|
||||||
(deftest main-test
|
(deftest main-test
|
||||||
(testing "-io behaves as identity"
|
(testing "-io behaves as identity"
|
||||||
|
|
@ -166,6 +161,12 @@
|
||||||
(deftest future-test
|
(deftest future-test
|
||||||
(is (= 6 (bb nil "@(future (+ 1 2 3))"))))
|
(is (= 6 (bb nil "@(future (+ 1 2 3))"))))
|
||||||
|
|
||||||
|
(deftest promise-test
|
||||||
|
(is (= :timeout (bb nil "(deref (promise) 1 :timeout)")))
|
||||||
|
(is (= :ok (bb nil "(let [x (promise)]
|
||||||
|
(deliver x :ok)
|
||||||
|
@x)"))))
|
||||||
|
|
||||||
(deftest process-builder-test
|
(deftest process-builder-test
|
||||||
(is (str/includes? (bb nil "
|
(is (str/includes? (bb nil "
|
||||||
(def ls (-> (ProcessBuilder. [\"ls\"]) (.start)))
|
(def ls (-> (ProcessBuilder. [\"ls\"]) (.start)))
|
||||||
|
|
@ -235,8 +236,9 @@
|
||||||
(is (zero? (bb nil "(try (/ 1 0) (catch ArithmeticException _ 0))"))))
|
(is (zero? (bb nil "(try (/ 1 0) (catch ArithmeticException _ 0))"))))
|
||||||
|
|
||||||
(deftest reader-conditionals-test
|
(deftest reader-conditionals-test
|
||||||
(is (= :hello (bb nil "#?(:clj (in-ns 'foo)) (println :hello)")))
|
(is (= :hello (bb nil "#?(:bb :hello :default :bye)")))
|
||||||
(is (= :hello (bb nil "#?(:bb :hello :default :bye)"))))
|
(is (= :hello (bb nil "#?(:clj :hello :bb :bye)")))
|
||||||
|
(is (= [1 2] (bb nil "[1 2 #?@(:bb [] :clj [1])]"))))
|
||||||
|
|
||||||
(deftest csv-test
|
(deftest csv-test
|
||||||
(is (= '(["Adult" "87727"] ["Elderly" "43914"] ["Child" "33411"] ["Adolescent" "29849"]
|
(is (= '(["Adult" "87727"] ["Elderly" "43914"] ["Child" "33411"] ["Adolescent" "29849"]
|
||||||
|
|
@ -250,8 +252,7 @@
|
||||||
(deftest Pattern-test
|
(deftest Pattern-test
|
||||||
(is (= ["1" "2" "3"]
|
(is (= ["1" "2" "3"]
|
||||||
(bb nil "(vec (.split (java.util.regex.Pattern/compile \"f\") \"1f2f3\"))")))
|
(bb nil "(vec (.split (java.util.regex.Pattern/compile \"f\") \"1f2f3\"))")))
|
||||||
(is (= java.util.regex.Pattern/CANON_EQ
|
(is (true? (bb nil "(some? java.util.regex.Pattern/CANON_EQ)"))))
|
||||||
(bb nil "java.util.regex.Pattern/CANON_EQ"))))
|
|
||||||
|
|
||||||
(deftest writer-test
|
(deftest writer-test
|
||||||
(let [tmp-file (java.io.File/createTempFile "bbb" "bbb")
|
(let [tmp-file (java.io.File/createTempFile "bbb" "bbb")
|
||||||
|
|
@ -340,6 +341,20 @@
|
||||||
(is (= "<?xml version=\"1.0\" encoding=\"UTF-8\"?><items><item>1</item><item>2</item></items>"
|
(is (= "<?xml version=\"1.0\" encoding=\"UTF-8\"?><items><item>1</item><item>2</item></items>"
|
||||||
(bb nil "(let [xml (xml/parse-str \"<items><item>1</item><item>2</item></items>\")] (xml/emit-str xml))"))))
|
(bb nil "(let [xml (xml/parse-str \"<items><item>1</item><item>2</item></items>\")] (xml/emit-str xml))"))))
|
||||||
|
|
||||||
|
(deftest uberscript-test
|
||||||
|
(let [tmp-file (java.io.File/createTempFile "uberscript" ".clj")]
|
||||||
|
(.deleteOnExit tmp-file)
|
||||||
|
(is (empty? (bb nil "--uberscript" (.getPath tmp-file) "-e" "(System/exit 1)")))
|
||||||
|
(is (= "(System/exit 1)" (slurp tmp-file)))))
|
||||||
|
|
||||||
|
(deftest unrestricted-access
|
||||||
|
(testing "babashka is allowed to mess with built-in vars"
|
||||||
|
(is (= 1 (bb nil "
|
||||||
|
(def inc2 inc) (alter-var-root #'clojure.core/inc (constantly dec))
|
||||||
|
(let [res (inc 2)]
|
||||||
|
(alter-var-root #'clojure.core/inc (constantly inc2))
|
||||||
|
res)")))))
|
||||||
|
|
||||||
;;;; Scratch
|
;;;; Scratch
|
||||||
|
|
||||||
(comment
|
(comment
|
||||||
|
|
|
||||||
12
test/babashka/profile.clj
Normal file
12
test/babashka/profile.clj
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
(ns babashka.profile
|
||||||
|
(:require [babashka.main :as main]))
|
||||||
|
|
||||||
|
(comment)
|
||||||
|
|
||||||
|
;; clojure -A:profile -e "(prn (loop [val 0 cnt 1000000] (if (pos? cnt) (recur (inc val) (dec cnt)) val)))"
|
||||||
|
|
||||||
|
(require '[clj-async-profiler.core :as prof])
|
||||||
|
|
||||||
|
(defn -main [& options]
|
||||||
|
(prof/profile (apply main/main options))
|
||||||
|
(shutdown-agents))
|
||||||
|
|
@ -4,3 +4,5 @@
|
||||||
(require '[file-var-classpath])
|
(require '[file-var-classpath])
|
||||||
(load-file (io/file "test" "babashka" "scripts" "loaded_by_file_var.bb"))
|
(load-file (io/file "test" "babashka" "scripts" "loaded_by_file_var.bb"))
|
||||||
(println *file*)
|
(println *file*)
|
||||||
|
(defn foo [])
|
||||||
|
(println (:file (meta #'foo)))
|
||||||
|
|
|
||||||
71
test/babashka/test_test.clj
Normal file
71
test/babashka/test_test.clj
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
(ns babashka.test-test
|
||||||
|
(:require
|
||||||
|
[babashka.test-utils :as tu]
|
||||||
|
[clojure.string :as str]
|
||||||
|
[clojure.test :as t :refer [deftest is]]
|
||||||
|
[clojure.java.io :as io]))
|
||||||
|
|
||||||
|
(defn bb [& args]
|
||||||
|
(apply tu/bb nil (map str args)))
|
||||||
|
|
||||||
|
(deftest deftest-test
|
||||||
|
(is (str/includes?
|
||||||
|
(bb "(require '[clojure.test :as t]) (t/deftest foo (t/is (= 4 5))) (foo)")
|
||||||
|
"expected: (= 4 5)\n actual: (not (= 4 5))\n")))
|
||||||
|
|
||||||
|
(deftest run-tests-test
|
||||||
|
(let [output (bb "(require '[clojure.test :as t]) (t/deftest foo (t/is (= 4 5))) (t/run-tests)")]
|
||||||
|
(is (str/includes? output "Testing user"))
|
||||||
|
(is (str/includes? output "{:test 1, :pass 0, :fail 1, :error 0, :type :summary}"))))
|
||||||
|
|
||||||
|
(deftest run-all-tests-test
|
||||||
|
(let [output (bb "
|
||||||
|
(require '[clojure.test :as t])
|
||||||
|
(t/deftest foo (t/is (= 4 5)))
|
||||||
|
(ns foobar)
|
||||||
|
(require '[clojure.test :as t])
|
||||||
|
(t/run-all-tests)")]
|
||||||
|
(is (str/includes? output "Testing user"))
|
||||||
|
(is (str/includes? output "Testing foobar"))
|
||||||
|
(is (str/includes? output "{:test 1, :pass 0, :fail 1, :error 0, :type :summary}"))))
|
||||||
|
|
||||||
|
(deftest fixtures-test
|
||||||
|
(let [output (bb "
|
||||||
|
(require '[clojure.test :as t])
|
||||||
|
(defn once [f] (prn :once-before) (f) (prn :once-after))
|
||||||
|
(defn each [f] (prn :each-before) (f) (prn :each-after))
|
||||||
|
(t/use-fixtures :once once)
|
||||||
|
(t/use-fixtures :each each)
|
||||||
|
(t/deftest foo)
|
||||||
|
(t/deftest bar)
|
||||||
|
(t/run-tests)")]
|
||||||
|
(is (str/includes? output (str/trim "
|
||||||
|
:once-before
|
||||||
|
:each-before
|
||||||
|
:each-after
|
||||||
|
:each-before
|
||||||
|
:each-after
|
||||||
|
:once-after")))))
|
||||||
|
|
||||||
|
(deftest with-test
|
||||||
|
(let [output (bb "
|
||||||
|
(require '[clojure.test :as t])
|
||||||
|
(t/with-test
|
||||||
|
(defn my-function [x y]
|
||||||
|
(+ x y))
|
||||||
|
(t/is (= 4 (my-function 2 2)))
|
||||||
|
(t/is (= 7 (my-function 3 4))))
|
||||||
|
(t/run-tests)")]
|
||||||
|
(is (str/includes? output "Ran 1 tests containing 2 assertions."))))
|
||||||
|
|
||||||
|
(deftest testing-test
|
||||||
|
(is (str/includes? (bb "(require '[clojure.test :as t]) (t/testing \"foo\" (t/is (= 4 5)))")
|
||||||
|
"foo")))
|
||||||
|
|
||||||
|
(deftest are-test
|
||||||
|
(is (str/includes? (bb "(require '[clojure.test :as t]) (t/are [x y] (= x y) 2 (+ 1 2))")
|
||||||
|
"expected: (= 2 (+ 1 2))")))
|
||||||
|
|
||||||
|
(deftest assert-expr-test
|
||||||
|
(is (str/includes? (bb (.getPath (io/file "test-resources" "babashka" "assert_expr.clj")))
|
||||||
|
"3.14 should be roughly 3.141592653589793")))
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
(set! *warn-on-reflection* true)
|
(set! *warn-on-reflection* true)
|
||||||
|
|
||||||
(defn bb-jvm [input & args]
|
(defn bb-jvm [input & args]
|
||||||
|
(reset! main/cp-state nil)
|
||||||
(let [os (java.io.StringWriter.)
|
(let [os (java.io.StringWriter.)
|
||||||
es (java.io.StringWriter.)
|
es (java.io.StringWriter.)
|
||||||
is (when input
|
is (when input
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue