Friday, February 19, 2010

Groovy ANTLR Plugins for Better DSLs

DSLs, ANTLR, and Groovy in one blog post? Oh yes, this should be good: a trifecta of interesting keywords.

There is a horrible scourge upon Groovy based Domain Specific Languages, and no I'm not talking about curly braces. That awful syntax blocking our users from natural language based productivity is that insidious amalgamation of about 5 pixels called "the comma". If only we could rid ourselves and our users of this terrible burden!

Some are trying: GEP-3 is the Groovy Enhancement Proposal called "Command Expression based DSL" that will allow some commas to be optional. While interesting, there has not been much public activity on it recently. One of the requirements of dropping commas from methods invocations is that "the evaluation must be easily explainable". I think most people agree that we have more work to do before the proposal is easily explained.

So where does that leave us? Does Groovy force these spurious commas on unsuspecting programmers? Hardly. The compiler architecture of Groovy is fairly open, and with a little creativity you can make the commas optional in your DSL. As long as you have access to the CompilerConfiguration then you have options, whether it be an AST Transformation or an ANTLR Plugin. And remember, if you are using GroovyShell then you have access to it.

As an example, consider the easyb syntax for behaviors. What I would like to see is the comma dropped. Mmmm... it looks so much better without the commas:

given "some data" {
println '... setting expectations'
}
when "a method is called" {
println '... calling some method'
}
then "some condition should exist" {
println '... making an assertion'
}

