Friday, October 3, 2008

Subversion 1.5 Merge Tracking

This week, I made it my personal mission to learn as much as I could about branching and merging in subversion. This was prompted by a general need in my department to get a better handle on our large, multi-client, shared codebases.

Up until last year, we used VSS. If you've ever tried using it for branching, it's not pretty. Doable, yes, but not ideal. I'd go so far as to say that for where I work, it simply wasn't robust enough to be an option. Over a year ago, I was given the go-ahead to do some new projects in subversion. This created some short-term and ongoing pain, what with some stuff in VSS And some in SVN. Still, it had to be done. Unfortunately, change is slow in large organizations, rightfully so sometimes. So we still haven't ported everything over to SVN yet, in part because there hasn't been a "killer feature" driving us to do so.

I believe with Subversion 1.5, that killer feature is here: merge tracking.

If you've ever tried merging with Subversion prior to 1.5, you know that you need to do it with revision numbers. This isn't too tough in easy cases, but consider:

You create a branch. It's a long-running feature branch, maybe taking 2 weeks to complete. Ideally, you'd keep that branch up to date with the latest and greatest from trunk frequently, meaning you'd be merging from trunk to branch a lot. This becomes problematic because you have to keep track of your merge revision numbers; otherwise, you can get "double merges". If you don't keep a log, and you/others don't use good commit comments, it doesn't take long before you find yourself in this position. Personally... it's just tough to maintain.

Subversion 1.5 merge tracking aims to eliminate that problem. Essentially, it keeps track of your merges so you don't have to. This way, you simply say "svn merge [TrunkURL]" and it does the deed. It remembers your previous merges. No more double merges.

I've been working with it on the command line for a bit, and so far, it's great. But ideally, this functionality would be built into the GUI tools we've all come to know and love. For me, that's subclipse and tortoise.

However, when I tried to figure out how to get those two apps to merge with the new merge tracking, I got stuck.

And that's the point of this post: how to use these two GUI tools to do merges from trunk to branch, with merge tracking, with svn 1.5

Tortoise

Had I read the excellent Tortoise docs before I started, I wouldn't have struggled at all with this. But I'm a "jump right in" kind of guy, clicking buttons and banging on things. So after it wasn't braindead simple, I gave up and RTFM (after complaining a lot... that's how I roll).

  1. While in trunk, make your changes. Commit
  2. switch to your branch (using tortoise, subclipse, whatever)
  3. Using tortoise, right click on the project in question, select "Merge"
  4. Select "Merge a range of revisions" radio button
  5. in the "URL to merge from", enter the URL to your trunk
  6. Leave the revision range blank. This was the central problem I was having!
  7. Click Next
  8. Do a "Test Merge" to see what it will do. If you're comfy with that, click "Merge"

In retrospect, this is actually really obvious, so maybe I was just having a momentary lapse of common sense. Possibly being confronted with the "revision" box kept my brain in revision number mode. Although I do swear that I tried it without any numbers but got nothing in the test merge.... but anyways.... it's working now.

Subclipse

I upgraded to the latest Subclipse, which as of this writing is the 1.4 tree. I did a team -- merge, got the normal merge dialog, and couldn't figure out how to get it to do a simple SVN 1.5 merge. It requires revision numbers to work correctly.

I was pretty surprised that the new merge tracking wasn't built in to subclipse. So, after complaining (see a pattern?), I joined the subclipse mailing list and posted it as a question. There, I learned that I wasn't missing anything, as I was with tortoise. This functionality is indeed not built into subclipse.

