Add cli-matic lib tests

This commit is contained in:
Michiel Borkent 2022-02-24 17:20:11 +01:00
parent a99e681572
commit 3eace3278f
17 changed files with 2178 additions and 5 deletions

View file

@ -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

View file

@ -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

1 maven-name git-url
8 borkdude/rewrite-edn https://github.com/borkdude/rewrite-edn
9 camel-snake-kebab/camel-snake-kebab https://github.com/clj-commons/camel-snake-kebab
10 circleci/bond https://github.com/circleci/bond
11 cli-matic/cli-matic https://github.com/l3nz/cli-matic.git
12 clj-commons/clj-yaml https://github.com/clj-commons/clj-yaml
13 clj-commons/multigrep https://github.com/clj-commons/multigrep
14 clj-stacktrace/clj-stacktrace https://github.com/mmcgrana/clj-stacktrace

View file

@ -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

View file

@ -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))))))))

View file

@ -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"}}

View 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}]))

View file

@ -0,0 +1,3 @@
{:list [1 2 "hi"]
:intval 100
:strval "good"}

View 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"]))))

View file

@ -0,0 +1,5 @@
{
"list": [1, 2, "hi"],
"intval": 100,
"strval": "good"
}

View 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" []))

View file

@ -0,0 +1,3 @@
L1
Line2
line 3

View file

@ -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" []))

View file

@ -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)

View 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)))))))

View 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)

View 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.

View file

@ -0,0 +1,3 @@
list: [1, 2, "hi"]
intval: 100
strval: "good"