## cli4clj Version 1.3.2 – New: Embedded CLIs

In this post, I announce cli4clj version 1.3.2 and write a bit about the newly added functionality for creating embedded Command Line Interfaces (CLIs). For those unfamiliar with cli4clj: the aim of cli4clj is to ease the implementation of simple interactive CLIs for Clojure applications.

The original and still primary focus of cli4clj is on implementing interactive CLIs intended to be used in interactive terminals/shells. However, in one of my other applications, I needed an interactive CLI that can be used via network, actually via a Message-oriented Middleware (MoM).

The primary aspect in the CLI via network use case, from the cli4clj perspective, is that cli4clj leverages jline for handling interactive input from a terminal/shell. In the CLI via network use case, however, input cannot be read from an interactive terminal/shell.

In the concrete scenario of using an MoM, I wanted to send commands via messages and receive the output via corresponding reply messages. Furthermore, I wanted to use the existing MoM infrastructure without the need of opening yet another socket. Hence, networked CLIs such as nREPL did not appear as a good match to me.

Furthermore, I wanted to re-use the same map-based approach for configuring the jline-based CLI for the “embedded” CLI. The reason for this is that this will allow to easily switch between a jline-based and an embedded CLI or use both at the same time, e.g., when an application is intended to be used interactively in a terminal and, at the same time, the same functionality should be exposed via network. Moreover, I wanted to make the new “embedded” CLI generic such that it can also be used in other places.

The embedded CLI functionality uses the map-based cli4clj configuration format and creates a “CLI-function” instead of an interactive input. The generated function accepts a string of commands and arguments as input and returns the string result of “executing” the provided input. Thereby, the string result is the output that was produced as a result of executing the supplied command and arguments string.

Below, an example for using the embedded CLI functionality is shown. The example was executed in the cli4clj project in a “lein repl”. In order to illustrate that the output is really returned as string from the “CLI-function” I used the indirection of putting the results in the “ret” var and showed them by printing the value of “ret”.

