Add cli-matic lib tests
This commit is contained in:
parent
a99e681572
commit
3eace3278f
17 changed files with 2178 additions and 5 deletions
4
deps.edn
4
deps.edn
|
|
@ -123,7 +123,9 @@
|
|||
org.clojure/algo.monads {:mvn/version "0.1.6"}
|
||||
io.lambdaforge/datalog-parser {:mvn/version "0.1.9"}
|
||||
clj-stacktrace/clj-stacktrace {:mvn/version "0.2.8"}
|
||||
clojure-msgpack/clojure-msgpack {:mvn/version "1.2.1"}}
|
||||
clojure-msgpack/clojure-msgpack {:mvn/version "1.2.1"}
|
||||
cli-matic {:git/url "https://github.com/l3nz/cli-matic.git", :git/sha "9cd53ba7336363e3d06650dbad413b6f8b06e471"}
|
||||
cli-matic/cli-matic {:git/url "https://github.com/l3nz/cli-matic.git", :git/sha "9cd53ba7336363e3d06650dbad413b6f8b06e471"}}
|
||||
:classpath-overrides {org.clojure/clojure nil
|
||||
org.clojure/spec.alpha nil}}
|
||||
:clj-nvd
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ borkdude/missing.test.assertions,https://github.com/borkdude/missing.test.assert
|
|||
borkdude/rewrite-edn,https://github.com/borkdude/rewrite-edn
|
||||
camel-snake-kebab/camel-snake-kebab,https://github.com/clj-commons/camel-snake-kebab
|
||||
circleci/bond,https://github.com/circleci/bond
|
||||
cli-matic/cli-matic,https://github.com/l3nz/cli-matic.git
|
||||
clj-commons/clj-yaml,https://github.com/clj-commons/clj-yaml
|
||||
clj-commons/multigrep,https://github.com/clj-commons/multigrep
|
||||
clj-stacktrace/clj-stacktrace,https://github.com/mmcgrana/clj-stacktrace
|
||||
|
|
|
|||
|
|
|
@ -25,7 +25,7 @@
|
|||
org.clojure/data.csv {:mvn/version "1.0.0"},
|
||||
cheshire/cheshire {:mvn/version "5.10.2"}
|
||||
org.clojure/data.xml {:mvn/version "0.2.0-alpha6"}
|
||||
clj-commons/clj-yaml {:mvn/version "0.7.107"}
|
||||
clj-commons/clj-yaml {:mvn/version "0.7.108"}
|
||||
com.cognitect/transit-clj {:mvn/version "1.0.329"}
|
||||
org.clojure/test.check {:mvn/version "1.1.1"}
|
||||
nrepl/bencode {:mvn/version "1.1.0"}
|
||||
|
|
@ -123,7 +123,9 @@
|
|||
org.clojure/algo.monads {:mvn/version "0.1.6"}
|
||||
io.lambdaforge/datalog-parser {:mvn/version "0.1.9"}
|
||||
clj-stacktrace/clj-stacktrace {:mvn/version "0.2.8"}
|
||||
clojure-msgpack/clojure-msgpack {:mvn/version "1.2.1"}}
|
||||
clojure-msgpack/clojure-msgpack {:mvn/version "1.2.1"}
|
||||
cli-matic {:git/url "https://github.com/l3nz/cli-matic.git", :git/sha "9cd53ba7336363e3d06650dbad413b6f8b06e471"}
|
||||
cli-matic/cli-matic {:git/url "https://github.com/l3nz/cli-matic.git", :git/sha "9cd53ba7336363e3d06650dbad413b6f8b06e471"}}
|
||||
:classpath-overrides {org.clojure/clojure nil
|
||||
org.clojure/spec.alpha nil}}
|
||||
:clj-nvd
|
||||
|
|
|
|||
|
|
@ -20,11 +20,21 @@
|
|||
(or (empty? ns-args)
|
||||
(contains? ns-args ns)))
|
||||
|
||||
(defn- filter-vars!
|
||||
[ns filter-fn]
|
||||
(doseq [[_name var] (ns-publics ns)]
|
||||
(when (:test (meta var))
|
||||
(when (not (filter-fn var))
|
||||
(alter-meta! var #(-> %
|
||||
(assoc ::test (:test %))
|
||||
(dissoc :test)))))))
|
||||
|
||||
(defn test-namespaces [& namespaces]
|
||||
(let [namespaces (seq (filter test-namespace? namespaces))]
|
||||
(when (seq namespaces)
|
||||
(doseq [n namespaces]
|
||||
(require n))
|
||||
(require n)
|
||||
(filter-vars! (find-ns n) #(-> % meta :skip-bb not)))
|
||||
(let [m (apply t/run-tests namespaces)]
|
||||
(swap! status (fn [status]
|
||||
(merge-with + status (dissoc m :type))))))))
|
||||
|
|
|
|||
|
|
@ -106,4 +106,5 @@
|
|||
org.clojure/algo.monads {:git-url "https://github.com/clojure/algo.monads", :test-namespaces (clojure.algo.test-monads), :git-sha "3a985b0b099110b1654d568fecf597bc9c8d1ff5"}
|
||||
io.lambdaforge/datalog-parser {:git-url "https://github.com/lambdaforge/datalog-parser", :test-namespaces (datalog.parser.pull-test datalog.parser.test.util datalog.parser.impl-test datalog.parser-test datalog.unparser-test), :git-sha "02d193f397afc3f93da704e7c6c850b194f0e797"}
|
||||
clj-stacktrace/clj-stacktrace {:git-url "https://github.com/mmcgrana/clj-stacktrace", :test-namespaces (clj-stacktrace.repl-test clj-stacktrace.core-test), :git-sha "94dc2dd748710e79800e94b713e167e5dc525717"}
|
||||
clojure-msgpack/clojure-msgpack {:git-url "https://github.com/edma2/clojure-msgpack", :test-namespaces (msgpack.core-check msgpack.core-test), :git-sha "a4bca2cf064a87d9c4a564c634c6ebb65578dad5"}}
|
||||
clojure-msgpack/clojure-msgpack {:git-url "https://github.com/edma2/clojure-msgpack", :test-namespaces (msgpack.core-check msgpack.core-test), :git-sha "a4bca2cf064a87d9c4a564c634c6ebb65578dad5"}
|
||||
cli-matic/cli-matic {:git-url "https://github.com/l3nz/cli-matic.git", :test-namespaces (cli-matic.utils-test cli-matic.presets-test cli-matic.help-gen-test cli-matic.utils-convert-config-test cli-matic.utils-candidates-test cli-matic.core-test cli-matic.utils-v2-test), :git-sha "9cd53ba7336363e3d06650dbad413b6f8b06e471"}}
|
||||
|
|
|
|||
712
test-resources/lib_tests/cli_matic/core_test.cljc
Normal file
712
test-resources/lib_tests/cli_matic/core_test.cljc
Normal file
|
|
@ -0,0 +1,712 @@
|
|||
(ns cli-matic.core-test
|
||||
(:require [clojure.test :refer [is are deftest testing]]
|
||||
[cli-matic.platform :as P]
|
||||
[cli-matic.platform-macros :refer [try-catch-all]]
|
||||
[clojure.spec.alpha :as s]
|
||||
[clojure.string :as str]
|
||||
[cli-matic.core :refer [parse-command-line
|
||||
run-cmd*
|
||||
->RV
|
||||
assert-unique-values
|
||||
assert-cfg-sanity
|
||||
parse-cmds-with-defaults]]
|
||||
[cli-matic.utils-v2 :as U2]))
|
||||
|
||||
(defn cmd_foo [& opts] nil)
|
||||
(defn cmd_bar [& opts] nil)
|
||||
(defn cmd_save_opts [& opts]
|
||||
;(prn "Called" opts)
|
||||
opts)
|
||||
|
||||
(defn cmd_returnstructure [opts]
|
||||
{:myopts opts
|
||||
:somedata "hiyo"})
|
||||
|
||||
(def cli-options
|
||||
[;; First three strings describe a short-option, long-option with optional
|
||||
;; example argument description, and a description. All three are optional
|
||||
;; and positional.
|
||||
["-p" "--port PORT" "Port number"
|
||||
:default 80
|
||||
:parse-fn #(P/parseInt %)
|
||||
:validate [#(< 0 % 0x10000) "Must be a number between 0 and 65536"]]
|
||||
["-H" "--hostname HOST" "Remote host"
|
||||
:default 0
|
||||
;; Specify a string to output in the default column in the options summary
|
||||
;; if the default value's string representation is very ugly
|
||||
:default-desc "localhost"
|
||||
:parse-fn #(P/parseInt %)]
|
||||
;; If no required argument description is given, the option is assumed to
|
||||
;; be a boolean option defaulting to nil
|
||||
[nil "--detach" "Detach from controlling process"]
|
||||
["-v" nil "Verbosity level; may be specified multiple times to increase value"
|
||||
;; If no long-option is specified, an option :id must be given
|
||||
:id :verbosity
|
||||
:default 0
|
||||
;; Use assoc-fn to create non-idempotent options
|
||||
:assoc-fn (fn [m k _] (update-in m [k] inc))]
|
||||
;; A boolean option that can explicitly be set to false
|
||||
["-d" "--[no-]daemon" "Daemonize the process" :default true]
|
||||
["-h" "--help"]])
|
||||
|
||||
(def SIMPLE-SUBCOMMAND-CFG
|
||||
{:app {:command "dummy"
|
||||
:description "I am some command"
|
||||
:version "0.1.2"}
|
||||
:global-opts [{:option "aa" :as "A" :type :int}
|
||||
{:option "bb" :as "B" :type :int}]
|
||||
:commands [{:command "foo" :short "f"
|
||||
:description "I am function foo"
|
||||
:opts [{:option "cc" :as "C" :type :int}
|
||||
{:option "dd" :as "D" :type :int}]
|
||||
:runs cmd_foo}
|
||||
|
||||
; another one
|
||||
{:command "bar"
|
||||
:description "I am function bar"
|
||||
:opts [{:option "ee" :as "E" :type :int}
|
||||
{:option "ff" :as "F" :type :int}]
|
||||
:runs cmd_bar}
|
||||
|
||||
; this one to check return structs
|
||||
{:command "rets"
|
||||
:description "I return a structure"
|
||||
:opts []
|
||||
:runs cmd_returnstructure}]})
|
||||
|
||||
(def SIMPLE-SUBCOMMAND-CFG-v2
|
||||
(U2/convert-config-v1->v2 SIMPLE-SUBCOMMAND-CFG))
|
||||
|
||||
(deftest simple-subcommand
|
||||
(testing "A simple subcommand - v2"
|
||||
|
||||
;; Normal subcomand
|
||||
(is (= (parse-command-line
|
||||
["--bb" "1" "foo" "--cc" "2" "--dd" "3"]
|
||||
SIMPLE-SUBCOMMAND-CFG-v2)
|
||||
|
||||
{:commandline {:bb 1 :cc 2 :dd 3 :_arguments []}
|
||||
:subcommand "dummy foo"
|
||||
:subcommand-path ["dummy" "foo"]
|
||||
:parse-errors :NONE
|
||||
:error-text ""
|
||||
:subcommand-def {:command "foo"
|
||||
:short "f"
|
||||
:description "I am function foo"
|
||||
:opts [{:as "C"
|
||||
:option "cc"
|
||||
:type :int}
|
||||
{:as "D"
|
||||
:option "dd"
|
||||
:type :int}]
|
||||
:runs cmd_foo}}))
|
||||
|
||||
|
||||
|
||||
;; short subcommand
|
||||
|
||||
|
||||
(is (= (parse-command-line
|
||||
["--bb" "1" "f" "--cc" "2" "--dd" "3"]
|
||||
SIMPLE-SUBCOMMAND-CFG-v2)
|
||||
|
||||
{:commandline {:bb 1 :cc 2 :dd 3 :_arguments []}
|
||||
:subcommand "dummy foo"
|
||||
:subcommand-path ["dummy" "foo"]
|
||||
:parse-errors :NONE
|
||||
:error-text ""
|
||||
:subcommand-def {:command "foo"
|
||||
:short "f"
|
||||
:description "I am function foo"
|
||||
:opts [{:as "C"
|
||||
:option "cc"
|
||||
:type :int}
|
||||
{:as "D"
|
||||
:option "dd"
|
||||
:type :int}]
|
||||
:runs cmd_foo}}))
|
||||
|
||||
;; unknown subcommand
|
||||
(is (= (parse-command-line
|
||||
["--bb" "1" "unknown" "--cc" "2" "--dd" "3"]
|
||||
SIMPLE-SUBCOMMAND-CFG-v2)
|
||||
|
||||
{:commandline {}
|
||||
:error-text "Unknown sub-command: 'dummy unknown'."
|
||||
:parse-errors :ERR-UNKNOWN-SUBCMD
|
||||
:subcommand "dummy unknown"
|
||||
:subcommand-path ["dummy" "unknown"]
|
||||
:subcommand-def nil}))))
|
||||
|
||||
(deftest run-examples
|
||||
(testing "Some real-life behavior for our SIMPLE case - v2"
|
||||
(are [i o]
|
||||
(= (run-cmd* SIMPLE-SUBCOMMAND-CFG-v2 i) o)
|
||||
|
||||
; no parameters - displays cmd help
|
||||
[]
|
||||
(->RV -1 :ERR-NO-SUBCMD :HELP-GLOBAL ["dummy"] "No sub-command specified.")
|
||||
|
||||
["x"]
|
||||
(->RV -1 :ERR-UNKNOWN-SUBCMD :HELP-GLOBAL ["dummy" "x"] "Unknown sub-command: 'dummy x'.")
|
||||
|
||||
["--lippa" "foo"]
|
||||
(->RV -1 :ERR-PARMS-GLOBAL :HELP-GLOBAL ["dummy"] "Global option error: Unknown option: \"--lippa\"")
|
||||
|
||||
; help globale
|
||||
["-?"]
|
||||
(->RV 0 :OK :HELP-GLOBAL ["dummy"] nil)
|
||||
|
||||
["--help"]
|
||||
(->RV 0 :OK :HELP-GLOBAL ["dummy"] nil)
|
||||
|
||||
; help sub-commands (incl short version)
|
||||
["foo" "-?"]
|
||||
(->RV 0 :OK :HELP-SUBCMD ["dummy" "foo"] nil)
|
||||
|
||||
["bar" "--help"]
|
||||
(->RV 0 :OK :HELP-SUBCMD ["dummy" "bar"] nil)
|
||||
|
||||
["f" "-?"]
|
||||
(->RV 0 :OK :HELP-SUBCMD ["dummy" "foo"] nil)
|
||||
|
||||
["rets"]
|
||||
(->RV 0 :OK nil nil nil))))
|
||||
|
||||
(def MANDATORY-SUBCOMMAND-CFG
|
||||
{:app {:command "dummy"
|
||||
:description "I am some command"
|
||||
:version "0.1.2"}
|
||||
:global-opts [{:option "aa" :as "A" :type :int :default :present}
|
||||
{:option "bb" :as "B" :type :int}]
|
||||
:commands [{:command "foo" :short "f"
|
||||
:description "I am function foo"
|
||||
:opts [{:option "cc" :as "C" :type :int :default :present}
|
||||
{:option "dd" :as "D" :type :int}]
|
||||
:runs cmd_foo}]})
|
||||
|
||||
(def MANDATORY-SUBCOMMAND-CFG-v2
|
||||
(U2/convert-config-v1->v2 MANDATORY-SUBCOMMAND-CFG))
|
||||
|
||||
(deftest check-mandatory-options
|
||||
(testing "Some real-life behavior with mandatory options"
|
||||
(are [i o]
|
||||
(= (run-cmd* MANDATORY-SUBCOMMAND-CFG-v2 i) o)
|
||||
|
||||
; no parameters - displays cmd help
|
||||
[]
|
||||
(->RV -1 :ERR-NO-SUBCMD :HELP-GLOBAL ["dummy"] "No sub-command specified.")
|
||||
|
||||
["x"]
|
||||
(->RV -1 :ERR-PARMS-GLOBAL :HELP-GLOBAL ["dummy"] "Global option error: Missing option: aa")
|
||||
|
||||
["--lippa" "foo"]
|
||||
(->RV -1 :ERR-PARMS-GLOBAL :HELP-GLOBAL ["dummy"] "Global option error: Unknown option: \"--lippa\"")
|
||||
|
||||
; help globale
|
||||
["-?"]
|
||||
(->RV 0 :OK :HELP-GLOBAL ["dummy"] nil)
|
||||
|
||||
; help sub-commands (incl short version)
|
||||
["--aa" "1" "foo" "-?"]
|
||||
(->RV 0 :OK :HELP-SUBCMD ["dummy" "foo"] nil)
|
||||
|
||||
; error no global cmd
|
||||
["foo" "--cc" "1"]
|
||||
(->RV -1 :ERR-PARMS-GLOBAL :HELP-GLOBAL ["dummy"] "Global option error: Missing option: aa")
|
||||
|
||||
; error no sub cmd
|
||||
["--aa" "1" "foo" "--dd" "1"]
|
||||
(->RV -1 :ERR-PARMS-SUBCMD :HELP-SUBCMD ["dummy" "foo"] "Option error: Missing option: cc")
|
||||
|
||||
; works
|
||||
["--aa" "1" "foo" "--cc" "1"]
|
||||
(->RV 0 :OK nil nil nil))))
|
||||
|
||||
|
||||
; Problems
|
||||
; --------
|
||||
;
|
||||
; Types
|
||||
;
|
||||
; lein run -m cli-matic.toycalc -- add --a x
|
||||
; ** ERROR: **
|
||||
; Error:
|
||||
; and nothing else
|
||||
|
||||
|
||||
;; VALIDATION OF CONFIGURATION
|
||||
;;
|
||||
|
||||
|
||||
(deftest check-unique-options
|
||||
(testing "Unique options"
|
||||
(are [i o]
|
||||
(= (try-catch-all
|
||||
(apply assert-unique-values i)
|
||||
(fn [_] :ERR))
|
||||
o)
|
||||
|
||||
; empty
|
||||
["a" [] :x]
|
||||
nil
|
||||
|
||||
; ok
|
||||
["pippo"
|
||||
[{:option "a" :as "Parameter A" :type :int :default 0}
|
||||
{:option "b" :as "Parameter B" :type :int :default 0}]
|
||||
:option]
|
||||
nil
|
||||
|
||||
; dupe
|
||||
["pippo"
|
||||
[{:option "a" :as "Parameter A" :type :int :default 0}
|
||||
{:option "a" :as "Parameter B" :type :int :default 0}]
|
||||
:option]
|
||||
:ERR)))
|
||||
|
||||
(comment
|
||||
;;; TO DO
|
||||
|
||||
(deftest check-cfg-format
|
||||
(testing "Cfg format"
|
||||
(are [i o]
|
||||
(= o
|
||||
(try-catch-all
|
||||
(-> i
|
||||
U2/add-setup-defaults-v1
|
||||
assert-cfg-sanity)
|
||||
(fn [_]
|
||||
;(prn e)
|
||||
:ERR)))
|
||||
|
||||
;; OK
|
||||
{:app {:command "toycalc" :description "A" :version "0.0.1"}
|
||||
|
||||
:global-opts [{:option "base" :as "T" :type :int :default 10}]
|
||||
|
||||
:commands [{:command "add" :description "Adds" :runs identity
|
||||
:opts [{:option "a" :as "Addendum 1" :type :int}
|
||||
{:option "b" :as "Addendum 2" :type :int :default 0}]}]}
|
||||
nil
|
||||
|
||||
;; No global options - still OK (bug #35)
|
||||
{:app {:command "toycalc" :description "A" :version "0.0.1"}
|
||||
:commands [{:command "add" :description "Adds" :runs identity
|
||||
:opts [{:option "a" :as "Addendum 1" :type :int}
|
||||
{:option "b" :as "Addendum 2" :type :int :default 0}]}]}
|
||||
nil
|
||||
|
||||
;; double in global
|
||||
{:app {:command "toycalc" :description "A" :version "0.0.1"}
|
||||
|
||||
:global-opts [{:option "base" :as "T" :type :int :default 10}
|
||||
{:option "base" :as "X" :type :int :default 10}]
|
||||
|
||||
:commands [{:command "add" :description "Adds" :runs identity
|
||||
:opts [{:option "a" :as "Addendum 1" :type :int}
|
||||
{:option "b" :as "Addendum 2" :type :int :default 0}]}]}
|
||||
:ERR
|
||||
|
||||
;; double in specific
|
||||
{:app {:command "toycalc" :description "A" :version "0.0.1"}
|
||||
|
||||
:global-opts [{:option "base" :as "T" :type :int :default 10}]
|
||||
|
||||
:commands [{:command "add" :description "Adds" :runs identity
|
||||
:opts [{:option "a" :short "q" :as "Addendum 1" :type :int}
|
||||
{:option "b" :short "q" :as "Addendum 2" :type :int :default 0}]}]}
|
||||
:ERR
|
||||
|
||||
;; positional subcmds in global opts
|
||||
{:app {:command "toycalc" :description "A" :version "0.0.1"}
|
||||
|
||||
:global-opts [{:option "base" :short 0 :as "T" :type :int :default 10}]
|
||||
|
||||
:commands [{:command "add" :description "Adds" :runs identity
|
||||
:opts [{:option "a" :short "q" :as "Addendum 1" :type :int}
|
||||
{:option "b" :short "d" :as "Addendum 2" :type :int :default 0}]}]}
|
||||
:ERR)))
|
||||
;;;;
|
||||
)
|
||||
|
||||
(def POSITIONAL-SUBCOMMAND-CFG
|
||||
{:app {:command "dummy"
|
||||
:description "I am some command"
|
||||
:version "0.1.2"}
|
||||
:global-opts [{:option "aa" :as "A" :type :int :default :present}
|
||||
{:option "bb" :as "B" :type :int}]
|
||||
:commands [{:command "foo" :short "f"
|
||||
:description "I am function foo"
|
||||
:opts [{:option "cc" :short 0 :as "C" :type :int :default :present}
|
||||
{:option "dd" :as "D" :type :int}
|
||||
{:option "ee" :short 1 :as "E" :type :int}]
|
||||
:runs cmd_save_opts}]})
|
||||
|
||||
(def POSITIONAL-SUBCOMMAND-CFG-v2
|
||||
(U2/convert-config-v1->v2 POSITIONAL-SUBCOMMAND-CFG))
|
||||
|
||||
(deftest check-positional-options
|
||||
(testing "Some real-life behavior with mandatory options"
|
||||
(are [i o]
|
||||
(= (select-keys
|
||||
(parse-command-line i POSITIONAL-SUBCOMMAND-CFG-v2)
|
||||
[:commandline :error-text]) o)
|
||||
|
||||
;; a simple case
|
||||
["--aa" "10" "foo" "1" "2"]
|
||||
{:commandline {:_arguments ["1" "2"]
|
||||
:aa 10
|
||||
:cc 1
|
||||
:ee 2}
|
||||
:error-text ""}
|
||||
|
||||
;; positional arg does not exist but is default present
|
||||
["--aa" "10" "foo"]
|
||||
{:commandline {}
|
||||
:error-text "Missing option: cc"}
|
||||
|
||||
;; positional arg does not exist and it is not default present
|
||||
["--aa" "10" "foo" "1"]
|
||||
{:commandline {:_arguments ["1"]
|
||||
:aa 10
|
||||
:cc 1}
|
||||
:error-text ""})))
|
||||
|
||||
(defn env-helper [s]
|
||||
(get {"VARA" "10"
|
||||
"VARB" "HELLO"} s))
|
||||
|
||||
(deftest check-environmental-vars
|
||||
(testing "Parsing with env - global opts"
|
||||
(are [opts cmdline result]
|
||||
(= (dissoc (parse-cmds-with-defaults opts cmdline true env-helper) :summary) result)
|
||||
|
||||
;; a simple case - no env vars
|
||||
[{:option "cc" :short 0 :as "C" :type :int :default :present}
|
||||
{:option "dd" :as "D" :type :string}]
|
||||
|
||||
["--cc" "0" "pippo" "pluto"]
|
||||
|
||||
{:arguments ["pippo" "pluto"]
|
||||
:errors nil
|
||||
:options {:cc 0}}
|
||||
|
||||
;; a simple case - absent, with env set, integer
|
||||
[{:option "cc" :short 0 :as "C" :type :int :default :present}
|
||||
{:option "dd" :as "D" :type :int :env "VARA"}]
|
||||
|
||||
["--cc" "0" "pippo" "pluto"]
|
||||
|
||||
{:arguments ["pippo" "pluto"]
|
||||
:errors nil
|
||||
:options {:cc 0 :dd 10}}
|
||||
|
||||
;; present, with env set, integer
|
||||
[{:option "cc" :short 0 :as "C" :type :int :default :present}
|
||||
{:option "dd" :as "D" :type :int :env "VARA"}]
|
||||
|
||||
["--cc" "0" "--dd" "23" "pippo" "pluto"]
|
||||
|
||||
{:arguments ["pippo" "pluto"]
|
||||
:errors nil
|
||||
:options {:cc 0 :dd 23}}
|
||||
|
||||
;; absent, with env missing, integer
|
||||
[{:option "cc" :short 0 :as "C" :type :int :default :present}
|
||||
{:option "dd" :as "D" :type :int :env "NO-VARA"}]
|
||||
|
||||
["--cc" "0" "pippo" "pluto"]
|
||||
|
||||
{:arguments ["pippo" "pluto"]
|
||||
:errors nil
|
||||
:options {:cc 0}})))
|
||||
|
||||
; =======================================================================
|
||||
; ======== S P E C S ==========
|
||||
; =======================================================================
|
||||
|
||||
; We add a stupid spec check
|
||||
; Specs are checked after parsing, both on parameters and globally.
|
||||
; if presents, specs are checked
|
||||
|
||||
(s/def ::ODD-NUMBER odd?)
|
||||
|
||||
(s/def ::GENERAL-SPEC-FOO #(= 99 (:ee %)))
|
||||
|
||||
(def SPEC-CFG
|
||||
{:app {:command "dummy"
|
||||
:description "I am some command"
|
||||
:version "0.1.2"}
|
||||
:global-opts [{:option "aa" :as "A" :type :int :default :present :spec ::ODD-NUMBER}
|
||||
{:option "bb" :as "B" :type :int :spec ::ODD-NUMBER}]
|
||||
:commands [{:command "foo"
|
||||
:short "f"
|
||||
:description "I am function foo"
|
||||
:opts [{:option "cc" :short 0 :as "C" :type :int :default :present}
|
||||
{:option "dd" :as "D" :type :int :spec ::ODD-NUMBER}
|
||||
{:option "ee" :short 1 :as "E" :type :int :spec ::ODD-NUMBER}]
|
||||
:spec ::GENERAL-SPEC-FOO
|
||||
:runs cmd_save_opts}]})
|
||||
(def SPEC-CFG-v2
|
||||
(U2/convert-config-v1->v2 SPEC-CFG))
|
||||
|
||||
(defn keep-1st-line-stderr
|
||||
"To avoid issues with expound changing messages, we remove all
|
||||
but the first line in stderr for testing."
|
||||
[{:keys [stderr] :as all}]
|
||||
|
||||
(let [nv (if (and (vector? stderr) (pos? (count stderr)))
|
||||
(let [lines (str/split-lines (first stderr))
|
||||
vec-of-fline [(first lines)]]
|
||||
|
||||
vec-of-fline) stderr)]
|
||||
|
||||
(assoc all
|
||||
:stderr nv)))
|
||||
|
||||
;; ------------
|
||||
|
||||
|
||||
(deftest check-specs
|
||||
(are [i o]
|
||||
(= (keep-1st-line-stderr (run-cmd* SPEC-CFG-v2 i)) o)
|
||||
|
||||
; all of the should pass
|
||||
["--aa" "3" "--bb" "7" "foo" "--cc" "2" "--dd" "3" "--ee" "99"]
|
||||
(->RV 0 :OK nil nil [])
|
||||
|
||||
; aa (global) not odd
|
||||
["--aa" "2" "--bb" "7" "foo" "--cc" "2" "--dd" "3" "--ee" "99"]
|
||||
(->RV -1 :ERR-PARMS-GLOBAL :HELP-GLOBAL ["dummy"] ["Global option error: Spec failure for global option 'aa'"])
|
||||
|
||||
; bb does not exist but it's not mandatory
|
||||
["--aa" "3" "foo" "--cc" "2" "--dd" "3" "--ee" "99"]
|
||||
(->RV 0 :OK nil nil nil)
|
||||
|
||||
; dd (local)
|
||||
["--aa" "3" "--bb" "7" "foo" "--cc" "2" "--dd" "4" "--ee" "99"]
|
||||
(->RV -1 :ERR-PARMS-SUBCMD :HELP-SUBCMD ["dummy" "foo"] ["Option error: Spec failure for option 'dd'"])
|
||||
|
||||
; dd is missing - spec not checked - bug #105
|
||||
["--aa" "3" "--bb" "7" "foo" "--cc" "2" "--ee" "99"]
|
||||
(->RV 0 :OK nil nil nil)
|
||||
|
||||
; ee non 99 (validazione globale subcmd)
|
||||
["--aa" "3" "--bb" "7" "foo" "--cc" "2" "--dd" "5" "--ee" "97"]
|
||||
(->RV -1 :ERR-PARMS-SUBCMD :HELP-SUBCMD ["dummy" "foo"] ["Option error: Spec failure for subcommand 'dummy foo'"])))
|
||||
|
||||
;;;;;
|
||||
|
||||
|
||||
; =================================================================
|
||||
;
|
||||
; =================================================================
|
||||
|
||||
|
||||
(def SETS-CFG
|
||||
{:app {:command "dummy"
|
||||
:description "I am some command"
|
||||
:version "0.1.2"}
|
||||
:global-opts []
|
||||
:commands [{:command "foo" :short "f"
|
||||
:description "I am function foo"
|
||||
:opts [{:option "kw" :as "blabla" :type #{:a :b :zebrafuffa}}]
|
||||
:runs cmd_save_opts}]})
|
||||
|
||||
(def SETS-CFG-v2
|
||||
(U2/convert-config-v1->v2 SETS-CFG))
|
||||
|
||||
(deftest check-sets
|
||||
(are [i o]
|
||||
(= (run-cmd* SETS-CFG-v2 i) o)
|
||||
|
||||
; all of the should pass
|
||||
["foo" "--kw" "a"]
|
||||
(->RV 0 :OK nil nil [])
|
||||
|
||||
["foo" "--kw" "B"]
|
||||
(->RV 0 :OK nil nil [])
|
||||
|
||||
["foo" "--kw" "zebrafufa"]
|
||||
{:help :HELP-SUBCMD
|
||||
:retval -1
|
||||
:status :ERR-PARMS-SUBCMD
|
||||
:stderr ["Option error: Error while parsing option \"--kw zebrafufa\": clojure.lang.ExceptionInfo: Value 'zebrafufa' not allowed. Did you mean ':zebrafuffa'? {}"]
|
||||
:subcmd ["dummy" "foo"]}))
|
||||
|
||||
|
||||
; =================================================================
|
||||
;
|
||||
; =================================================================
|
||||
|
||||
|
||||
(def FLAGS-CFG
|
||||
{:app {:command "dummy"
|
||||
:description "I am some command"
|
||||
:version "0.1.2"}
|
||||
:global-opts []
|
||||
:commands [{:command "foo" :short "f"
|
||||
:description "I am function foo"
|
||||
:opts [{:option "bar" :as "bar" :type :with-flag :default false}
|
||||
{:option "flag" :as "flag" :type :flag :default false}]
|
||||
:runs cmd_save_opts}]})
|
||||
|
||||
(def FLAGS-CFG-v2
|
||||
(U2/convert-config-v1->v2 FLAGS-CFG))
|
||||
|
||||
(deftest check-flags
|
||||
(are [input expected]
|
||||
(= expected (run-cmd* FLAGS-CFG-v2 input))
|
||||
|
||||
["foo" "--bar"]
|
||||
(->RV 0 :OK nil nil [])
|
||||
|
||||
["foo" "--no-bar"]
|
||||
(->RV 0 :OK nil nil [])
|
||||
|
||||
["foo" "--flag" "Y"]
|
||||
(->RV 0 :OK nil nil [])
|
||||
|
||||
["foo" "--flag" "Yes"]
|
||||
(->RV 0 :OK nil nil [])
|
||||
|
||||
["foo" "--flag" "On"]
|
||||
(->RV 0 :OK nil nil [])
|
||||
|
||||
["foo" "--flag" "T"]
|
||||
(->RV 0 :OK nil nil [])
|
||||
|
||||
["foo" "--flag" "True"]
|
||||
(->RV 0 :OK nil nil [])
|
||||
|
||||
["foo" "--flag" "1"]
|
||||
(->RV 0 :OK nil nil [])
|
||||
|
||||
["foo" "--flag" "N"]
|
||||
(->RV 0 :OK nil nil [])
|
||||
|
||||
["foo" "--flag" "No"]
|
||||
(->RV 0 :OK nil nil [])
|
||||
|
||||
["foo" "--flag" "Off"]
|
||||
(->RV 0 :OK nil nil [])
|
||||
|
||||
["foo" "--flag" "F"]
|
||||
(->RV 0 :OK nil nil [])
|
||||
|
||||
["foo" "--flag" "False"]
|
||||
(->RV 0 :OK nil nil [])
|
||||
|
||||
["foo" "--flag" "false"]
|
||||
(->RV 0 :OK nil nil [])
|
||||
|
||||
["foo" "--flag" "0"]
|
||||
(->RV 0 :OK nil nil [])
|
||||
|
||||
["foo" "--flag" "2"]
|
||||
(->RV -1 :ERR-PARMS-SUBCMD :HELP-SUBCMD ["dummy" "foo"]
|
||||
"Option error: Error while parsing option \"--flag 2\": clojure.lang.ExceptionInfo: Unsupported flag value {:flag \"2\"}")
|
||||
|
||||
["foo" "--bar" "--flag" "Y"]
|
||||
(->RV 0 :OK nil nil [])
|
||||
|
||||
["foo" "--no-bar" "--flag" "0"]
|
||||
(->RV 0 :OK nil nil [])))
|
||||
|
||||
(deftest check-flags-more-complex
|
||||
(are [input expected]
|
||||
(= expected (parse-command-line input FLAGS-CFG-v2))
|
||||
|
||||
["foo" "--bar"]
|
||||
{:commandline {:_arguments [] :bar true :flag false}
|
||||
:error-text ""
|
||||
:parse-errors :NONE
|
||||
:subcommand "dummy foo"
|
||||
:subcommand-path ["dummy" "foo"]
|
||||
:subcommand-def {:command "foo"
|
||||
:description "I am function foo"
|
||||
:opts [{:as "bar"
|
||||
:default false
|
||||
:option "bar"
|
||||
:type :with-flag}
|
||||
{:as "flag"
|
||||
:default false
|
||||
:option "flag"
|
||||
:type :flag}]
|
||||
:runs cmd_save_opts
|
||||
:short "f"}}
|
||||
|
||||
["foo" "--no-bar"]
|
||||
{:commandline {:_arguments [] :bar false :flag false}
|
||||
:error-text ""
|
||||
:parse-errors :NONE
|
||||
:subcommand "dummy foo"
|
||||
:subcommand-path ["dummy" "foo"]
|
||||
:subcommand-def {:command "foo"
|
||||
:description "I am function foo"
|
||||
:opts [{:as "bar"
|
||||
:default false
|
||||
:option "bar"
|
||||
:type :with-flag}
|
||||
{:as "flag"
|
||||
:default false
|
||||
:option "flag"
|
||||
:type :flag}]
|
||||
:runs cmd_save_opts
|
||||
:short "f"}}
|
||||
|
||||
["foo" "--no-bar" "--flag" "Y"]
|
||||
{:commandline {:_arguments [] :bar false :flag true}
|
||||
:error-text ""
|
||||
:parse-errors :NONE
|
||||
:subcommand "dummy foo"
|
||||
:subcommand-path ["dummy" "foo"]
|
||||
:subcommand-def {:command "foo"
|
||||
:description "I am function foo"
|
||||
:opts [{:as "bar"
|
||||
:default false
|
||||
:option "bar"
|
||||
:type :with-flag}
|
||||
{:as "flag"
|
||||
:default false
|
||||
:option "flag"
|
||||
:type :flag}]
|
||||
:runs cmd_save_opts
|
||||
:short "f"}}
|
||||
|
||||
["foo" "--no-bar" "--flag" "Off"]
|
||||
{:commandline {:_arguments [] :bar false :flag false}
|
||||
:error-text ""
|
||||
:parse-errors :NONE
|
||||
:subcommand "dummy foo"
|
||||
:subcommand-path ["dummy" "foo"]
|
||||
:subcommand-def {:command "foo"
|
||||
:description "I am function foo"
|
||||
:opts [{:as "bar"
|
||||
:default false
|
||||
:option "bar"
|
||||
:type :with-flag}
|
||||
{:as "flag"
|
||||
:default false
|
||||
:option "flag"
|
||||
:type :flag}]
|
||||
:runs cmd_save_opts
|
||||
:short "f"}}))
|
||||
|
||||
(deftest spec-on-options-bug105
|
||||
(let [CFG {:command "toycalc"
|
||||
:description "A command-line toy adder"
|
||||
:version "0.0.1"
|
||||
:opts [{:as "Parameter A"
|
||||
:default 0
|
||||
:option "a"
|
||||
:type :int}
|
||||
{:as "Parameter B"
|
||||
:default 0
|
||||
:option "b"
|
||||
:type :int}]
|
||||
:runs cmd_foo}]))
|
||||
|
||||
|
||||
|
||||
3
test-resources/lib_tests/cli_matic/edn_simple.edn
Normal file
3
test-resources/lib_tests/cli_matic/edn_simple.edn
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{:list [1 2 "hi"]
|
||||
:intval 100
|
||||
:strval "good"}
|
||||
406
test-resources/lib_tests/cli_matic/help_gen_test.cljc
Normal file
406
test-resources/lib_tests/cli_matic/help_gen_test.cljc
Normal file
|
|
@ -0,0 +1,406 @@
|
|||
(ns cli-matic.help-gen-test
|
||||
(:require [clojure.test :refer [is are deftest testing]]
|
||||
#?(:clj [cli-matic.platform-macros :refer [try-catch-all]]
|
||||
:cljs [cli-matic.platform-macros :refer-macros [try-catch-all]])
|
||||
[cli-matic.help-gen :refer [generate-possible-mistypes
|
||||
generate-section
|
||||
generate-a-command
|
||||
generate-global-help
|
||||
generate-subcmd-help]]
|
||||
[cli-matic.utils-v2 :as U2]))
|
||||
|
||||
(deftest generate-possible-mistypes-test
|
||||
|
||||
(are [w c a o]
|
||||
(= o (generate-possible-mistypes w c a))
|
||||
|
||||
;
|
||||
"purchasse"
|
||||
["purchase" "purchasing" "add" "remove"]
|
||||
[nil "PP" "A" "R"]
|
||||
["purchase" "purchasing"]))
|
||||
|
||||
(deftest generate-section-test
|
||||
|
||||
(are [t l o]
|
||||
(= o (-> (generate-section t l)
|
||||
flatten
|
||||
vec))
|
||||
|
||||
; full
|
||||
"tit"
|
||||
["a" "b"]
|
||||
["tit:" " a" " b" ""]
|
||||
|
||||
; empty
|
||||
"tittel"
|
||||
[]
|
||||
[]))
|
||||
|
||||
(deftest generate-a-command-test
|
||||
|
||||
(are [c s d o]
|
||||
(= o (generate-a-command {:command c :short s :description d}))
|
||||
|
||||
; full
|
||||
"tit" "t" ["a" "b"]
|
||||
" tit, t a"
|
||||
|
||||
; linea singola
|
||||
"tit" "t" "singel"
|
||||
" tit, t singel"
|
||||
|
||||
; no description
|
||||
"tit" "t" []
|
||||
" tit, t ?"))
|
||||
|
||||
(defn dummy-cmd [_])
|
||||
|
||||
(def CONFIGURATION-TOYCALC
|
||||
{:app {:command "toycalc"
|
||||
:description "A command-line toy calculator"
|
||||
:version "0.0.1"}
|
||||
:global-opts [{:option "base"
|
||||
:as "The number base for output"
|
||||
:type :int
|
||||
:default 10}]
|
||||
:commands [{:command "add" :short "a"
|
||||
:description ["Adds two numbers together"
|
||||
""
|
||||
"Looks great, doesn't it?"]
|
||||
:opts [{:option "a1" :short "a" :env "AA" :as "First addendum" :type :int :default 0}
|
||||
{:option "a2" :short "b" :as "Second addendum" :type :int :default 0}]
|
||||
:runs dummy-cmd}
|
||||
{:command "sub" :short "s"
|
||||
:description "Subtracts parameter B from A"
|
||||
:opts [{:option "pa" :short "a" :as "Parameter A" :type :int :default 0}
|
||||
{:option "pb" :short "b" :as "Parameter B" :type :int :default 0}]
|
||||
:runs dummy-cmd}]})
|
||||
|
||||
(def CONFIGURATION-TOYCALC-v2
|
||||
(U2/convert-config-v1->v2 CONFIGURATION-TOYCALC))
|
||||
|
||||
(def CONFIGURATION-TOYCALC-NESTED
|
||||
{:command "toycalc"
|
||||
:description "A command-line toy calculator"
|
||||
:version "0.0.1"
|
||||
:opts [{:as "The number base for output"
|
||||
:default 10
|
||||
:option "base"
|
||||
:type :int}]
|
||||
:subcommands [{:command "add"
|
||||
:short "a"
|
||||
:description "Adds two numbers together"
|
||||
:version "3.3.3"
|
||||
:examples ["Example One" "Example Two"]
|
||||
:opts [{:as "Addendum 1"
|
||||
:option "a"
|
||||
:type :int}
|
||||
{:as "Addendum 2"
|
||||
:default 0
|
||||
:option "b"
|
||||
:type :int}]
|
||||
:runs dummy-cmd}
|
||||
{:command "subc"
|
||||
:description "Subtracts parameter B from A"
|
||||
:examples "Just one example"
|
||||
:version "1.2.3"
|
||||
:opts [{:as "Parameter q"
|
||||
:default 0
|
||||
:option "q"
|
||||
:type :int}]
|
||||
:subcommands [{:command "sub"
|
||||
:description "Subtracts"
|
||||
:opts [{:as "Parameter A to subtract from"
|
||||
:default 0
|
||||
:option "a"
|
||||
:type :int}
|
||||
{:as "Parameter B"
|
||||
:default 0
|
||||
:option "b"
|
||||
:type :int}]
|
||||
:runs dummy-cmd}]}]})
|
||||
|
||||
(deftest generate-global-help-test
|
||||
|
||||
(is
|
||||
(= ["NAME:"
|
||||
" toycalc - A command-line toy calculator"
|
||||
""
|
||||
"USAGE:"
|
||||
" toycalc [global-options] command [command options] [arguments...]"
|
||||
""
|
||||
"VERSION:"
|
||||
" 0.0.1"
|
||||
""
|
||||
"COMMANDS:"
|
||||
" add, a Adds two numbers together"
|
||||
" sub, s Subtracts parameter B from A"
|
||||
""
|
||||
"GLOBAL OPTIONS:"
|
||||
" --base N 10 The number base for output"
|
||||
" -?, --help"
|
||||
""]
|
||||
(generate-global-help CONFIGURATION-TOYCALC-v2 ["toycalc"]))))
|
||||
|
||||
(deftest generate-global-help-test-nested
|
||||
|
||||
(is
|
||||
(= ["NAME:"
|
||||
" toycalc - A command-line toy calculator"
|
||||
""
|
||||
"USAGE:"
|
||||
" toycalc [global-options] command [command options] [arguments...]"
|
||||
""
|
||||
"VERSION:"
|
||||
" 0.0.1"
|
||||
""
|
||||
"COMMANDS:"
|
||||
" add, a Adds two numbers together"
|
||||
" subc Subtracts parameter B from A"
|
||||
""
|
||||
"GLOBAL OPTIONS:"
|
||||
" --base N 10 The number base for output"
|
||||
" -?, --help"
|
||||
""]
|
||||
(generate-global-help CONFIGURATION-TOYCALC-NESTED ["toycalc"])))
|
||||
|
||||
(is
|
||||
(= ["NAME:"
|
||||
" toycalc - A command-line toy calculator"
|
||||
""
|
||||
"USAGE:"
|
||||
" toycalc [global-options] command [command options] [arguments...]"
|
||||
""
|
||||
"VERSION:"
|
||||
" 0.0.1"
|
||||
""
|
||||
"COMMANDS:"
|
||||
" add, a Adds two numbers together"
|
||||
" subc Subtracts parameter B from A"
|
||||
""
|
||||
"GLOBAL OPTIONS:"
|
||||
" --base N 10 The number base for output"
|
||||
" -?, --help"
|
||||
""]
|
||||
(generate-global-help CONFIGURATION-TOYCALC-NESTED [])))
|
||||
|
||||
(is
|
||||
(= ["NAME:"
|
||||
" toycalc subc - Subtracts parameter B from A"
|
||||
""
|
||||
"USAGE:"
|
||||
" toycalc subc [global-options] command [command options] [arguments...]"
|
||||
""
|
||||
"EXAMPLES:"
|
||||
" Just one example"
|
||||
""
|
||||
"VERSION:"
|
||||
" 1.2.3"
|
||||
""
|
||||
"COMMANDS:"
|
||||
" sub Subtracts"
|
||||
""
|
||||
"GLOBAL OPTIONS:"
|
||||
" --q N 0 Parameter q"
|
||||
" -?, --help"
|
||||
""]
|
||||
(generate-global-help CONFIGURATION-TOYCALC-NESTED ["toycalc" "subc"]))))
|
||||
|
||||
(deftest generate-subcmd-help-test-nested
|
||||
(is
|
||||
(= ["NAME:"
|
||||
" toycalc add - Adds two numbers together"
|
||||
""
|
||||
"USAGE:"
|
||||
" toycalc [add|a] [command options] [arguments...]"
|
||||
""
|
||||
"EXAMPLES:"
|
||||
" Example One"
|
||||
" Example Two"
|
||||
""
|
||||
"VERSION:"
|
||||
" 3.3.3"
|
||||
""
|
||||
"OPTIONS:"
|
||||
" --a N Addendum 1"
|
||||
" --b N 0 Addendum 2"
|
||||
" -?, --help"
|
||||
""]
|
||||
(generate-subcmd-help CONFIGURATION-TOYCALC-NESTED ["toycalc" "add"]))))
|
||||
|
||||
(deftest generate-subcmd-help-test
|
||||
|
||||
(is
|
||||
(= ["NAME:"
|
||||
" toycalc add - Adds two numbers together"
|
||||
" "
|
||||
" Looks great, doesn't it?"
|
||||
""
|
||||
"USAGE:"
|
||||
" toycalc [add|a] [command options] [arguments...]"
|
||||
""
|
||||
"OPTIONS:"
|
||||
" -a, --a1 N 0 First addendum [$AA]"
|
||||
" -b, --a2 N 0 Second addendum"
|
||||
" -?, --help"
|
||||
""]
|
||||
(generate-subcmd-help CONFIGURATION-TOYCALC-v2 ["toycalc" "add"])))
|
||||
|
||||
(is
|
||||
(= ["NAME:"
|
||||
" toycalc sub - Subtracts parameter B from A"
|
||||
""
|
||||
"USAGE:"
|
||||
" toycalc [sub|s] [command options] [arguments...]"
|
||||
""
|
||||
"OPTIONS:"
|
||||
" -a, --pa N 0 Parameter A"
|
||||
" -b, --pb N 0 Parameter B"
|
||||
" -?, --help"
|
||||
""]
|
||||
(generate-subcmd-help CONFIGURATION-TOYCALC-v2 ["toycalc" "sub"])))
|
||||
|
||||
(is
|
||||
(= :ERR
|
||||
(try-catch-all
|
||||
(generate-subcmd-help CONFIGURATION-TOYCALC-v2 ["toycalc" "undefined-cmd"])
|
||||
(fn [_] :ERR)))))
|
||||
|
||||
(def CONFIGURATION-POSITIONAL-TOYCALC
|
||||
{:app {:command "toycalc"
|
||||
:description "A command-line toy calculator"
|
||||
:version "0.0.1"}
|
||||
:global-opts [{:option "base"
|
||||
:as "The number base for output"
|
||||
:type :int
|
||||
:default 10}]
|
||||
:commands [{:command "add" :short "a"
|
||||
:description ["Adds two numbers together"
|
||||
""
|
||||
"Looks great, doesn't it?"]
|
||||
:opts [{:option "a1" :short 0 :env "AA" :as "First addendum" :type :int :default 0}
|
||||
{:option "a2" :short 1 :as "Second addendum" :type :int :default 0}]
|
||||
:runs dummy-cmd}]})
|
||||
|
||||
(def CONFIGURATION-POSITIONAL-TOYCALC-v2
|
||||
(U2/convert-config-v1->v2 CONFIGURATION-POSITIONAL-TOYCALC))
|
||||
|
||||
(deftest generate-subcmd-positional-test
|
||||
(is
|
||||
(= ["NAME:"
|
||||
" toycalc add - Adds two numbers together"
|
||||
" "
|
||||
" Looks great, doesn't it?"
|
||||
""
|
||||
"USAGE:"
|
||||
" toycalc [add|a] [command options] a1 a2"
|
||||
""
|
||||
"OPTIONS:"
|
||||
" --a1 N 0 First addendum [$AA]"
|
||||
" --a2 N 0 Second addendum"
|
||||
" -?, --help"
|
||||
""]
|
||||
(generate-subcmd-help CONFIGURATION-POSITIONAL-TOYCALC-v2 ["toycalc" "add"]))))
|
||||
|
||||
(def CONFIGURATION-MULTILINES
|
||||
{:command "multiline"
|
||||
:description ["An app description can span"
|
||||
"multiple lines."]
|
||||
:version "1.2.3"
|
||||
:opts [{:option "global-opt"
|
||||
:as ["Global opt help"
|
||||
"with"
|
||||
"multiple lines."]
|
||||
:type :int
|
||||
:default 10}]
|
||||
:subcommands [{:command "mycmd"
|
||||
:description ["A command description"
|
||||
""
|
||||
"Can contain multiple lines."
|
||||
"Only the first line is displayed on global help."]
|
||||
:opts [{:option "mycmd-opt1" :short "a" :type :int :default 0
|
||||
:as ["not really a multiline but just fine"]}
|
||||
{:option "long-name-here-should-stretch-things-out" :short "l" :type :keyword
|
||||
:as ["testing out how a longer"
|
||||
"option affects things."]}
|
||||
{:option "opt2" :type :int :default 0
|
||||
:as ["text that is long"
|
||||
"can be split"
|
||||
"over"
|
||||
"many"
|
||||
"lines"
|
||||
"and"
|
||||
" will"
|
||||
" be"
|
||||
" indented"
|
||||
" appropriately"
|
||||
"and"
|
||||
"can"
|
||||
"include empty"
|
||||
""
|
||||
"lines."]}
|
||||
{:option "opt3" :short "c" :env "ENV_VAR" :type :string :multiple true :default :present
|
||||
:as ["here's what happens to a multiline with"
|
||||
"env also set"]}]
|
||||
|
||||
:runs dummy-cmd}]})
|
||||
|
||||
(deftest multilines-global-help-test
|
||||
(is
|
||||
(= ["NAME:"
|
||||
" multiline - An app description can span"
|
||||
" multiple lines."
|
||||
""
|
||||
"USAGE:"
|
||||
" multiline [global-options] command [command options] [arguments...]"
|
||||
""
|
||||
"VERSION:"
|
||||
" 1.2.3"
|
||||
""
|
||||
"COMMANDS:"
|
||||
" mycmd A command description"
|
||||
""
|
||||
"GLOBAL OPTIONS:"
|
||||
" --global-opt N 10 Global opt help"
|
||||
" with"
|
||||
" multiple lines."
|
||||
" -?, --help"
|
||||
""]
|
||||
(generate-global-help CONFIGURATION-MULTILINES ["multiline"]))))
|
||||
|
||||
(deftest multilines-cmd-help-test
|
||||
(is
|
||||
(= ["NAME:"
|
||||
" multiline mycmd - A command description"
|
||||
" "
|
||||
" Can contain multiple lines."
|
||||
" Only the first line is displayed on global help."
|
||||
""
|
||||
"USAGE:"
|
||||
" multiline mycmd [command options] [arguments...]"
|
||||
""
|
||||
"OPTIONS:"
|
||||
" -a, --mycmd-opt1 N 0 not really a multiline but just fine"
|
||||
" -l, --long-name-here-should-stretch-things-out S testing out how a longer"
|
||||
" option affects things."
|
||||
" --opt2 N 0 text that is long"
|
||||
" can be split"
|
||||
" over"
|
||||
" many"
|
||||
" lines"
|
||||
" and"
|
||||
" will"
|
||||
" be"
|
||||
" indented"
|
||||
" appropriately"
|
||||
" and"
|
||||
" can"
|
||||
" include empty"
|
||||
" "
|
||||
" lines."
|
||||
" -c, --opt3 S* here's what happens to a multiline with"
|
||||
" env also set [$ENV_VAR]"
|
||||
" -?, --help"
|
||||
""]
|
||||
(generate-subcmd-help CONFIGURATION-MULTILINES ["multiline" "mycmd"]))))
|
||||
5
test-resources/lib_tests/cli_matic/json_simple.json
Normal file
5
test-resources/lib_tests/cli_matic/json_simple.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"list": [1, 2, "hi"],
|
||||
"intval": 100,
|
||||
"strval": "good"
|
||||
}
|
||||
453
test-resources/lib_tests/cli_matic/presets_test.cljc
Normal file
453
test-resources/lib_tests/cli_matic/presets_test.cljc
Normal file
|
|
@ -0,0 +1,453 @@
|
|||
(ns cli-matic.presets-test
|
||||
(:require [clojure.test :refer [is are deftest testing]]
|
||||
[cljc.java-time.zone-id :as zone-id]
|
||||
[cljc.java-time.local-date :as local-date]
|
||||
[cljc.java-time.local-date-time :as local-date-time]
|
||||
[cljc.java-time.zoned-date-time :as zoned-date-time]
|
||||
[cli-matic.core :refer [parse-command-line]]
|
||||
[cli-matic.utils-v2 :refer [convert-config-v1->v2]]
|
||||
[cli-matic.presets :refer [set-help-values set-find-value set-find-didyoumean]]
|
||||
[clojure.java.io :as io]))
|
||||
|
||||
(defn cmd_foo [v]
|
||||
(prn "Foo:" v)
|
||||
0)
|
||||
|
||||
(defn mkDummyCfg
|
||||
[myOption]
|
||||
|
||||
(convert-config-v1->v2
|
||||
|
||||
{:app {:command "dummy"
|
||||
:description "I am some command"
|
||||
:version "0.1.2"}
|
||||
:global-opts []
|
||||
:commands [{:command "foo"
|
||||
:description "I am function foo"
|
||||
:opts [myOption]
|
||||
:runs cmd_foo}]}))
|
||||
|
||||
; :subcommand "foo"
|
||||
; :subcommand-def
|
||||
|
||||
(defn parse-cmds-simpler [args cfg]
|
||||
(dissoc
|
||||
(parse-command-line args cfg)
|
||||
:subcommand
|
||||
:subcommand-path
|
||||
:subcommand-def))
|
||||
|
||||
(defn str-val
|
||||
"Rewrites a value for float comparison to a string"
|
||||
[o]
|
||||
(let [v (get-in o [:commandline :val])
|
||||
vs (str v)]
|
||||
(assoc-in o [:commandline :val] vs)))
|
||||
|
||||
; :int values
|
||||
|
||||
(deftest test-ints
|
||||
|
||||
(testing "int value"
|
||||
(are [i o]
|
||||
(= (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :as "x" :type :int}))
|
||||
o)
|
||||
|
||||
; integers
|
||||
["foo" "--val" "7"]
|
||||
{:commandline {:_arguments []
|
||||
:val 7}
|
||||
:error-text ""
|
||||
:parse-errors :NONE}))
|
||||
|
||||
;
|
||||
(testing "int-0 value"
|
||||
(are [i o]
|
||||
(= (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :as "x" :type :int-0}))
|
||||
o)
|
||||
|
||||
; integers
|
||||
["foo" "--val" "7"]
|
||||
{:commandline {:_arguments []
|
||||
:val 7}
|
||||
:error-text ""
|
||||
:parse-errors :NONE}
|
||||
|
||||
; integers
|
||||
["foo"]
|
||||
{:commandline {:_arguments []
|
||||
:val 0}
|
||||
:error-text ""
|
||||
:parse-errors :NONE})))
|
||||
|
||||
;; float values (float and float-0)
|
||||
;; to compare them, we rewrite them to strings
|
||||
(deftest test-float
|
||||
|
||||
(testing "float value"
|
||||
(are [i o]
|
||||
(= (str-val (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :as "x" :type :float})))
|
||||
|
||||
(str-val o))
|
||||
|
||||
; integers as floats
|
||||
["foo" "--val" "7"]
|
||||
{:commandline {:_arguments []
|
||||
:val 7.0}
|
||||
:error-text ""
|
||||
:parse-errors :NONE}
|
||||
|
||||
; floats as floats
|
||||
["foo" "--val" "3.14"]
|
||||
{:commandline {:_arguments []
|
||||
:val 3.14}
|
||||
:error-text ""
|
||||
:parse-errors :NONE}))
|
||||
|
||||
(testing "float0 value"
|
||||
(are [i o]
|
||||
(= (str-val (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :as "x" :type :float-0})))
|
||||
|
||||
(str-val o))
|
||||
|
||||
; integers as floats
|
||||
["foo" "--val" "7"]
|
||||
{:commandline {:_arguments []
|
||||
:val 7.0}
|
||||
:error-text ""
|
||||
:parse-errors :NONE}
|
||||
|
||||
; floats as floats
|
||||
["foo" "--val" "3.14"]
|
||||
{:commandline {:_arguments []
|
||||
:val 3.14}
|
||||
:error-text ""
|
||||
:parse-errors :NONE}
|
||||
|
||||
; missing as zero
|
||||
["foo"]
|
||||
{:commandline {:_arguments []
|
||||
:val 0.0}
|
||||
:error-text ""
|
||||
:parse-errors :NONE})))
|
||||
|
||||
;; :keyword
|
||||
(deftest test-keyword
|
||||
(testing "simple keyword"
|
||||
(are [i o]
|
||||
(= (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :as "x" :type :keyword})) o)
|
||||
|
||||
;
|
||||
["foo" "--val" "abcd"]
|
||||
{:commandline {:_arguments []
|
||||
:val :abcd}
|
||||
:error-text ""
|
||||
:parse-errors :NONE}))
|
||||
(testing "Already keyword"
|
||||
(are [i o]
|
||||
(= (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :as "x" :type :keyword})) o)
|
||||
|
||||
;
|
||||
["foo" "--val" ":core/xyz"]
|
||||
{:commandline {:_arguments []
|
||||
:val :core/xyz}
|
||||
:error-text ""
|
||||
:parse-errors :NONE}))
|
||||
(testing "double colon"
|
||||
(are [i o]
|
||||
(= (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :as "x" :type :keyword})) o)
|
||||
|
||||
;
|
||||
["foo" "--val" "::abcd"]
|
||||
{:commandline {:_arguments []
|
||||
:val :user/abcd}
|
||||
:error-text ""
|
||||
:parse-errors :NONE})))
|
||||
|
||||
; :string
|
||||
(deftest test-string
|
||||
(testing "just strings"
|
||||
(are [i o]
|
||||
(= (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :as "x" :type :string})) o)
|
||||
|
||||
;
|
||||
["foo" "--val" "abcd"]
|
||||
{:commandline {:_arguments []
|
||||
:val "abcd"}
|
||||
:error-text ""
|
||||
:parse-errors :NONE}))
|
||||
|
||||
(testing
|
||||
(are [i o]
|
||||
(= (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :short "v" :as "x" :type :string})) o)
|
||||
|
||||
;
|
||||
["foo" "-v" "abcd" "aaarg"]
|
||||
{:commandline {:_arguments ["aaarg"]
|
||||
:val "abcd"}
|
||||
:error-text ""
|
||||
:parse-errors :NONE}))
|
||||
|
||||
(testing "multiple strings"
|
||||
(are [i o]
|
||||
(= (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :as "x" :type :string :multiple true})) o)
|
||||
;
|
||||
["foo" "--val" "abcd"]
|
||||
{:commandline {:_arguments []
|
||||
:val ["abcd"]}
|
||||
:error-text ""
|
||||
:parse-errors :NONE}
|
||||
|
||||
["foo" "--val" "abcd" "--val" "defg"]
|
||||
{:commandline {:_arguments []
|
||||
:val ["abcd" "defg"]}
|
||||
:error-text ""
|
||||
:parse-errors :NONE}))
|
||||
|
||||
(testing "multiple strings but no multiple option"
|
||||
(are [i o]
|
||||
(= (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :as "x" :type :string :multiple false})) o)
|
||||
|
||||
;
|
||||
["foo" "--val" "abcd"]
|
||||
{:commandline {:_arguments []
|
||||
:val "abcd"}
|
||||
:error-text ""
|
||||
:parse-errors :NONE}
|
||||
|
||||
["foo" "--val" "abcd" "--val" "defg"]
|
||||
{:commandline {:_arguments []
|
||||
:val "defg"}
|
||||
:error-text ""
|
||||
:parse-errors :NONE})))
|
||||
|
||||
; :yyyy-mm-dd
|
||||
|
||||
(deftest test-dates
|
||||
(testing "YYYY-MM-DD suck"
|
||||
(are [i o]
|
||||
(= (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :as "x" :type :yyyy-mm-dd})) o)
|
||||
|
||||
; this works
|
||||
["foo" "--val" "2018-01-01"]
|
||||
{:commandline {:_arguments []
|
||||
:val (-> (local-date/parse "2018-01-01")
|
||||
(local-date/at-start-of-day)
|
||||
(local-date-time/at-zone (zone-id/system-default))
|
||||
(zoned-date-time/to-instant)
|
||||
(java.util.Date/from))}
|
||||
:error-text ""
|
||||
:parse-errors :NONE}
|
||||
|
||||
; this does not
|
||||
["foo" "--val" "pippo"]
|
||||
{:commandline {:_arguments []
|
||||
:val nil}
|
||||
:error-text ""
|
||||
:parse-errors :NONE})))
|
||||
|
||||
; Slurping di file di testo
|
||||
|
||||
;; BB_TEST_PATCH: rewrite where the tests get resources from
|
||||
(def base-dir (.getParent (io/file *file*)))
|
||||
(def resource (fn [x] (str (io/file base-dir x))))
|
||||
|
||||
(deftest test-slurping
|
||||
(testing "Slurping all-in-one"
|
||||
(are [i o]
|
||||
(= (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :as "x" :type :slurp})) o)
|
||||
|
||||
; one file
|
||||
["foo" "--val" (resource "three_lines.txt")]
|
||||
{:commandline {:_arguments []
|
||||
:val "L1\nLine2\nline 3\n"}
|
||||
:error-text ""
|
||||
:parse-errors :NONE})) (testing "Slurping multiline"
|
||||
(are [i o]
|
||||
(= (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :as "x" :type :slurplines})) o)
|
||||
|
||||
; one file
|
||||
["foo" "--val" (resource "three_lines.txt")]
|
||||
{:commandline {:_arguments []
|
||||
:val ["L1" "Line2" "line 3"]}
|
||||
:error-text ""
|
||||
:parse-errors :NONE})))
|
||||
|
||||
; JSON
|
||||
|
||||
(deftest test-json
|
||||
(testing "JSON single value"
|
||||
(are [i o]
|
||||
(= (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :as "x" :type :json})) o)
|
||||
|
||||
; one file
|
||||
["foo" "--val" "{\"a\":1, \"b\":2}"]
|
||||
{:commandline {:_arguments []
|
||||
:val {"a" 1
|
||||
"b" 2}}
|
||||
:error-text ""
|
||||
:parse-errors :NONE}))
|
||||
|
||||
(testing "Slurping multiline JSON"
|
||||
(are [i o]
|
||||
(= (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :as "x" :type :jsonfile})) o)
|
||||
|
||||
; one file
|
||||
["foo" "--val" (resource "json_simple.json")]
|
||||
{:commandline {:_arguments []
|
||||
:val {"list" [1 2 "hi"]
|
||||
"intval" 100
|
||||
"strval" "good"}}
|
||||
:error-text ""
|
||||
:parse-errors :NONE})))
|
||||
|
||||
; EDN
|
||||
|
||||
(deftest test-edn
|
||||
(testing "EDN single value"
|
||||
(are [i o]
|
||||
(= (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :as "x" :type :edn})) o)
|
||||
|
||||
; one file
|
||||
["foo" "--val" "{:a 1, :b 2}"]
|
||||
{:commandline {:_arguments []
|
||||
:val {:a 1
|
||||
:b 2}}
|
||||
:error-text ""
|
||||
:parse-errors :NONE}))
|
||||
|
||||
(testing "Slurping multiline EDN"
|
||||
(are [i o]
|
||||
(= (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :as "x" :type :ednfile})) o)
|
||||
|
||||
; one file
|
||||
["foo" "--val" (resource "edn_simple.edn")]
|
||||
{:commandline {:_arguments []
|
||||
:val {:list [1 2 "hi"]
|
||||
:intval 100
|
||||
:strval "good"}}
|
||||
:error-text ""
|
||||
:parse-errors :NONE})))
|
||||
|
||||
; YAML
|
||||
|
||||
(deftest test-yaml
|
||||
(testing "YAML single value"
|
||||
(are [i o]
|
||||
(= (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :as "x" :type :yaml})) o)
|
||||
|
||||
; one file
|
||||
["foo" "--val" "a: 1\nb: 2"]
|
||||
{:commandline {:_arguments []
|
||||
:val {"a" 1
|
||||
"b" 2}}
|
||||
:error-text ""
|
||||
:parse-errors :NONE}))
|
||||
|
||||
(testing "Slurping multiline YAML"
|
||||
(are [i o]
|
||||
(= (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :as "x" :type :yamlfile})) o)
|
||||
|
||||
; one file
|
||||
["foo" "--val" (resource "yaml_simple.yaml")]
|
||||
{:commandline {:_arguments []
|
||||
:val {"list" [1 2 "hi"]
|
||||
"intval" 100
|
||||
"strval" "good"}}
|
||||
:error-text ""
|
||||
:parse-errors :NONE}))
|
||||
|
||||
(testing "Complex multiline YAML"
|
||||
(are [i o]
|
||||
(= (-> (parse-cmds-simpler
|
||||
i
|
||||
(mkDummyCfg {:option "val" :as "x" :type :yamlfile}))
|
||||
(get-in [:commandline :val])
|
||||
(select-keys ["invoice" "date"])) o)
|
||||
|
||||
; one file
|
||||
["foo" "--val" (resource "yaml_full.yaml")]
|
||||
{"invoice" 34843
|
||||
"date" #inst "2001-01-23"}
|
||||
#_{:commandline {:_arguments []
|
||||
:val {"list" [1 2 "hi"]
|
||||
"intval" 100
|
||||
"strval" "good"}}
|
||||
:error-text ""
|
||||
:parse-errors :NONE})))
|
||||
|
||||
|
||||
; =============== SETS ==========
|
||||
|
||||
|
||||
(deftest set-help-values-test
|
||||
(are [s o]
|
||||
(= o (set-help-values s))
|
||||
|
||||
#{:a :b} "a|b"
|
||||
#{:a} "a"
|
||||
|
||||
#{"A" "b" "CD"} "A|CD|b"))
|
||||
|
||||
(deftest set-find-value-test
|
||||
(are [st v o]
|
||||
(= o (set-find-value st v))
|
||||
|
||||
#{:a :b} "a" :a
|
||||
#{:a :b} "A" :a
|
||||
#{:a :b} "x" nil
|
||||
|
||||
#{:a :b} ":a" :a
|
||||
|
||||
#{"A" "B"} "a" "A"
|
||||
#{"A" "B"} ":A" "A"
|
||||
#{"A" "B"} "Q" nil
|
||||
#{":A" ":B"} "a" ":A"))
|
||||
|
||||
(deftest set-find-didyoumean-test
|
||||
(are [st v cds]
|
||||
(= cds (set-find-didyoumean st v))
|
||||
|
||||
#{:anna :babbo} "aNni" [:anna]
|
||||
#{:anna :babbo :anno} ":anni" [:anna :anno]
|
||||
#{:anna :babbo :anno} "ZebrA" []))
|
||||
3
test-resources/lib_tests/cli_matic/three_lines.txt
Normal file
3
test-resources/lib_tests/cli_matic/three_lines.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
L1
|
||||
Line2
|
||||
line 3
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
(ns cli-matic.utils-candidates-test
|
||||
(:require [clojure.test :refer :all])
|
||||
(:require [cli-matic.utils-candidates :refer [str-distance
|
||||
candidate-suggestions]]))
|
||||
|
||||
(defn abs [n] (max n (- n)))
|
||||
|
||||
(defn float=
|
||||
"Approximate float equality.
|
||||
|
||||
Jeez, in each and every language I used in my life I
|
||||
had to write this. Sometimes I wonder which way thing are
|
||||
going.
|
||||
"
|
||||
[a b]
|
||||
(let [fa (float a)
|
||||
fb (float b)
|
||||
err 0.001]
|
||||
(> err (abs (- fa fb)))))
|
||||
|
||||
(deftest str-distance-test
|
||||
(are [s1 s2 d]
|
||||
(float= d (str-distance s1 s2))
|
||||
|
||||
; same string = 0
|
||||
"pippo" "pippo" 0
|
||||
|
||||
; one change
|
||||
"pippo" "Pippo" 0.20
|
||||
|
||||
; compute as prc of longest
|
||||
"pippox" "Pippo" 0.334
|
||||
|
||||
; nils?
|
||||
"xxx" nil 1
|
||||
|
||||
; both empty
|
||||
"" "" 0
|
||||
|
||||
; both nil
|
||||
nil nil 0))
|
||||
|
||||
(deftest candidate-suggestions-test
|
||||
|
||||
(are [c t r]
|
||||
(= r (vec (candidate-suggestions c t 0.5)))
|
||||
|
||||
; only one
|
||||
["foo" "bar" "baz" "buzz"] "baar" ["bar" "baz"]
|
||||
|
||||
;none
|
||||
["foo" "bar" "baz" "buzz"] "zebra" []
|
||||
|
||||
; best comes first
|
||||
["foo" "bara" "barrr" "buzz" "o"] "bar" ["bara" "barrr"]
|
||||
|
||||
;none found
|
||||
["foo" "bara" "barrr" "buzz" "o"] "qaqaqa" []))
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
(ns cli-matic.utils-convert-config-test
|
||||
(:require [clojure.test :refer [is are deftest testing]]
|
||||
[cli-matic.optionals :as OPT]
|
||||
|
||||
[cli-matic.utils-convert-config
|
||||
:refer [unmangle-fn-name
|
||||
unmangle-fn
|
||||
fn->className]]))
|
||||
|
||||
|
||||
;
|
||||
; Some example fns
|
||||
;
|
||||
|
||||
|
||||
(defn add_numbers [x] x)
|
||||
(defn add-numbers [y] (inc y))
|
||||
|
||||
|
||||
;
|
||||
; Tests
|
||||
;
|
||||
|
||||
|
||||
(deftest ^:skip-bb unmangle-fn-name-test
|
||||
(are [i o]
|
||||
(= o (unmangle-fn-name i))
|
||||
|
||||
;; A moderately complex name
|
||||
"cli_matic.utils_v2$convert_config_v1__GT_v2"
|
||||
"cli-matic.utils-v2/convert-config-v1->v2"))
|
||||
|
||||
(deftest ^:skip-bb unmangle-fn-test
|
||||
(are [i o]
|
||||
(= o (unmangle-fn i))
|
||||
|
||||
;; A moderately complex name
|
||||
add-numbers
|
||||
'cli-matic.utils-convert-config-test/add-numbers
|
||||
|
||||
; add-numbers
|
||||
; "cli-matic.utils-convert-config-test/add-numbers"
|
||||
))
|
||||
|
||||
(OPT/orchestra-instrument)
|
||||
197
test-resources/lib_tests/cli_matic/utils_test.cljc
Normal file
197
test-resources/lib_tests/cli_matic/utils_test.cljc
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
(ns cli-matic.utils-test
|
||||
(:require [clojure.test :refer [is are deftest testing]]
|
||||
[cli-matic.utils :refer [asString asStrVec
|
||||
indent-string indent
|
||||
pad deep-merge
|
||||
all-subcommands-aliases
|
||||
all-subcommands
|
||||
canonicalize-subcommand
|
||||
mk-cli-option
|
||||
exit! exception-info]]
|
||||
[cli-matic.platform-macros :refer [try-catch-all]]
|
||||
[cli-matic.platform :as P]
|
||||
[cli-matic.presets :as PR]))
|
||||
|
||||
(deftest asString-test
|
||||
|
||||
(are [i o] (= (asString i) o)
|
||||
|
||||
; a string
|
||||
"x" "x"
|
||||
|
||||
; a vector of strings
|
||||
["a" "b"] "a\nb"
|
||||
|
||||
; a vector of vectors
|
||||
["a" ["b" "c"] "d"] "a\nb\nc\nd"
|
||||
|
||||
|
||||
|
||||
; add more cases.....
|
||||
))
|
||||
|
||||
(deftest asStrVec-test
|
||||
|
||||
(are [i o]
|
||||
(= (asStrVec i) o)
|
||||
|
||||
; a string
|
||||
"x" ["x"]
|
||||
|
||||
; a vector of strings
|
||||
nil []
|
||||
|
||||
; a vector of vectors
|
||||
["a" ["b" "c"] "d"] ["a" ["b" "c"] "d"]
|
||||
|
||||
|
||||
|
||||
; add more cases.....
|
||||
))
|
||||
|
||||
(deftest indent-string-test
|
||||
(are [i o] (= (indent-string i) o)
|
||||
"a" " a"))
|
||||
|
||||
(deftest indent-test
|
||||
(are [i o] (= (indent i) o)
|
||||
|
||||
; a string
|
||||
|
||||
"a" " a"
|
||||
|
||||
; a vector
|
||||
["a" "b"] [" a" " b"]))
|
||||
|
||||
(deftest pad-test
|
||||
(are [s s1 t o] (= (pad s s1 t) o)
|
||||
|
||||
; shorter
|
||||
"pippo" nil 3 "pip"
|
||||
|
||||
; longer
|
||||
"pippo" nil 7 "pippo "
|
||||
|
||||
; with merged string
|
||||
"pippo" "pluto" 10 "pippo, plu"))
|
||||
|
||||
(deftest deep-merge-test
|
||||
|
||||
(is (=
|
||||
{:one 4 :two {:three 6}}
|
||||
|
||||
(deep-merge {:one 1 :two {:three 3}}
|
||||
{:one 2 :two {:three 4}}
|
||||
{:one 3 :two {:three 5}}
|
||||
{:one 4 :two {:three 6}}))))
|
||||
|
||||
|
||||
;
|
||||
; cli-matic specific
|
||||
;
|
||||
|
||||
|
||||
(defn cmd_foo [& opts])
|
||||
(defn cmd_bar [& opts])
|
||||
(defn cmd_returnstructure [opts]
|
||||
{:myopts opts
|
||||
:somedata "hiyo"})
|
||||
|
||||
(def SIMPLE-SUBCOMMAND-CFG
|
||||
{:app {:command "dummy"
|
||||
:description "I am some command"
|
||||
:version "0.1.2"}
|
||||
:global-opts [{:option "aa" :as "A" :type :int}
|
||||
{:option "bb" :as "B" :type :int}]
|
||||
:commands [{:command "foo" :short "f"
|
||||
:description "I am function foo"
|
||||
:opts [{:option "cc" :as "C" :type :int}
|
||||
{:option "dd" :as "D" :type :int}]
|
||||
:runs cmd_foo}
|
||||
|
||||
; another one
|
||||
{:command "bar"
|
||||
:description "I am function bar"
|
||||
:opts [{:option "ee" :as "E" :type :int}
|
||||
{:option "ff" :as "F" :type :int}]
|
||||
:runs cmd_bar}
|
||||
|
||||
; this one to check return structs
|
||||
{:command "rets"
|
||||
:description "I return a structure"
|
||||
:opts []
|
||||
:runs cmd_returnstructure}]})
|
||||
|
||||
(deftest subcommands-and-aliases
|
||||
(testing "Subcommands and aliases"
|
||||
(is (= (all-subcommands-aliases SIMPLE-SUBCOMMAND-CFG)
|
||||
{"bar" "bar"
|
||||
"f" "foo"
|
||||
"foo" "foo"
|
||||
"rets" "rets"})))
|
||||
|
||||
(testing "All subcommands"
|
||||
(is (= (all-subcommands SIMPLE-SUBCOMMAND-CFG)
|
||||
#{"bar"
|
||||
"f"
|
||||
"foo"
|
||||
"rets"})))
|
||||
|
||||
(testing "Canonicalize-subcommand"
|
||||
(is (= (canonicalize-subcommand SIMPLE-SUBCOMMAND-CFG "foo")
|
||||
"foo"))
|
||||
(is (= (canonicalize-subcommand SIMPLE-SUBCOMMAND-CFG "f")
|
||||
"foo"))
|
||||
(is (= (canonicalize-subcommand SIMPLE-SUBCOMMAND-CFG "bar")
|
||||
"bar"))))
|
||||
|
||||
(deftest make-option
|
||||
(testing "Build a tools.cli option"
|
||||
(are [i o]
|
||||
(= o (mk-cli-option i))
|
||||
|
||||
; simplest example
|
||||
{:option "extra" :short "x" :as "Port number" :type :int}
|
||||
["-x" "--extra N" "Port number"
|
||||
:parse-fn P/parseInt]
|
||||
|
||||
; no shorthand
|
||||
{:option "extra" :as "Port number" :type :int}
|
||||
[nil "--extra N" "Port number"
|
||||
:parse-fn P/parseInt]
|
||||
|
||||
; with a default
|
||||
{:option "extra" :as "Port number" :type :int :default 13}
|
||||
[nil "--extra N" "Port number"
|
||||
:parse-fn P/parseInt :default 13]
|
||||
|
||||
; :present means there is no default
|
||||
{:option "extra" :as "Port number" :type :int :default :present}
|
||||
[nil "--extra N*" "Port number"
|
||||
:parse-fn P/parseInt]
|
||||
|
||||
; with-flag option
|
||||
{:option "true" :short "t" :as "A with-flag option" :type :with-flag :default false}
|
||||
["-t" "--[no-]true" "A with-flag option"
|
||||
:default false]
|
||||
|
||||
; flag option
|
||||
{:option "flag" :short "f" :as "A flag option" :type :flag :default false}
|
||||
["-f" "--flag F" "A flag option"
|
||||
:parse-fn PR/parseFlag
|
||||
:default false])))
|
||||
|
||||
(deftest test-exit!
|
||||
|
||||
(is (= ["Ciao" 23]
|
||||
(try-catch-all
|
||||
(exit! "Ciao" 23)
|
||||
(fn [t]
|
||||
(exception-info t)))))
|
||||
|
||||
(is (= -1
|
||||
(last (try-catch-all
|
||||
(/ 10 0)
|
||||
(fn [t]
|
||||
(exception-info t)))))))
|
||||
|
||||
243
test-resources/lib_tests/cli_matic/utils_v2_test.cljc
Normal file
243
test-resources/lib_tests/cli_matic/utils_v2_test.cljc
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
(ns cli-matic.utils-v2-test
|
||||
(:require [clojure.test :refer [is are deftest testing]]
|
||||
#?(:clj [cli-matic.platform-macros :refer [try-catch-all]]
|
||||
:cljs [cli-matic.platform-macros :refer-macros [try-catch-all]])
|
||||
[cli-matic.optionals :as OPT]
|
||||
[cli-matic.utils-v2 :refer [convert-config-v1->v2
|
||||
walk
|
||||
can-walk?
|
||||
as-canonical-path
|
||||
get-most-specific-value]]))
|
||||
|
||||
|
||||
;
|
||||
; dummy functions
|
||||
;
|
||||
|
||||
|
||||
(defn add_numbers [x] x)
|
||||
(defn subtract_numbers [x] x)
|
||||
|
||||
(deftest convert-config-v1->v2-test
|
||||
|
||||
(are [i o] (= (convert-config-v1->v2 i) o)
|
||||
|
||||
; ============== TEST 1 ===============
|
||||
; Input
|
||||
{:app {:command "toycalc"
|
||||
:description "A command-line toy calculator"
|
||||
:version "0.0.1"}
|
||||
|
||||
:global-opts [{:option "base"
|
||||
:as "The number base for output"
|
||||
:type :int
|
||||
:default 10}]
|
||||
|
||||
:commands [{:command "add"
|
||||
:description "Adds two numbers together"
|
||||
:opts [{:option "a" :as "Addendum 1" :type :int}
|
||||
{:option "b" :as "Addendum 2" :type :int :default 0}]
|
||||
:runs add_numbers}
|
||||
|
||||
{:command "sub"
|
||||
:description "Subtracts parameter B from A"
|
||||
:opts [{:option "a" :as "Parameter A" :type :int :default 0}
|
||||
{:option "b" :as "Parameter B" :type :int :default 0}]
|
||||
:runs subtract_numbers}]}
|
||||
|
||||
; Output
|
||||
{:command "toycalc"
|
||||
:description "A command-line toy calculator"
|
||||
:version "0.0.1"
|
||||
:opts [{:as "The number base for output"
|
||||
:default 10
|
||||
:option "base"
|
||||
:type :int}]
|
||||
:subcommands [{:command "add"
|
||||
:description "Adds two numbers together"
|
||||
:opts [{:as "Addendum 1"
|
||||
:option "a"
|
||||
:type :int}
|
||||
{:as "Addendum 2"
|
||||
:default 0
|
||||
:option "b"
|
||||
:type :int}]
|
||||
:runs add_numbers}
|
||||
{:command "sub"
|
||||
:description "Subtracts parameter B from A"
|
||||
:opts [{:as "Parameter A"
|
||||
:default 0
|
||||
:option "a"
|
||||
:type :int}
|
||||
{:as "Parameter B"
|
||||
:default 0
|
||||
:option "b"
|
||||
:type :int}]
|
||||
:runs subtract_numbers}]}))
|
||||
|
||||
(deftest walk-test
|
||||
|
||||
(let [cfg {:command "toycalc"
|
||||
:description "A command-line toy calculator"
|
||||
:version "0.0.1"
|
||||
:opts [{:as "The number base for output"
|
||||
:default 10
|
||||
:option "base"
|
||||
:type :int}]
|
||||
:subcommands [{:command "add"
|
||||
:description "Adds two numbers together"
|
||||
:opts [{:as "Addendum 1"
|
||||
:option "a"
|
||||
:type :int}
|
||||
{:as "Addendum 2"
|
||||
:default 0
|
||||
:option "b"
|
||||
:type :int}]
|
||||
:runs add_numbers}
|
||||
{:command "subc"
|
||||
:description "Subtracts parameter B from A"
|
||||
:opts [{:as "Parameter q"
|
||||
:default 0
|
||||
:option "q"
|
||||
:type :int}]
|
||||
|
||||
:subcommands [{:command "sub"
|
||||
:description "Subtracts"
|
||||
:opts [{:as "Parameter A"
|
||||
:default 0
|
||||
:option "a"
|
||||
:type :int}
|
||||
{:as "Parameter B"
|
||||
:default 0
|
||||
:option "b"
|
||||
:type :int}]
|
||||
:runs subtract_numbers}]}]}]
|
||||
|
||||
(are [p o] (=
|
||||
(try-catch-all
|
||||
(as-canonical-path (walk cfg p))
|
||||
(fn [_] :ERR))
|
||||
|
||||
o)
|
||||
|
||||
; es 1
|
||||
["toycalc" "add"]
|
||||
["toycalc" "add"]
|
||||
|
||||
; es 2
|
||||
["toycalc" "subc" "sub"]
|
||||
["toycalc" "subc" "sub"]
|
||||
|
||||
; not found
|
||||
["toycalc" "addq"]
|
||||
:ERR
|
||||
|
||||
["toycalc" "subc" "xx"]
|
||||
:ERR)
|
||||
|
||||
(are [p o] (= (can-walk? cfg p) o)
|
||||
|
||||
; es 1
|
||||
["toycalc" "add"]
|
||||
true
|
||||
|
||||
; es 2
|
||||
["toycalc" "subc" "sub"]
|
||||
true
|
||||
|
||||
; not found
|
||||
["toycalc" "addq"]
|
||||
false
|
||||
|
||||
["toycalc" "subc" "xx"]
|
||||
false))
|
||||
|
||||
(let [cfg-one {:command "onlyone"
|
||||
:description "A single subcommand"
|
||||
:version "0.0.1"
|
||||
:opts [{:as "The number base for output"
|
||||
:default 10
|
||||
:option "base"
|
||||
:type :int}]
|
||||
:runs subtract_numbers}]
|
||||
|
||||
(are [p o] (=
|
||||
(try-catch-all
|
||||
(as-canonical-path (walk cfg-one p))
|
||||
(fn [_] :ERR))
|
||||
|
||||
o)
|
||||
|
||||
; es 1
|
||||
["onlyone"]
|
||||
["onlyone"]
|
||||
|
||||
|
||||
; Nothing
|
||||
|
||||
|
||||
[]
|
||||
["onlyone"]
|
||||
|
||||
; notfound
|
||||
["toycalc" "subc" "xx"]
|
||||
:ERR)))
|
||||
|
||||
|
||||
|
||||
; :on-shutdown
|
||||
|
||||
|
||||
(defn shutdown_BASE [] 0)
|
||||
(defn shutdown_SUB [] 1)
|
||||
|
||||
(deftest get-most-specific-value-test
|
||||
|
||||
(let [cfg {:command "toycalc"
|
||||
:description "A command-line toy calculator"
|
||||
:version "0.0.1"
|
||||
:on-shutdown shutdown_BASE
|
||||
:opts []
|
||||
:subcommands [{:command "add"
|
||||
:description "Adds two numbers together"
|
||||
:opts []
|
||||
:runs add_numbers}
|
||||
{:command "subc"
|
||||
:description "Subtracts parameter B from A"
|
||||
:opts []
|
||||
:subcommands [{:command "sub"
|
||||
:description "Subtracts"
|
||||
:opts []
|
||||
:runs subtract_numbers
|
||||
:on-shutdown shutdown_SUB}]}]}]
|
||||
|
||||
(are [p o]
|
||||
(= (try-catch-all
|
||||
(get-most-specific-value cfg p :on-shutdown "-NF-")
|
||||
(fn [_] :ERR))
|
||||
o)
|
||||
|
||||
; Definito nella root
|
||||
["toycalc"]
|
||||
shutdown_BASE
|
||||
|
||||
; Sempre da root
|
||||
["toycalc" "add"]
|
||||
shutdown_BASE
|
||||
|
||||
; non definito, quindi uso root
|
||||
["toycalc" "subc"]
|
||||
shutdown_BASE
|
||||
|
||||
; definito specifico
|
||||
["toycalc" "subc" "sub"]
|
||||
shutdown_SUB
|
||||
|
||||
; not found
|
||||
["toycalc" "addq"]
|
||||
:ERR
|
||||
|
||||
["toycalc" "subc" "xx"]
|
||||
:ERR)))
|
||||
|
||||
(OPT/orchestra-instrument)
|
||||
29
test-resources/lib_tests/cli_matic/yaml_full.yaml
Normal file
29
test-resources/lib_tests/cli_matic/yaml_full.yaml
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
invoice: 34843
|
||||
date : 2001-01-23
|
||||
bill-to: &id001
|
||||
given : Chris
|
||||
family : Dumars
|
||||
address:
|
||||
lines: |
|
||||
458 Walkman Dr.
|
||||
Suite #292
|
||||
city : Royal Oak
|
||||
state : MI
|
||||
postal : 48046
|
||||
ship-to: *id001
|
||||
product:
|
||||
- sku : BL394D
|
||||
quantity : 4
|
||||
description : Basketball
|
||||
price : 450.00
|
||||
- sku : BL4438H
|
||||
quantity : 1
|
||||
description : Super Hoop
|
||||
price : 2392.00
|
||||
tax : 251.42
|
||||
total: 4443.52
|
||||
comments: >
|
||||
Late afternoon is best.
|
||||
Backup contact is Nancy
|
||||
Billsmer @ 338-4338.
|
||||
3
test-resources/lib_tests/cli_matic/yaml_simple.yaml
Normal file
3
test-resources/lib_tests/cli_matic/yaml_simple.yaml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
list: [1, 2, "hi"]
|
||||
intval: 100
|
||||
strval: "good"
|
||||
Loading…
Reference in a new issue