At some point in the compilation process, this source code will be represented as a text stream. What we're going to do is intercept that text stream, and provide some simple rewrite rules to add the comma in where it should be. An ANTLR plugin can intercept this text, add commas into it, and then pass it only to the Groovy compiler: Groovy is none the wiser. There is some boilerplate wiring together to do, but the bulk of the work is defining the rewrite rule (also known as a production). For the simple case, we can use a regular expression to add the comma in:
String addCommas(text) {
def pattern = ~/(.*)(given|when|then) "([^"\\]*(\\.[^"\\]*)*)" \{(.*)/
def replacement = /$1$2 "$3", {$4/
(text =~ pattern).replaceAll(replacement)
}

Does a regular expression scale well to larger problems? No really. At some point you will need an alternative (maybe even at this point!) For instance, this Regex matches nested quotes, but the quotes must be double quotes, and Groovy's single quotes and multiline strings are not supported. Oh well, it is just an example.

The "wiring together boilerplate" consists of subclassing AntlrParserPlugin so that you can write the text and subclassing ParserPluginFactory so you can wire in your AntlrParserPlugin subclass. The ParserPluginFactory can then be passed directly to the CompilerConfiguration which is passed to GrovoyShell. That makes no sense to me even as I write it, so it is probably best to go look at the full source code listing in Groovy Web Console.

For those of you using browsers not supporting anchor tags, here is the code inline:


class SourceModifierParserPlugin extends AntlrParserPlugin {
Reduction parseCST(SourceUnit sourceUnit, Reader reader) throws CompilationFailedException {
def text = addCommas(reader.text)
StringReader stringReader = new StringReader(text)
super.parseCST(sourceUnit, stringReader)
}
}

def parserPluginFactory = new ParserPluginFactory() {
ParserPlugin createParserPlugin() {
new SourceModifierParserPlugin()
}
}

def conf = new CompilerConfiguration(pluginFactory: parserPluginFactory)
def binding = ...
def shell = new GroovyShell(binding, conf)

And once you have a GroovyShell you can evaluate the world! Including the pseudo-easyb script from the beginning of the post. It runs with no problems... missing commas and all.

ANTLR plugins have been around Groovy for a long time, and this example is based off of Guillaume Laforge's famous Groovy Web Console Script #3. There is nothing particularly hard about writing an ANTLR plugin, but there might be something difficult with maintaining it. DSLs come with a host of issues including versioning. If you create an external DSL then you've published a language. It's fun at first but not so much later.

And there you have it. No more of those despicable commas! Now we just need to do something about those dispicable DSLs.

Testing Asynchronous Code with GPars Dataflows

In my last post I showed how to use JConch 1.2 to unit test asynchronous code. It contains a locking/barrier mechanism that allows you to gracefully tell your unit test how to wait and proceed for off thread events without sleeping or polling. Whee!

As a small example, here is how the TestCoordinator would be used to test a mouse click event that happens on a different thread:

TestCoordinator coord = new TestCoordinator()

MyComponent component = new MyComponent()
component.addActionListener({ ActionEvent e ->
assert "click" == e.actionCommand
coord.finishTest()
} as ActionListener)

component.click()
coord.delayTestFinish(1, TimeUnit.SECONDS)

If you are living in a Groovy world, then you should consider using GPars Dataflow variables instead of the JConch TestCoordinator. The advantage of using Dataflows is that you get a similar thread coordination API but also get a variable capture API, allowing you to move assertion statements to the end of your unit test where they belong. If you are looking for a clean, minimalist approach to capturing off thread events, then dataflows might be exactly what you need:
import java.awt.event.*
import javax.swing.SwingUtilities
import groovyx.gpars.dataflow.DataFlowVariable

def event = new DataFlowVariable()

MyComponent component = new MyComponent()
component.addActionListener({ ActionEvent e ->
event << e
} as ActionListener)

component.click()

ActionEvent e = event.val
assert e.source != null

assert e.actionCommand == "click"

A DataFlowVariable is a little like a Java Future mixed with an immutable, thread safe object. The DataFlowVariable has an underlying value which can be set once, and only once, and trying to access that value will block the caller until the value is available. In this example, setting the value is done in the event handler with the "event << e" line, and accessing the value is done in the "event.val" line. Getting the "val" will block the caller until a value is available. (Remember, in Groovy .val property access is translated into a getVal() method call).

The advantage of using Dataflows in testing, when compared to Java, is that you get a nicer thread synchronization API than what the java.util.concurrent primitives provide. The advantage of Dataflows, when compared to JConch, is that you get to move your assertion methods back to the end of the unit tests. William Wake described a format for unit tests called Arrange-Act-Assert, and it is still one of the best guidelines to writing clear and understandable unit tests. Assertion methods belong at the end of your test and arrangement code belongs at the beginning (note how many mock object frameworks subvert this ordering!). Capturing a variable is a good way to move the assertions to the end of the method, but in most frameworks it requires copious amounts of accidentally complex code.

The downside of using Dataflows is that you once again have to set an @Timeout value on your unit test in the event that you never get a value (otherwise your tests will hang). OH WAIT, that is totally wrong. The GPars authors did provide a timeout value on DataFlowVariable#getVal():
ActionEvent e = event.getVal(4, TimeUnit.SECONDS)
I had you there for a second though, right? Be warned, if the timeout value is exceeded then getVal() returns null, it does not throw an exception.

The only downside to using DataFlow concurrency that I can see is that you actually need to understand a little bit about DataFlow concurrency. Who would have thought? DataFlowVariable is just the tip of the iceberg when it comes to GPars DataFlow support, and is not even the primary use case. The Dataflow User Guide is a good read and will explain a lot more about the concepts and GPars' implementation.

Feel free to try this yourself. You don't even need to download any jars or change your project files. Just add the correct Grapes to the top of your unit test:
@Grab(group='org.codehaus.gpars', module='gpars', version='0.9')
@GrabResolver(name='jboss', root='http://repository.jboss.org/maven2/')
If you need more help, then check out the full example.

Happy Testing!

Tuesday, February 9, 2010

Asynchronous Unit Test Coordination with JConch 1.2

Slowly but surely, JConch Java Concurrency Library is becoming a depot for multithreaded and asynchronous testing on the Java platform. First there was SerialExectorService, allowing you to test with a Java ExecutorService that never started threads (example, javadoc). Then there was assertSynchronized, allowing you to make easy unit test assertions about your object's synchronization policy (javadoc, example).

And now JConch 1.2 offers TestCoordinator: a tool for testing asynchronous code and multi-threaded callbacks. The TestCoordinator solves the problem of how to properly wait for asynchronous method calls without littering your unit tests with wait/join calls or CyclicBarrier/CountDownLatch API calls. In this regard, it provides a useful unit testing abstraction over the great Java 5 concurrency primitives.

Note: If you'd like to skip all the prose then go straight to the example and unit tests that are part of JConch 1.2.

Writing good unit tests requires the same input as writing good production code: practice, dedication, and about 10,000 hours hours experience. Almost any component framework that requires some sort of event listener invariably has a unit test that looks like this:

MyComponent component = new MyComponent()

ActionEvent event
component.addActionListener({ ActionEvent e ->
event = e
} as ActionListener)

component.click()
Thread.sleep(1000)

assert event.source != null
assert "click" == event.actionCommand
OK, usually it's in Java and not Groovy, but you get the point. Life's too short to write Java at home. The simple format is 1) create a component with a listener that captures the input, 2) activate the component, and 3) put the thread to sleep for a while and hope that the event fires before your assertions get run. The obvious problem is that you end up with either slow or intermittently failing unit tests depending on how long you sleep (no, there really isn't a perfect sleep number that avoids both).

In the Java 4 days this was "solved" by monkeying with Object#wait and Object#notify method calls. Ewww. Java 5 introduced CyclicBarrier and CountDownLatch which solved the problem nicely albeit in a primitive way. It is an improvement, but we can do better than this:
@Test(timeout=5000)
public void testClick() {
CyclicBarrier gate = new CyclicBarrier(2)

MyComponent component = new MyComponent()

component.addActionListener({ ActionEvent e ->
assert "click" == e.actionCommand
gate.await()
} as ActionListener)

component.click()
gate.await()
}
This approach allows us to move the assertion methods inside the callback, because we're insured that either the callback will be called or the test will fail with a timeout (the @Test(timeout=5000) bit). It is simpler and hides some complexity, but there is still a bunch of primitives distracting the reader from the core content of the test... what is that (2) parameter on the barrier constructor? Is the timeout value clear or is it hidden within a method annotation? And just what does await() mean? There is a lot of primitiveness involved here, which is what TestCoordinator abstracts over:
import jconch.testing.TestCoordinator

TestCoordinator coord = new TestCoordinator()

MyComponent component = new MyComponent()
component.addActionListener({ ActionEvent e ->
assert "click" == e.actionCommand
coord.finishTest()
} as ActionListener)

component.click()
coord.delayTestFinish(1, TimeUnit.SECONDS)
As you can see, TestCoordinator acts alot like a barrier or latch, but without all the low level API. When you've activated your class under test then you call delayTestFinish(...); the unit test will wait unit the listener calls finishTest(). And if finishTest() has already been called (a frequent possibility with multithreaded systems), then delayTestFinish just proceeds. If the timeout value expires then the test fails. No funny timeout annotation. No funny API. Just a simple coordinator. You want to delay a test until a callback is finished, and the API is designed around this nomenclature.

There are two other options to delay as well:
coord.delayTestFinish(1000) // milliseconds
coord.delayTestFinish() // requires @Timeout value!
For those of you coming from GWT... yes, this is almost exactly the API of GWTTestCase. Use what works, I say.

For more info on TestCoordinator, check out the UnitTest and the example. Happy Testing!

Sunday, February 7, 2010

New in Groovy 1.7.1: Constructor Mocking and Half Mocks

The Groovy MockFor object got some fun new features this weekend: constructor mocking and "half-mocks". The tickets are marked for Groovy 1.7.1, so these features are available in the nightly builds or in the next few weeks as 1.7.1 is released. The easiest thing is, of course, to just build from source.

The first feature is Constructor mocking. It is now possible to specify a return value for mock constructor calls. Here is an example that uses a "Person" object. The "tom" variable is a real Person object, while the "mary" object is not. Thanks to this new feature, instantiating Mary returns to you Tom.

import groovy.mock.interceptor.MockFor

class Person {
String name
}

def tom = new Person(name:'Tom')

def mock = new MockFor(Person, true)

mock.demand.with {
Person() { tom }
}

mock.use {
def mary = new Person(name:'Mary')
assert tom == mary
}
There are two parts to getting this working: the "demand.with" block, where the person constructor is specified to return tom, and the true parameter passed to MockFor. To make MockFor backwards compatible, a flag was needed to control when to allow constructor mocking and when not to allow it. As you can see, after doing this, the tom and mary objects are equal (in fact, the same reference).

The problem with this feature alone is that calling tom.getName() results in a mock exception saying no demand is set. If Tom is a real instance of Person then why would you get a demand exception? Well, within the mock.use block Tom is a mock. The behaviour you probably expect is to have the real Tom methods pass through to the real implementation, which is why "half-mocks" were introduced. Check out how you can specify methods to ignore and pass through to the underlying object:
mock.ignore(~'get.*')
mock.demand.with {
Person() { tom }
}

mock.use {
def mary = new Person(name:'Mary')
assert mary.name == 'Tom'
}
The ignore method takes a pattern to match, and the mock ignores those methods, passing them on to the underlying implementation. So your mock is still a mock, not a real object, but methods are passed through as needed. This is the opposite of how EasyMock partial mocking works, where a partial mock is technically a subclass of the target class, and methods are routed by default to the real implementation. In Groovy, the method are routed by default to the mock.

Fun stuff, and thanks to Paul King for the work!