cli4clj is a library for easing the implementation of simple interactive command line interfaces (CLIs) for Clojure applications. In this post, I briefly announce the release of cli4clj version 1.2.3.
The most important changes in version 1.2.3 are:
- Add support for hiding commands in help and completion.
Hidden commands are prefixed with “_”. - Hide the “enable-trace” command by changing it to “_enable-trace”.
- Add a hidden “_sleep” command.
For hiding commands, the convention of prefixing hidden commands with “_” was introduced. This means that all commands that begin with an underscore, “_”, will neither be shown in the help nor in the command completion.
The rationale for hiding certain commands is to improve the usability by removing everything a “typical” user does not need. In this process, I also changed the “enable-trace” command to “_enable-trace” in order to hide it from the user.
The “_sleep” command was added as a stopgap solution for testing multi-threaded CLI applications. The current CLI test functionality works very well. However, there is a problem with multi-threaded applications.
Clearly, when a command triggers interaction with another thread that eventually prints a result, a corresponding unit test needs to wait until the processing finished and the result was printed. The CLI “UI thread” however, cannot know how long it has to wait. Thus, I added the “_sleep” command as a simple stopgap solution.
In the following, the code of the newly added unit tests for testing the “_sleep” command is shown for illustrating the problem and the current stopgap “solution”:
(deftest async-cmd-not-finished-test (let [cli-opts {:cmds {:async-foo {:fn (fn [] (println "Starting...") (let [tmp-out *out*] (doto (Thread. (fn [] (binding [*out* tmp-out] (sleep 500) (println "Finished.")))) (.start))) "Started.")}}} test-cmd-input ["async-foo"] out-string (test-cli-stdout #(start-cli cli-opts) test-cmd-input)] (is (= (expected-string ["Starting..." "\"Started.\""]) out-string)))) (deftest async-cmd-sleep-finished-test (let [cli-opts {:cmds {:async-foo {:fn (fn [] (println "Starting...") (let [tmp-out *out*] (doto (Thread. (fn [] (binding [*out* tmp-out] (sleep 500) (println "Finished.")))) (.start))) "Started.")}}} test-cmd-input ["async-foo" "_sleep 1000"] out-string (test-cli-stdout #(start-cli cli-opts) test-cmd-input)] (is (= (expected-string ["Starting..." "\"Started.\"" "Finished."]) out-string))))
Actually, using sleeps for this is not really an ideal solution. However, for now, this approach serves its purpose. In future, I may come up with something better that still allows me to keep cli4clj simple.
As usual, constructive criticism, feedback, and comments are always appreciated.
Pingback: cli4clj 1.2.5 – Improved Testability of Multi-threaded Command Line Applications in Clojure | ruedigergad