cli4clj.example=> (use 'cli4clj.cli)
nil
cli4clj.example=> (def cli-opts {:cmds {:print {:fn #(println %)} :divide {:fn (fn [x y] (/ x y))}}})
#'cli4clj.example/cli-opts
cli4clj.example=> (def cli-fn (embedded-cli-fn cli-opts))
#'cli4clj.example/cli-fn
cli4clj.example=> (def ret (cli-fn "print foo"))
#'cli4clj.example/ret
cli4clj.example=> ret
"foo"
cli4clj.example=> (def ret (cli-fn "divide 4 2"))
#'cli4clj.example/ret
cli4clj.example=> ret
"2"
cli4clj.example=> (def ret (cli-fn "divide 4 0"))
#'cli4clj.example/ret
cli4clj.example=> ret
"ERROR: Divide by zero"
cli4clj.example=> (def ret (cli-fn "bar"))
#'cli4clj.example/ret
cli4clj.example=> ret
"ERROR: Invalid command: \"[bar]\". Please type \"help\" to get an overview of commands."
cli4clj.example=> (def ret (cli-fn "help"))
#'cli4clj.example/ret
cli4clj.example=> ret
"divide\n\n\nhelp [? h]\n\tShow help.\n\tDisplay a help text that lists all available commands including further detailed information about these commands.\n\nprint\n\n\nquit [q]\n\tQuit the CLI.\n\tTerminate and close the command line interface."
cli4clj.example=> (println ret)
divide
help [? h]
Show help.
Display a help text that lists all available commands including further detailed information about these commands.
print
quit [q]
Quit the CLI.
Terminate and close the command line interface.
nil
cli4clj.example=>


Besides adding the embedded CLI functionality, I also fixed some issues, as you can see in the version number. However, as things usually go with adding new functionality, even though it was tested and implemented following test-driven development, I likely added new bugs as well. So, whenever you spot a problem, just let me know.

I hope cli4clj is useful for you. I am always happy about constructive feedback and comments.

## bowerick 2.1.0 Released

In this post, I announce the release of bowerick 2.1.0. Before going into more detail, the summary of changes is as follows:

• Added functionality for advanced message handling for message producers and consumers.
• Added a built-in test message/event producer for easing experiments and demonstrations.
• Improved online management functionality.
• Updated version numbers from 1.99.x to 2.x.y.

The addition of this functionality was inspired by a reply I got to an earlier post about bowerick. Thanks a lot to Evgeny (eaksenov) for the input. I hope that the new functionality is what you had in mind with your request.

Up to now, bowerick message producers and consumers only considered the actual payload. For sending, a producer only accepted a single argument, the data/payload to be transmitted. Analogously, the callback called by consumers on message reception only accepted a single argument, the received data/payload.

Advanced message handling now allows more sophisticated interaction when sending/receiving messages. Regarding the split between message producer and consumer, I consider the consumer part more complete by now. Thus, I will start the discussion with the consumer.

Consumer

With the new advanced message handling functionality, a bowerick consumer can now also accept a callback with two arguments. If a two argument callback is passed, the first argument will contain the same data/payload as for the single argument callback. The second argument, is an instance of either the received message or the message headers, depending on which wire format/protocol is used.

For OpenWire, STOMP, and MQTT, the second callback argument is the received message instance. For STOMP via WebSockets (WS/STOMP), the second callback argument is the StompHeaders instance that was received along with the payload.

Please note: while the OpenWire and STOMP messages both implement javax.jms.Message, the concrete implementations are different, e.g., ActiveMQBytesMessage respectively StompJmsBytesMessage when transmitting byte array data. Furthermore, the messages received via MQTT (org.eclipse.paho.client.mqttv3.MqttMessage) do not implement javax.jms.Message.

Below is an excerpt as example of a two argument callback function:

...
(defn cb [data msg]
(println "Message type:" (type msg)))
(def consumer (create-consumer broker-uri topic-name cb))
...


Producer

For producers, the advanced message handling is in a rather early stage. So far, only setting custom message properties for OpenWire and STOMP is supported.

In order to set message properties for OpenWire and STOMP messages, you have to pass a second argument to a producer instance in addition to the actual payload data. The additional argument is a map in which the message properties have to be associated to the :message-properties key. The message properties have to be defined as a map of string keys and arbitrary values.

The string keys are the identifiers used for naming the message properties. What type of property, e.g., Boolean, Int, Double, etc., is used is determined based on the type of the corresponding value.

Below an excerpt is shown as example:

...
(def prod (create-producer broker-uri topic-name))
(def msg-opts {:message-properties
{"Some Boolean" true
"Some Byte" (byte 42)
"Some Double" 1.701
"Some Float" (float 1.701)
"Some Integer" (int 1701)
"Some Long" 1701
"Some Short" (short 1701)
"Some String" "Enterprise"}})
(prod "my data" msg-opts)
...


Instead of hard-coding the identifier :message-properties, the var msg-prop-key from the bowerick.jms namespace can be used as key as well.

Please note that MQTT and WS/STOMP producers also support the second argument. However, the second argument will be simply ignored for now for these protocols.

Built-in Test Message/Event Producer

For testing, experiments, and demonstration purposes, it can be beneficial to have some continuous traffic flowing across the messaging system. Thus, I added a simple built-in message/event producer for synthetically generating and sending messages.

The new message/event producer generates message with 50 Hz. The messages contain x, y, and z coordinates of, say, a point. Currently, the z coordinate is constant and on the x/y plane the point follows a circular movement.

This message/event producer is the basis for a demo, which I will cover in a separate post later.

Improved Online Management Functionality

When running bowerick in daemon mode as broker, it provides some, currently very simple, management functionality. Previously, this functionality was implemented rather coarse such that it was not very user friendly and cumbersome to extent.

The new online management functionality is based on new embedded CLI functionality, which I recently added to cli4clj. For this post, I will just go with the summary that this will help to improve usability and extendibility of the online management functionality. I will write more about this cli4clj embedded CLI feature in a dedicated future post.

Updated Version Number

Since quite some time, I hade been using version numbers of 1.99.x. Originally, my intent for using the 1.99.x versions was that the release of the next major version, 2.x.y, was imminent.

I “just wanted to implement that one big change” and then switch to 2.x.y. Unfortunately, I never really got around to implementing that change and was distracted by other more urgent or fun aspects. Thus, I did not really make it to get rid of that 1.99.x version.

However, eventually, I got annoyed by this 1.99.x thing going on for so long. So, I finally decided to switch back to some more decent versioning and just do the jump to 2.x.y.

In future, I may need to do another major version bump when the “big change” finally comes. However, so far, I think it is much easier to have a reasonable version scheme rather than continually going on with this 1.99.x stop gap solution.

End

In this post, I announced the release of bowerick 2.1.0. Besides some technical details about new features etc. I also tried to provide some rationale for the new major version number.

In addition, I somewhat teased you with an outlook on some more future posts. So, I actually owe you at least two more posts, right now.

I hope you consider bowerick useful and am happy about your feedback and comments. As you could see in this post, sometimes, coming up with a feature request may actually lead pretty quickly to a corresponding implementation.

As usual, I am very happy about any constructive feedback.

Posted in Announcements, bowerick, Libs. | Tagged , , , , , | 2 Comments

## bowerick 1.99.17 released.

In this post, I briefly announce the release of bowerick version 1.99.17. Compared to the next post I have planned, this is not the chronological but the, in my opinion, logical order.

Changes compared to the previous bowerick release are:

• Enable heartbeat for Web Socket (WS) client connections.
• Add simple examples for connecting to the broker from a web browser.

Having a heartbeat for WS client connections avoids disconnects due to timeout when using bowerick as WS client. The default values (10000 in ms) can be overridden by re-binding the *ws-client-ping-heartbeat* and *ws-client-pong-heartbeat* global dynamic variables.

In addition, I added two simple examples for using Message-oriented Middleware WS connections from a web browser. Playing with these examples actually was what motivated me to add the WS client heartbeat as the timeout disconnects became a little annoying over time. In the next post or posts, I plan to write about the WS web browser examples.

I hope bowerick is useful for you. I am happy about any constructive feedback and criticism.

Posted in bowerick, Libs. | Tagged , , , | 3 Comments

## Video of My #clojuredconf / @clojuredconf Talk: “Packet Capturing with the JVM and Clojure – Yes, we can!”

The video was uploaded already quite some time ago. However, I still want to put a link on my blog to have all references in a common place.

In case you are interested, here is the teaser or abstract that I submitted for the conference schedule:

“In this talk, it will be shown that packet capturing with Clojure works fast, how it is made fast, and that Clojure offers amazing capabilities for processing such high-throughput stream data. Following a bottom-up approach, the covered topics include:

• integration of low-level packet capturing with the JVM via JNI,
• acceleration of packet capturing with the JVM up to the magnitude of 10 GBit/s speed,
• a Domain Specific Language (DSL) for extracting information from raw binary data,
• and a self-adaptive mechanism for automatically adjusting the data processing at run-time. While this talk focuses on packet capturing, the presented topics can also be applied to other application fields.”

The slides of the talk can be found in an earlier post.

Once more, many many thanks to everyone involved in :clojureD: organizers, attendees, speakers, and everyone else. :clojureD was a fantastic experience as speaker, as attendee to the other talks, and as part of the many great conversations with the very nice people around.

## test2junit Version 1.3.0 Released

As my last post about test2junit is some time ago, here is the very brief description: test2junit is a Leiningen plug-in that provides improved JUnit XML format output when executing clojure.test tests. In addition, it allows to automatically run the corresponding JUnit Report Ant task for generating HTML from the XML output. An example is available on the test2junit repository.

In this post, I announce the release of version 1.3.0 and briefly discuss the changes since the last announcement. The most relevant changes since the last announcement are:

With the fix to issue #10, the file and source code line at which a problem occurred are properly shown. It turned out that the culprit was just a wrong index. Thanks a lot to Andrew for spotting and reporting the issue!

System Properties of the JVM executing the tests are now included. Before just nothing was emitted for this part of the XML. An example for how this looks can be seen in the example output by scrolling to the bottom and clicking on “Properties”.

Furthermore, Stderr and Stdout are also included in the XML output. An example for how this looks can be seen in the example output by scrolling to the bottom and clicking “System.err” or “System.out” respectively.

The content shown for “System.out” actually includes Stderr as well. The reason for this is that I think it would make reading the output more complicated when Stderr and Stdout are separated into two different outputs. It would be, e.g., more difficult to related messages from Stdout and Stderr to each other. The output for “System.err” only shows Stderr. This makes it easier to check for important output from Stderr.

One potential drawback of including Stderr and Stdout in the XML output is that the output has to be redirected for capturing it such that it is not printed in real-time in the console right now. To accommodate this, the output is printed after collecting it.

This has two consequences: instead of being printed in “real-time” the output is printed in batches after the capture of one unit of output was done. In addition, Stderr and Stdout are both printed to Stdout. However, so far, I did not encounter negative effects of this in practice yet.

I hope that you consider the changes useful. In case you have comments, requests, or other feedback, it would be great if you send me your feedback.

## clj-net-pcap Version 1.7.1 Released

I actually uploaded clj-net-pcap version 1.7.1 quite some time ago. Unfortunately, I was very busy and couldn’t even write an announcement. In this post, I briefly announce the clj-net-pcap version 1.7.1 release.

I did the work on clj-net-pcap version 1.7.1 in preparation for the :clojureD conference. The most relevant changes for version 1.7.1 are:

• Use latest cli4clj version.

My motivation for these changes was to provide a better live demo of clj-net-pcap. With the latest cli4clj version, the interactive command line interface (CLI) offers more convenient functionality.

With the stderr-forwarder-fn, the output printed for each packet is printed to stderr instead of stdout. By redirecting stderr to a named pipe or file, it is now possible to use the interactive CLI without the CLI being flooded with the packet output. This, in my opinion, eases the use of clj-net-pcap for simple experiments or demonstrations a lot.

Below, a screenshot of a demo setup is shown. On the top left, the clj-net-pcap interactive CLI is shown. On the top right, another shell is shown that was used to generate traffic via the ping command. On the bottom, the output from the named pipe to which the stderr output was redirected is shown.

The following listing shows how the named pipe is created and the new stderr forwarder is used to redirect the packet capture output to it:

mkfifo stderr-pipe
java -jar clj-net-pcap-1.7.1-standalone.jar -F stderr-forwarder-fn 2> stderr-pipe


The remainder of the example as shown in the screenshot above is simple to print what is written to the pipe via “cat” and to generate the traffic via “ping”.

I hope that this will be as useful for you as it was for me. Comments etc. are, as usual, highly appreciated. Thanks.

Posted in Announcements, clj-net-pcap | | 2 Comments

## Slides of my :clojureD Talk #clojuredconf

I uploaded the slides for my :clojureD talk:

Packet Capturing with the JVM and Clojure – Yes, we can!

Huge thanks to the organizers, speakers, attendees, and everyone else involved in :clojureD! 🙂

Posted in Uncategorized | 1 Comment

## Note to Self: Clojure Code in LaTeX vs. Copy&Paste

This post is a somewhat extended “Note to Self” in the sense that some of this may also hopefully be useful for others.

I like LaTeX a lot and its “listings” package is great for including source code in documents. However, while the source code typically looks good in the generated documents, there are problems when copying and pasting the code from the listings.

Typically, one of the reasons of providing a code example is that someone can reproduce certain things. Hence, it would be great if it was possible to directly copy and paste code from listings generated with LaTeX to a source code file or REPL. Unfortunately, there are some issues related to this.

At first, I will show a minimum working example for illustrating the problem. Afterwards, I show a minimum working example with the solution I use so far. The current “solution” is based on various things I could find on the Internet and merging all the bits and pieces. Actually, when searching for “LaTeX” and “lstlisting” and “copy paste” or “source code” one can find many posts, questions, etc. with all sorts of answers.

The Problem

On my machine, the following “naive” LaTeX document

\documentclass{scrartcl}

\usepackage{listings}

\begin{document}

\begin{lstlisting}
(let [my-list '(1 2 3)
my-product (reduce * my-list)]
(println "My-Product (1*2*3): " my-product))
\end{lstlisting}

\end{document}


produces the output shown in the following screenshot:

The problem is that copying and pasting this from a PDF viewer, such as Evince, results in the following when pasted:

( l e t [ my− l i s t ’ ( 1 2 3 )
my−p r o d u c t ( r e d u c e ∗ my− l i s t ) ]
( p r i n t l n ”My −Product ( 1 ∗ 2 ∗ 3 ) : ” my−p r o d u c t ) )


Clearly, this cannot be used for pasting it into a source file or the REPL. The most obvious problem is the spacing of characters but there are also issues related to quotation marks etc.

My current Solution

In order to fix these copy&paste issues, I currently use the following solution:

\documentclass{scrartcl}

\usepackage{listings}
\usepackage[T1]{fontenc}
\usepackage{textcomp}
\usepackage[variablett]{lmodern}

\lstset{
basicstyle=\ttfamily,
columns=fullflexible,
upquote,
keepspaces,
literate={*}{{\char42}}1
{-}{{\char45}}1
}

\begin{document}

\begin{lstlisting}
(let [my-list '(1 2 3)
my-product (reduce * my-list)]
(println "My-Product (1*2*3): " my-product))
\end{lstlisting}

\end{document}


This produces the following output:

Copying and pasting this from the PDF, results in the following:

(let [my-list '(1 2 3)
my-product (reduce * my-list)]
(println "My-Product (1*2*3): " my-product))


Besides the indentation not being correct, this is actually something that can be run as-is in a REPL or be used in a source code file.

The credit for the solution goes to all the helpful posts on this topic. As I had to merge various solution approaches from multiple posts and play a bit with all the input before coming to the above “solution”, I, unfortunately, did not keep track of the references that led me to this state. So I just go with a general “thank you” to the nice and helpful LaTeX community.

## Using the Clojure-based bowerick MoM/JMS Library in Java

First of all, for those who did not run across bowerick yet, here is a brief description: bowerick is a Clojure library that bundles the functionality of various Message-oriented Middleware (MoM)/Java Message Service (JMS) implementations and provides one centralized abstraction for these. It currently supports Stomp, OpenWire, MQTT, and Stomp via WebSockets. You can find more information in other posts I wrote about bowerick.

In this post, I briefly discuss how bowerick, which is written almost entirely in Clojure, can be used in Java. In a sense, this completes a full circle as most (all? But I did not double-check that.) of the MoM/JMS libraries employed in bowerick are Java-based.

My intention is to keep the post focused and concise and to present the most interesting things straight away. Hence, the outline is a bit unusual compared to my other posts:

• bowerick and Java
In this part, I present some Java code snippets etc. for using bowerick in Java.
• bowerick Design Considerations for Java Interoprability
Here, I write about some measures I took in order to allow/ease Java interoperability.
• Closing Remarks

bowerick in Java

In order to demonstrate the use of bowerick in Java, I created a simple example project on github. The example project was set up as Maven project in which the examples were implemented as JUnit test cases. In this section, I will present some snippets of this project.

All transports (Stomp, OpenWire, etc.) supported by bowerick in Clojure can also be used in Java. However, as additional measures have to be taken to make Clojure code available in Java, not all Clojure functions are exposed to Java. Still, I think that the most important features are available in Java as well. The bowerick features currently available in Java are:

• Starting and stopping an embedded JMS broker
• Creating a producer and sending messages
• Creating a consumer and receiving messages
• Selected Serialization Options:
Default, JSON, Kryo (via Carbonite) with Optional LZF Compression

The central point for using bowerick in Java is the JmsController class. E.g., starting and stopping of an embedded broker can be done via an instance of this class as shown in the following listing:

jmsController = new JmsController("stomp://some-url:port");
jmsController.startEmbeddedBroker();
...
jmsController.stopEmbeddedBroker();


This example shows the start/stop of a broker with a single Stomp transport. In addition, a broker with multiple transports can be created by passing a List of Strings to the JmsController constructor as shown in the following listing:

List<String> urls = Arrays.asList(new String[] {
"stomp://127.0.0.1:1701",
"tcp://127.0.0.1:1864",
"ws://127.0.0.1:8472",
"mqtt://127.0.0.1:2000"});
jmsController = new JmsController(urls);
jmsController.startEmbeddedBroker();
...
jmsController.stopEmbeddedBroker();


For the creation of consumers and producers, static methods in JmsController are used. In the following, a simple producer using the example of sending a String is shown:

JmsProducer producer = JmsController.createJsonProducer(
"stomp://127.0.0.1:1701",
"/topic/test, 1);
producer.sendData("A test string");
producer.close();


Similarly, a consumer for receiving data can be created via a static method in JmsController. The following listing shows the creation and usage of a consumer:

final CountDownLatch cdl = new CountDownLatch(1);
final StringBuffer received = new StringBuffer();

AutoCloseable consumer = JmsController.createJsonConsumer(
"stomp://127.0.0.1:1701,
"/topic/test",
new JmsConsumerCallback() {
public void processData(Object data) {
cdl.countDown();
}
},
1);

cdl.await();

consumer.close();


Note that producers and consumers have to be closed when they are not needed anymore. Producers and consumers implement AutoClosable such that they can also be used, e.g., in corresponding try-catch blocks for auto closing the created instances. Furthermore, the consumer example includes a bit more surrounding code in order to demonstrate the use of the received data.

Instead of Strings, more complex data structures, such as Java lists, maps, or sets, can also be sent via these mechanisms.

Regarding the Maven pom.xml, the parts that have to be added are pretty simple as well:

...
<dependencies>
<dependency>
<groupId>bowerick</groupId>
<artifactId>bowerick</artifactId>
<version>1.99.12</version>
</dependency>
</dependencies>
...
<repositories>
<repository>
<id>clojars.org</id>
<url>http://clojars.org/repo</url>
</repository>
</repositories>
...


bowerick Design Considerations for Java Interoperability

Even though I primarily use bowerick in Clojure, I also tried to implement it already with Java interoperability in mind. In this section, I will outline the aspects that I consider important in this context.

Clojure offers, in my opinion, excellent Java interoperability such that it is very easy to write Clojure projects that can also be used from within Java. However, in scope of bowerick, I came across some things that I found helpful.

In bowerick, consumers and producers are not just pure functions. In fact, a producer, e.g., has to provide functionality for sending data and functionality for closing and freeing its resources, namely the JMS connection. Hence, there has to be some way for dispatching between these two functions. For bowerick, I chose to define producers and consumers as records and to pass the send and close functions as constructor arguments to these records.

In order to ease the use of these records in Java, I defined the records to implement the Java AutoClosable interface. This is shown for the consumer in the following listing:

(defrecord ConsumerWrapper [close-fn]
AutoCloseable
(close [this]
(close-fn)))


From the perspective of Clojure, just passing a function to the consumer as callback for processing the data would have been sufficient. However, in order to ease the interoperability with Java, I chose to specifically define an interface for this:

(gen-interface
:name bowerick.JmsConsumerCallback
:methods [[processData [Object] void]])


In order to prepare the producer functionality to be available in Java as well, I defined another interface for sending the producer data, in addition to AutoClosable. In the following listing, both the interface definition and the record definition are shown in one listing even though they are defined in separate namespaces in bowerick:

(gen-interface
:name bowerick.JmsProducer
:extends [AutoCloseable]
:methods [[sendData [Object] void]])
...
(defrecord ProducerWrapper [send-fn close-fn]
AutoCloseable
(close [this]
(close-fn))
JmsProducer
(sendData [this data]
(send-fn data))
IFn
(invoke [this data]
(send-fn data)))


Closing Remarks

Personally, I think that this can be an example for a symbiotic co-existence of Java and Clojure. My personal impression is that using Clojure helped me a lot during the development of bowerick and that making bowerick available in Java can benefit on the Java side as well. However, this is just my very subjective impression without any real hard figures to “prove” it.

Regarding the current Java interoperability implementation: I use Object as type in some places, such as the JmsController constructor or the JmsConsumerCallback processData method. For the JmsController, currently supported argument types are, String or List. For the processData method, the type strongly depends on the data that was sent and how it got de-serialized. However, Object being somewhat the NULL pointer equivalent of Java, using just Object breaks the benefits of static typing and requires casting etc., which may be considered disadvantageous.

I hope that this is useful for some. Feedback, comments, and constructive criticism, are always appreciated.

## Unit Testing Arbitrary Interactive Clojure Command Line Interfaces (CLIs) with cli4clj – Illustrated using the Example of the Clojure REPL

In my previous posts, I primarily focused on cli4clj itself and on CLIs created with cli4clj. In this post, I want to address a bit the applicability of the cli4clj unit testing functionality for testing “third-party” CLIs. I will do this by showing how to easily unit test the Clojure REPL with the cli4clj unit testing functionality.

As examples for illustrating the working principles in this post, I will use the source code of unit tests that I recently added to the cli4clj repository. The examples will start simple and will be enhanced step by step in order to provide a hopefully fluent and clear line of argumentation.

In the examples, the input commands will be stored in the “in-cmds” vector, which represents the sequence of inputs that are consecutively entered into the REPL. The output is stored in the “out” var. The expected output that will be emitted is shown as the expected string value in the “(test/is (= “expected string” out))” expressions.

At first, I consider a simple execution of the Clojure REPL with cli4clj. This can be considered as a sort of “Hello World” execution example and is shown in the listing below:

(test/deftest clojure-repl-stdout-no-op-test
(let [in-cmds [""]
out (cli-tests/test-cli-stdout
clojure.main/repl
in-cmds)]
(test/is
(= "user=> user=>" out))))


This example illustrates the basic capability of executing the Clojure REPL with cli4clj. One annoying property is that the REPL prompt output “user=> user=>” will clutter up the output. The problem of the REPL prompt cluttering up the output will get even worse when the complexity of the considered scenarios increases.

In the next step, the problem of the REPL prompt showing up in the output will be tackled. For getting rid of the REPL prompt, the function used for printing the prompt can be set to emit an empty string. In the following listing, the “str” function is used, which, when called without arguments, returns the empty string, in order to remove the REPL prompt:

(test/deftest clojure-repl-stdout-no-op-no-prompt-test
(let [in-cmds [""]
out (cli-tests/test-cli-stdout
#(clojure.main/repl :prompt str)
in-cmds)]
(test/is (= "" out))))


With the REPL prompt output out of the way, it is time to actually do something. In the next example, the “inc” function is used as a kind of “Hello World” for actually executing some functionality:

(test/deftest clojure-repl-stdout-inc-no-prompt-test
(let [in-cmds ["(inc 1)"]
out (cli-tests/test-cli-stdout
#(clojure.main/repl :prompt str)
in-cmds)]
(test/is (= "2" out))))


In the next step, I show a slightly more complex example for actually doing things with the REPL. This example includes the definition of a var, the use of the var, and the execution of a function that prints to stdout:

(test/deftest clojure-repl-stdout-def-inc-println-no-prompt-test
(let [in-cmds ["(def x 21)"
"(inc x)"
"(println x)"]
out (cli-tests/test-cli-stdout
#(clojure.main/repl :prompt str)
in-cmds)]
(test/is (=
(cli-tests/expected-string
["#'user/x"
"22"
"21"
"nil"])
out))))


Instead of the expected result string, as used in the earlier examples, this example uses “cli-tests/expected-string” to build that string. “cli-tests/expected-string” simply takes care of inserting the proper line separator in between the strings given to it in the argument vector.

The example demonstrates that the REPL works as expected. A var can be defined, used, and println outputs the string an returns nil as expected.

However, there is one peculiarity that may not be obvious at first but may become critical later. In this example execution, the var “x” is defined in the “user” namespace and the namespace is hard-coded in the expected string. Under certain circumstances, e.g., when running the tests with “lein cloverage”, however, the namespace in which “x” will be declared will be different and thus the test will fail.

Luckily, this problem can be easily fixed by using the “*ns*” global variable to get the current namespace. This is shown in the following listing:

(test/deftest clojure-repl-stdout-def-inc-println-no-prompt-test
(let [in-cmds ["(def x 21)"
"(inc x)"
"(println x)"]
out (cli-tests/test-cli-stdout
#(clojure.main/repl :prompt str)
in-cmds)]
(test/is (=
(cli-tests/expected-string
[(str "#'" *ns* "/x")
"22"
"21"
"nil"])
out))))


In this example, the hard-coded string “#’user/x” was replaced with a dynamically constructed string based on “*ns*”: “(str “#'” *ns* “/x”)”. I tested this version with “lein test” and “lein cloverage”.

Last but not least, I also show the use of the recently added string-latch. The string-latch can be particularly useful for “stepping” through the execution of a test.

I will not cover details of the string-latch here. For the discussion here, it is sufficient to say that the string latch “sl” waits for the defined strings in a step-wise way. Once one string was matched, it waits for the next string to occur and so on. Thereby, an optional function can be defined that will be executed when the corresponding string matches. An example for this is shown in the listing below:

(test/deftest clojure-repl-stdout-def-inc-println-no-prompt-string-latch-test
(let [in-cmds ["(def x 21)"
"(inc x)"
"(println x)"]
val-0 (atom nil)
val-1 (atom nil)
val-2 (atom nil)
sl (cli-tests/string-latch
[[(str "#'" *ns* "/x") #(reset! val-0 %)]
["22" #(reset! val-1 %)]
["21" #(reset! val-2 %)]
"nil"])
out (cli-tests/test-cli-stdout
#(clojure.main/repl :prompt str)
in-cmds
sl)]
(test/is (= [(str "#'" *ns* "/x")] @val-0))
(test/is (=
[(str "#'" *ns* "/x")
cli/*line-sep*
"22"]
@val-1))
(test/is (=
[(str "#'" *ns* "/x")
cli/*line-sep*
"22"
cli/*line-sep*
"21"]
@val-2))
(test/is (=
(cli-tests/expected-string
[(str "#'" *ns* "/x")
"22"
"21"
"nil"])
out))))


In this example, the string latch is used to set the atom vars “val-0” to 2. The vars are set to the vector of strings that were so far observed by the string-latch “sl”. It can be seen how the strings accumulate with increasing val-x index.

Please note that the namespace was not hard-coded but that the approach using *ns* was used. Furthermore, note that the string latch also contains the new-line characters, which are matched via the cli/*line-sep* placeholder.

Summary

In this post, I used the Clojure REPL as example to show that the unit testing functionality provided by cli4clj can also be used for testing more interactive CLI applications than ones created with cli4clj. So far, I did not encounter limitations such that from my current point of view, it should be possible to test any interactive Clojure CLI application with cli4clj.

I hope that you consider this post and cli4clj useful. As usual, constructive feedback, criticism, and comments are always welcome.