However, it is available in the Collabnet Desktop. (for you fast-and-loose types, here's the update URL for eclipse 3.3: http://downloads.open.collab.net/eclipse/update-site/e3.3)

This is a free plugin that does not replace subclipse but complements it. So I installed it, and now when I do a team -- merge from within Eclipse, I am confronted with a new set of screens. These screens walk you through doing a 1.5 style merge... Worked like a charm!

So far, in my limited play with this, I havent hit any snags.

Bottom line: subclipse doesn't do 1.5 merging, but the collabnet desktop will add this feature right into your existing subclipse 1.4 installation. One note: when I installed it in eclipse's update manager, I didn't hit any snags. But as with all things plugin related, your mileage may vary.

Many, many thanks to the folks at Tortoise and Collabnet for the excellent work!

Thursday, October 2, 2008

Ajax Testing with MXUnit, Webdriver, and Firefox

"Release early and often", is an Agile mantra. I usually wait until something is more stable, but I'm particularly excited about this effort and hope to get some feedback quickly - even better, get some help!

One of the goals we have at MXUnit is to be able to perform not just unit testing, but other types of testing as well - all inside of MXUnit and using CFML. One big gap and need I see is the ability to test ColdFusion applications via the the user interface and to test the UI itself. Testing has some catching up to do with the rapid adoption of jQuery, ExtJs, and other Javascript Ajax frameworks. One effort in particular that caught my attention is Webdriver (http://code.google.com/p/webdriver/). Simon Stewart's smart ideas behind it are to create a simple interface and to be able to use existing browsers. Webdriver has the ability to invoke Firefox and Internet Explorer natively as well as leverage HtmlUnit. HtmlUnit's main advantage is that it's fast. However, it does not handle JavaScript in the same way most user's browser do - it can, at best , emulate the browser. So, how can we then accurately automate testing in common browsers?

This effort, CFWebdriver Experimental (for the moment), allows you to write tests that call URLs, find page elements, write data to form fields, click buttons, and verify output. Again, the Webdriver interface is intended to be simple, which is very appealing.

Here's some example code:

<cfcomponent output="false" extends="mxunit.framework.TestCase">
<cfscript>
function doGoogleSearch(){
driver = createObject("component","cfwebdriver.WebDriver").newInstance("firefox");
driver.get("
http://google.com/");
q = driver.findElement("q");
q.sendKeys("mxunit");
q.submit();
link = driver.findElementByLinkText("MXUnit Blog");
link.click();
assertEquals("MXUnit Blog", driver.getTitle());
}
</cfscript>
</cfcomponent>

What this does is to start Firefox, invoke Google's home page, find the input named "q", enter mxunit into the input, submit the form, find the link text MXUnit Blog, click that link, then verify the expected text in the title tag. It works nicely on Windows and Ubuntu! Here's a screen shot of MXUnit and Webdriver testing Google's Ajax Search API example (http://www.google.com/uds/samples/apidocs/helloworld.html)

cfwebdriver-firefox-mxunit

This is included in the experimental download .... repeat ... experimental. ;-) Visit http://github.com/virtix/cfwebdriver/tree/master and click the Download button. I'll get this in the mxunit subversion repository this weekend - having problems accessing it M-F/9-5.

The installation instructions are in the README in the download, but it's basically copying the Webdriver jars into the ColdFusion classpath, and making sure they are loaded first. I've tested this on CF8 multi-instance installations on Windows and Ubuntu, and Railo 3 on Windows.

This is the preferred method of installation as of today:

1. Copy /webdriverlib to WEB-INF/lib/webdriverlib
2. Edit WEB-INF/web.xml adding the path to the webdriver jars above
to the BEGINING of the cf.class.path element:

<context-param id="coldfusion_context_88">
<param-name>cf.class.path</param-name>
<param-value>

./WEB-INF/lib/webdriverlib,./WEB-INF/cfusion/lib/updates,./WEB-INF/cfusion/lib,./WEB-INF/cfusion/gateway/lib,./WEB-
INF/flex/jars,./WEB-INF/cfform/jars</param-value>
</context-param>

3. Restart the cf instance

4. Try the example1.cfc test and the googleAjaxTest.cfc

Notes: The README should have enough info to get you up and running. The Internet Explorer implementation is very buggy when running inside of CF. It works sometimes and other times not. There's 2 CFCs (wrappers). These contains only some of the full functionality of Webdriver but should be enough to give you a good taste. If the CFC wrappers don't do what you need, you can always go down the java route and instantiate the java representations and work with those instead. Or better yet, contribute ;-)

Happy Ajax testing!

-bill

Labels: ,