Making pretty diagrams with GraphViz
So recently I needed to draw some directed-graph diagrams - work-flow diagrams in this particular instance. After a bit of googling I came across some nice examples of diagrams produced by graphviz - actually what really turned me on to graphviz was this nice set of examples from Kent Bye.
A little tinkering and I soon had my workflow definitions (declared in xml in our application) rendered nicely with GraphViz. The markup language is deadly simple and intuitive, and the directed graphs which it produces are really good looking (I didn't try any other types).
Lets take a look at some examples, starting simply with two nodes related in one direction only. The markup would look like this:
digraph {
"Start" -> "End" [];
}
That's it. No really. This is all you need to create a simple, two-node directed graph diagram. To draw the diagram you need to install graphviz (with a quality OS like ubuntu you just need the magic incantation sudo apt-get install graphviz
and you're up and running). Once installed invoke graphviz like this:
dot -Tpng -oMyGraph.png MyGraph.dot
This invokes the directed-graph drawing tool, tells it to produce a png image from the input file MyGraph.dot
and store it in the output file MyGraph.png
. The resulting diagram looks like this:
Coolness!
Lets get a bit more trick! We can control the size and shape of our nodes, and label the edges:
digraph {
"A" [shape="circle"];
"B" [shape="rectangle"];
"C" [shape="diamond"];
"A" -> "B" [label="A to B"];
"B" -> "C" [label="B to C"];
"A" -> "B" [label="A to C"];
}
Here's the diagram:
Isn't it just too cool? Last one coming up - this time with colours:
digraph {
"Back" [shape="egg" color="green" style="filled" fillcolor="yellow"];
"Forth" [shape="house" color="red"];
"Other" [shape="invtriangle" color="blue"];
"Back" -> "Forth" [color="orange" label="weee"];
"Forth" -> "Back" [color="purple" label="eeew"];
"Other" -> "Forth"
"Other" -> "Back"
}
And here's how it looks:
I realize this just scratches the surface - there's a whole bunch of other node shapes you can use, you can define defaults for nodes and edges once at the beginning of your *.dot file, and much much more.
I only discovered GraphViz about 2 months ago, but since I did i've been using it quite a lot. Its very handy for drawing simple representations of work-flows, navigational flows, any kind of hierarchical structures (trees and graphs), etc., and just so simple to write. Generating the *.dot
syntax programmatically is extremely easy - I now have our app invoking "dot" to generate the graphs on the fly.
Note: Google Charts now has experimental support for (a subset of) graphviz. Check it out here.You don't even have to install graphviz :)
I'll leave you with one more example - a visualisation of a subset of a work-flow from a work-flow system I've been building recently for our Editorial System. The work-flow engine we've built is basically a Finite State Machine (FSM), and a work-flow diagram like this is basically a State diagram for the machine. A workflow thus forms a very nice directed graph, making GraphViz ideal for rendering work-flows:
digraph {
node [shape=circle,fontsize=8,fixedsize=true,width=0.9];
edge [fontsize=8];
rankdir=LR;
"low-priority" [shape="doublecircle" color="orange"];
"high-priority" [shape="doublecircle" color="orange"];
"s1" -> "low-priority";
"s2" -> "low-priority";
"s3" -> "low-priority";
"low-priority" -> "s4";
"low-priority" -> "s5";
"low-priority" -> "high-priority" [label="wait-time exceeded"];
"high-priority" -> "s4";
"high-priority" -> "s5";
}
And here's the GraphViz output:
Comment on this postGWT Widgets with html id attributes
It isn't immediately obvious how to get your GWT widgets to produce id attributes that you can use from external scripts or testing tools like WebDriver or Selenium.
To make your widgets produce id attributes you need to invoke the ensureDebugId
method with an id string, e.g.
Label myLabel = new Label("Say something");
myLabel.ensureDebugId("myLabel");
Now, if you run that you still won't get any id attributes. The clue is in the method name ensureDebugId
. You actually need to inherit the Debug
module in your module descriptor (.gwt.xml
) file. The module to inherit is:
<inherits name="com.google.gwt.user.Debug"/>
Now if you view the source of your label you'll see that it has an id attribute, but you might be surprised that it doesn't quite match the id you supplied (myLabel
). In fact it will say gwt-debug-myLabel
.
Testing GWT apps in dev-mode with WebDriver
Today I've been playing a little with WebDriver for testing GWT apps. First impressions: * WebDriver is awesome - can't wait to try testing more complex web-apps! * WebDriver opens up all kinds of possibilities beyond testing web-apps - for example to drive interactive demos and tutorials. * Did I mention WebDriver is awesome? :)
Preparing the Eclipse project
To get started I made a new toy project to play in. I don't use the Google Eclipse plugins for GWT dev - if you do you could create this project using the wizards instead. I develop in Eclipse, but always set up my projects with Maven, so the first step for me was to create a new maven project:
mvn archetype:create -DgroupId=com.sjl -DartifactId=webdriver
Next, create the Eclipse project so I can use eclipse to edit the maven pom...
mvn eclipse:eclipse
And then edit pom.xml
in Eclipse to add the GWT, JUnit4 and WebDriver dependencies (by default maven adds JUnit 3.8, so I replace that with JUnit 4.4).
To add the GWT dependencies you also need a repository that actually has them - I use the CodeHaus gwt maven plugins during the build step, which also exposes the CodeHaus repository for fetching the dependences:
<pluginRepositories>
<pluginRepository>
<id>codehaus-snapshots</id>
<name>Codehaus plugin snapshot repository</name>
<url>http://snapshots.repository.codehaus.org</url>
</pluginRepository>
</pluginRepositories>
...
<dependencies>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-dev</artifactId>
<version>2.0.4</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-user</artifactId>
<version>2.0.4</version>
<scope>provided</scope>
</dependency>
...
Next the WebDriver dependencies - I got these as transitive dependencies of selenium2:
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium</artifactId>
<version>2.0a4</version>
</dependency>
Having edited the pom.xml
file you need to rebuild the eclipse project using the eclipse maven plugin:
mvn eclipse:eclipse
This will take a little while to download the dependencies if you don't already have them - the gwt dependencies in particular are quite large. Once the command completes you need to refresh the project in Eclipse for it to reload the project classpath.
Creating a simple GWT app
I made a really simple application to play with, consisting of just an EntryPoint that displays a Label, and a PushButton which adds more Label's when clicked. I had just a handful of aims in mind for this first attempt: 1. Get a GWT app to load via WebDriver 2. Find an element which GWT has created (the first Label) 3. Interact with an element that does something (the PushButton) 4. Check that the something actually happened and that we can test for it.
The GWT code for this is extremely simple:
package com.sjl.webdriver.client;
import com.google.gwt.core.client.*;
import com.google.gwt.event.dom.client.*;
import com.google.gwt.user.client.ui.*;
public class SimpleApp implements EntryPoint {
public void onModuleLoad() {
final RootPanel _root = RootPanel.get();
_root.add(new Label("Hello"));
_root.add(newButton());
}
private PushButton newButton() {
PushButton _b = new PushButton("push me");
_b.ensureDebugId("button");
_b.addClickHandler(new ClickHandler(){
public void onClick(ClickEvent event) {
_root.add(new Label("whoop"));
}
});
return _b;
}
}
Pushing the button adds a new Label for each push - something we should be able to detect pretty easily.
My gwt module file looks like this:
<module rename-to="webdriver">
<inherits name="com.google.gwt.user.User"/>
<inherits name="com.google.gwt.user.Debug"/>
<entry-point class="com.sjl.webdriver.client.SimpleApp" />
</module>
Next I made an ApplicationDriver
class to wrap up the details of getting WebDriver to interact with the web app and let our tests deal with the web app in terms of a ubiquitous domain language - overkill for this simple example i'm sure, but if you want to write expressive tests for a complex web app you really need to do so at a higher level than WebDriver invocations.
The ApplicationDriver
class looks like this:
package com.sjl.webdriver;
import java.util.*;
import org.openqa.selenium.*;
import org.openqa.selenium.firefox.*;
public class ApplicationDriver {
private WebDriver driver;
public ApplicationDriver() {
System.setProperty("webdriver.firefox.profile", "default");
driver = new FirefoxDriver();
driver.get(
"http://127.0.0.1:8888/index.html?" +
"gwt.codesvr=127.0.0.1:9997");
}
public void pushTheButton() {
WebElement _element = driver.findElement(
By.id("gwt-debug-button"));
_element.click();
}
public int countLabels() {
List<WebElement> _elements = driver.findElements(
By.className("gwt-Label"));
return _elements.size();
}
public void quit() {
driver.close();
}
}
Now there are a few things worth noting here, as it took me a while to arrive at this point with something that actually worked.
First of all, notice that i'm using the FirefoxDriver
implementation. I had started out with HtmlUnitDriver
which works just fine for testing a compiled GWT app (provided you create it with javascript support - new HtmlUnitDriver(true)
- and ignore the warnings about x-javascript) but I wanted to test during development by running OOPHM.
Stupidly I was trying to get this to work with the HtmlUnitDriver
for a while, til I had a Homer Simpson moment (doh!) and realized that of course HtmlUnitDriver
can't work - it doesn't have a GWT plugin :).
Secondly, the FirefoxDriver
implementation starts up an instance of Firefox with a profile called "WebDriver". If you don't have a profile with that name Firefox will create one when it starts, but it won't have any of your plugins (including the GWT plugin!).
Creating a Firefox profile for WebDriver
Its easy to create a new profile (close all instances of firefox down completely then run it from the cmdline with the -profilemanager switch) - precise instructions vary by platform. I created the profile then just copied my existing profile contents to it to save installing all the plugins again. In the sample code above i've told WebDriver to use the "default" profile instead by setting a system property.
Finally, note that I'm finding the PushButton
by id lookup. To make this work you have to force GWT to spit out an id for the element, and allow for the fact that GWT adds a prefix ("gwt-debug-") to the id you specify. To force GWT to produce id's for your Widget elements:
- Inherit the Debug module in your module descriptor file (.gwt.xml)
- Set an id on the Widget using widget.ensureDebugId("theId");
Last of all I built the testcase on top of the ApplicationDriver
, starting with a test to detect the first Label, then a test to click the PushButton and check that a new Label is added. The testcase looks like this:
package com.sjl.webdriver;
import static junit.framework.Assert.*;
import org.junit.*;
public class TestSimpleApp {
private ApplicationDriver driver;
@Before
public void openBrowser() {
driver = new ApplicationDriver();
}
@After
public void closeBrowser() {
driver.quit();
}
@Test
public void canDetectALabel() {
assertEquals(1, driver.countLabels());
}
@Test
public void pushingTheButtonAddsALabel() {
assertEquals(1, driver.countLabels());
driver.pushTheButton();
assertEquals(2, driver.countLabels());
}
@Test
public void pushingTheButtonAgainAddsAnotherLabel() {
assertEquals(1, driver.countLabels());
driver.pushTheButton();
assertEquals(2, driver.countLabels());
driver.pushTheButton();
assertEquals(3, driver.countLabels());
}
}
That's it ... start up GWT OOPHM, run the Junit testcase, and marvel as Firefox starts up, runs the app, and shuts down again (3 times - once for each test), leaving you with a nice green bar in JUnit. I'm sure that restarting firefox between each test would be a bad idea - slooooow - in practice, I just wanted to try it to see that it worked :)
The next interesting test to play with will be testing asynchronous activity like AJAX requests. I'm hoping there'll be some nice Patterns described by the WebDriver community for writing such tests.
Comment on this postHow to clear memory caches in ubuntu
While performance testing some mysql queries in ubuntu I came across the old problem that even after restarting mysql to clear its own buffers, the OS caches speed up consecutive runs of the same query. In order to get consistent results you need to flush both the mysql buffers and the native OS buffers.
Flushing the mysql buffers is as easy as restarting mysql (sudo service mysql restart
in Maverick). Flushing the OS buffers is also dead easy:
sudo sh -c "sync; echo 3 > /proc/sys/vm/drop_caches"
I have system-monitor gauges on my taskbar showing cpu, memory and disk activity, and its a beautiful thing to see the cache suddenly get wiped :)
Comment on this post