Thursday, October 30, 2008

Setting up Ubuntu 8.1 For Development

I have to dual boot into Linux to do some ruby work. Sure, I can do most of my dev work in windows but, to be honest, it doesn't work that well for all of my ruby needs. For instance many Gems just won't build on windows especially with my not having VC++ 6.0 installed. Thus, I have to have something else to work in; enter Linux.

If you have been following along you'll know that last year (or earlier this year) I attempted to setup a pure Linux dev environment but one of my projects (a coldfusion+ms sql server 2005) forced me back to windows. So I went back to a pure windows setup. Now, finally, I am compromising.

Fortunately my old posts helped me get my citrix client setup (though I had some new issues with my display adapter). Here follows some tips that helped me get over the hump of getting things installed and working as needed.

  • nVidia Adapter - I actually had a lot of problems with this. I installed from a newly pressed Beta cd for Ubuntu 8.10 and activated the necessary driver; however no matter how hard I tried I couldn't get my video settings to save in the xorg.conf file properly. Fortunately a perusal of a forum post turned up the answer:

    ~$ sudo nvidia-xconfig
    ~$ gksudo nvidia-settings

    The first line backs up the xorg.conf file and creates a new one that the nvidia driver can understand. The second opens the settings dialog as super-user and lets you make your changes. However, before doing this I had to update my system. Even though I seemed to have the latest and greatest version of the drivers an update was required before my machine would boot with the nVidia settings.
  • synaptic package manager - for whatever reason synaptic just won't see all the software that is out there to install. I've enabled all the different repos but synaptic shows me almost nothing. Thankfully, the command line saves the day. First, if you want to find something useful you can type

    apt-cache search {item of interest}
    Where {item of interest} is some string to search for. Then you can use

    sudo apt-get install {x}
    Where {x} is the package to install.
  • irb - Once ruby was installed I couldn't get irb to run from the command line. The problem was irb was called irb1.8 in /usr/bin so I created a symbolic link at /usr/bin/irb that points to /usr/bin/irb1.8 like so:

    sudo ln -s /usr/bin/irb1.8 /usr/bin/irb
    sudo chmod 755 /usr/bin/irb
  • sqllite for ruby If you try to use gem to install sqlite3-ruby it will bitch and moan. You have to use apt-get to install libsqlite3-ruby like so:

    sudo apt-get install libsqlite3-ruby

    Then anything you try to install that needs sqlite support should work OK. Sadly, this was also a pain on windows; but thanks to the RubyOnRails wiki:

    Grab http://www.sqlite.org/sqlitedll-3_6_1.zip and unzip the contents to a location on your path. I use c:\ruby\bin.

    Next, install the SQlite3 gem:

    gem install sqlite3-ruby

    Note, as of 30/08/2008 this can fail because the latest sqlite3-ruby gem does not have a windows version. To get around this, install as so:

    gem install --version 1.2.3 sqlite3-ruby

  • partitioning - Honestly, this should have been obvious but don't try to install while you have a large usb drive connected. The partition manager takes forever to scan the external drive. The partition manager is also pretty unclear with it's graphical display of what your partitions will look like so I used the manual option and setup a 2gb swap and a 13 gb linux partition mounted at /. I don't need anything more complicated.

Tuesday, October 28, 2008

cfclasses directory getting big?

This is probably obvious to most of you out there but, just in case you missed it there is a setting in the CF Admin panel that will help keep your cfclasses directory from getting huge.

On the "Caching" page of CF Admin there is a checkbox titled " Save class files " if you are in development you should seriously keep that UNCHECKED.

Here's an example to illustrate why that is a good idea. We are working on a semi-complicated CF app using Model-Glue and Coldspring. When the site is first hit, after starting CF server, 3,002 .class files are generated and placed in the cfclasses directory. Now, imagine you are editing and revisiting some of those files (or flushing the entire MG cache). That number keeps on growing and growing. Eventually, something will go wrong and you'll want to delete some .class files to make sure you are seeing the latest code. However, because your cfclasses directory is so huge you won't even be able to "cd" into it. Your system will appear to hang. Trying to delete the directories contents will take a long, long time.

What do you do? Well you uncheck that damn box that's what! That will prevent a huge backlog of files from building up in the cfclasses directory in the first place. Trust me, uncheck it.

Monday, October 27, 2008

Javascript: When a String isn't

This is actually pretty simple and is best explained with example code:


var x = "0E1234";

if(x != 0){
alert("ok, makes sense!");
}


If you stick that source code into any browser and try to execute it you'll never see the alert. Look at it again and see if you can tell why?

Javascript is pretty agressive in casting things so if you have an int on the right and a value that can be successfully cast as an int on th left Javascript will cast the second half of the expression to an int as well. 0E1234 casts to an int as 0.

Basically, if you wanted that javascript to do what you expected you'd need to write:

var x = "0E1234";

if(x != "0"){
alert("ok, makes sense!");
}


Note the quotes around the zero now. Now it treats both sides of the expression as a string and voila' the alert box will appear.

===


There is another option to test for equality, the === (triple equal sign). What this does is check for absolute equality in not just value but base type as well.


var x = "0E1234";

if(!(x === 0)){
alert("ok, makes sense!");
}


That would cause the alert to go off as well. Just remember that "0" and 0 aren't the same either so:

var x = "0";

if(!(x === 0)){
alert("ok, makes sense!");
}


would also evaluate to the point where the alert dialog appeared. Pretty simple stuff but it can really bite you when you least expect it.

For instance, let's say you were trying to evaluate a dropdown boxes selected value. The first value {Please Select A Value} evaluates to "0"

...
&tl;option value="0">Please Select A Value</option>
...


If you try to make sure the selected value isn't equal to zero wrap the zero your comparing against in quotes because, otherwise, you'll end up confused as to why "0" doesn't equal 0.

Saturday, October 18, 2008

Coldspring - Config Bean Inheiritance

In one of my apps a large chunk of the basic config data is the same but 2 fields are different depending on which client you are logged in as: the datasource and the clientSchema name. However, each client still needs the rest of that config data; so I rolled a means of supporting inheritance in my coldspring bean. Inheritance of ColdSpring defined properties as opposed to inheritance of functionality specifically.

Every configService bean I have has to extend this:


<bean id="baseConfigService" class="LB2.config.beans.baseConfigService" singleton="true">
<property name="properties">
<map>
<entry key="importDSN"><value>SpecialAccessDBDataSourcePlaceHolder</value></entry>

<entry key="rootPath"><value>C:\dev\websites\WEBSITE\PATH\</value></entry>
<entry key="relRoot"><value>/MySiteRoot/</value></entry>

<entry key="siteEmailAddress"><value>AdminEmailAddress</value></entry>
<entry key="commonSchema"><value>SHARED_SCHEMA_NAME</value></entry>
</map>
</property>
</bean>


Normally I would have to have that exact same stuff pasted into each clients config bean definition which, frankly, sucked because the paths and datasource names change depending on how the customer wants them all setup so then he had to make a bunch of duplicate edits. Not a good plan at all. Now a client specific configService bean looks like this:


<bean id="clientXConfigService" class="LB2.config.beans.configService" singleton="false">

<property name="properties">
<map>
<entry key="dsn"><value>CLIENT_DATASOURCE</value></entry>
<entry key="clientSchema"><value>CLIENT_SPECIFIC_SCHEMA</value></entry>
</map>
</property>

<property name="baseConfigService">
<ref bean="baseConfigService"/>
</property>
</bean>


Now, this might seem kind of obvious so far but really, what I want to be able to do is call my client level config service, and ask for a property that might exist in either the base service or the client service and have it return it without any of my code caring at which level the property is defined. This is taken care of through my baseConfigService.cfc and the configService.cfc.

baseConfigService.cfc



<!---
--->

<cfcomponent name="baseConfigService"
hint="Config Service API.">

<cffunction name="init" access="public" returntype="LB2.config.beans.baseConfigService" output="false"
hint="Constructor. I create a new ConfigService">
<cfset variables.properties = structnew()/>
<cfreturn this/>
</cffunction>

<cffunction name="setProperties" access="public" returntype="void" output="false"
hint="I overwrite all propertie in the configuration service">
<cfargument name="properties" type="struct" required="true" />

<cfset variables.properties = arguments.properties />

</cffunction>

<cffunction name="setProperty" access="public" returntype="void" output="false"
hint="I set a property in the configuration service">
<cfargument name="PropertyName" type="string" required="true" />
<cfargument name="PropertyValue" type="any" required="true" />

<cfset variables.properties[arguments.propertyName] = arguments.propertyValue />

</cffunction>

<cffunction name="getProperty" access="public" returntype="any" output="false"
hint="I get a property from the configuration service">
<cfargument name="PropertyName" type="string" required="true" />

<cfif structKeyExists(variables.properties,arguments.propertyName)>
<cfreturn variables.properties[arguments.propertyName]/>
<cfelse>
<cfthrow type="ConfigSerivce.PropertyNotFoundException" message="Property: #arguments.propertyName# is not known to the config service and thus cannot be retrieved." />
</cfif>

</cffunction>


<cffunction name="removeProperty" access="public" returntype="any" output="false"
hint="I remove a property from the configuration service">
<cfargument name="PropertyName" type="string" required="true" />

<cfif structKeyExists(variables.properties,arguments.propertyName)>
<cfset structDelete(variables.properties,arguments.propertyName)/>
<cfelse>
<cfthrow type="ConfigSerivce.PropertyNotFoundException" message="Property: #arguments.propertyName# is not known to the config service and thus cannot be removed." />
</cfif>

</cffunction>


</cfcomponent>


My configService cfc actually has a child object in it of type baseConfigService while also extending the baseConfigService at the same time.

configService.cfc



<cfcomponent name="ConfigService"
hint="Config Service API." extends="baseConfigService">

<cffunction name="init" access="public" returntype="LB2.config.beans.ConfigService" output="false"
hint="Constructor. I create a new ConfigService">
<cfset variables.properties = structnew()/>
<cfset variables.baseproperties = "" />
<cfreturn this/>
</cffunction>

<cffunction name="getProperty" access="public" returntype="any" output="false"
hint="I get a property from the configuration service">
<cfargument name="PropertyName" type="string" required="true" />

<cfset var val = "" />


<cfif structKeyExists(variables.properties,arguments.propertyName)>
<cfset val = trim(variables.properties[arguments.propertyName])/>
<cfelse>
<cfset val = trim(getBaseProperty(arguments.propertyName)) />
<cfset setProperty(arguments.propertyName, val) />
</cfif>

<cfif NOT LEN(val)>
<cfthrow type="ConfigSerivce.PropertyNotFoundException" message="Property: #arguments.propertyName# is not known to the config service and thus cannot be retrieved." />
</cfif>

<cfreturn val />
</cffunction>

<cffunction name="getBaseProperty" access="private" returntype="any" output="true"
hint="I try to get a property from the base setting object if it exists">
<cfargument name="PropertyName" type="string" required="true" />

<cfset var val = "" />


<cfif isObject(variables.baseproperties)>
<cftry>
<cfset val = variables.baseproperties.getProperty(arguments.PropertyName) />
<cfcatch>
<!--- do nothing if the base fails --->
</cfcatch>
</cftry>
</cfif>

<cfreturn val />

</cffunction>


<cffunction name="setBaseConfigService" access="public" returntype="void" output="false">
<cfargument name="settings" type="any">

<cfset variables.baseproperties = arguments.settings />
</cffunction>

</cfcomponent>


Now you can see, my clientConfig service tries to find the property locally and if that fails then it tries to find it in the baseConfigService. If it finds it in the baseConfigService it caches it locally so it doesn't have to dig down later. If it can't find the property at either level then an exception is thrown.

Incorporating Parent Beans


When ColdSpring 1.2 first came out I had hoped that I could simplify this setup by using the parent attribute and then I could just have the main properties defined in one bean and have the secondary properties defined in each client bean. This basically ends up defining the properties node twice within one bean. It turns out, unsurprisingly, that you can't really do this. What did suprise me was that ColdSpring doesn't throw an exception it just ignores the second properties node (that which is defined in the baseConfigService bean definition).

You could pretty easily refactor what I have done though to support the idea of the parent bean but I'm not sure anything would be gained from it; in fact I think it would probably just cause me to have a less readable coldspring configuration.

Sadly, I can't incorporate the Parent Bean to help improve this method. Both my baseConfigService and my configService define a property element named properties. By defining the parentBean the properties element in the baseConfigService is just ignored.

Thursday, October 16, 2008

MxUnit and CruiseControl on Separate Machines

In my previous efforts over this week of getting Cruise Control working (for Continuous Integration) I was doing it on my dev machine and testing against my dev instance which, obviously, isn't an ideal situation. My Dev instance is in flux while our Continuous Integration (CI) server should have the latest checked in code only.

The CI server was actually updating and maintaining a local copy of the files, it was executing executing its local copy of the test cases, but the tests were testing my dev copies of the production CFCs. What I needed to do was to setup a dev webserver elsewhere and have my local CI server try to execute the tests against that server.

Overall this change was fairly easy but I made a few mistakes along the way so I just figured I would detail my successful solution for anyone else interested.

Build.xml


The first thing I did was break my ant Build.xml file up into three separate ones. The first is the core build file and 95% of the targets stayed in it. The other two, dev_build.xml and ci_build.xml set some environment specific properties and call the fullTestSuite target a little differently.

The main differences between the two are the setting of the host and siteBaseDir which mxUnit needs based on the different types of tests being run. Plus, in the dev_build.xml I update from svn before running the full test suite (and sometimes before the others as well) while the ci_build already gets updated by the CI task.

I can't have the ci_build update from svn because ant doesn't access the shared drive and using the d:\... path causes the build to break in most other targets other than the MxUnit ones.

dev_build.xml



<project name="Listbuilder USAF" default="runAllTests" basedir=".">


<import file="base_build.xml" />


<!-- PROPERTY DEFINITIONS -->
<property name="host" value="localhost" />
<property name="siteBaseDir" value="${basedir}" />



<!--- TEST SUITES -->

<target name="runAllTests" target="svnAntUpdate">
<antcall target="testWithReport" />
</target>

<target name="runBeanTests">
<antcall target="beanTests" />
</target>

<target name="runDaoTests">
<antcall target="daoTests" />
</target>

<target name="runGatewayTests">
<antcall target="gatewayTests" />
</target>
</project>

ci_build.xml



<project name="Listbuilder USAF" default="testWithReport" basedir=".">


<import file="base_build.xml" />


<!-- PROPERTY DEFINITIONS -->
<property name="host" value="oak" />
<property name="siteBaseDir" value="d:\inetpub\wwwroot\listbuilder" />



<!--- TEST SUITES -->

<target name="runAllTests">
<antcall target="runFullTestSuite" />
</target>

<target name="runBeanTests">
<antcall target="beanTests" />
</target>

<target name="runDaoTests">
<antcall target="daoTests" />
</target>

</project>

Webserver


Obviously I had to setup a new copy of my website on the dev server, create the proper mapping and datasource entry. I didn't have to actually configure the site since, at the moment I don't have Selenium or anything working and my unit tests don't really need the site to work, they just need the components to be tested to be available. I checked out a copy of the website on the dev box and then created a mapping to the server/drive for the dev machine that has the website on it.

CruiseControl Service


I had to tell the cruise control service on my machine to start as me so that it could have access to the network drive that my dev machine is sharing. Without this I couldn't have CruiseControl execute an ant task remotely to update the dev instance of the site.

To configure the service to start as a specific user I opened the Services Control Panel (Start -> Admin Tools -> Services), find the Cruise Control service, open it, go to the second tab (login) and provide the correct login information.

CruiseControl config.xml


If you have been following my trials and tribulations you'll know i've run into some svn problems due to incompatiable clients. The problem still exists if I try to use the CruiseControll svnbootstrapper task. I just can't do it. Fortunately I figured out a work around.

Under the schedule task there is a "composite" task that you can wrap a variety of build tasks in.


<composite>
<!-- becuase I can't use the svnbootstrapper (too old svn client version) I do this to work around it -->
<ant anthome="apache-ant-1.7.0" buildfile="projects\${project.name}build.xml" target="updateBuildFile" />
<!-- update our test server (oak) -->
<ant anthome="apache-ant-1.7.0" buildfile="\\oak\d$\inetpub\wwwroot\listbuilder\build.xml" target="svnAntUpdate" />
<!-- now do the real build -->
<ant anthome="apache-ant-1.7.0" buildfile="projects\${project.name}\ci_build.xml" target="testWithoutReport" />
</composite>


The first thing I do is update my local build file to make sure it is solid, then you'll notice that second ant build action is updating my remote site instance. Finally the actual build happens. All the logging still happens on my local CI instance and the final reports that are generated by CruiseControl are pretty nice and detailed.

build.xml


Yeah, I already talked about this but my base build.xml file has some changes in it so that I am certain to get as useful of information as possible in my CI build reports.

Problems>


Initially I had a target to test my beans, another to test my daos, another for gateways and so on. Then my full test suite target would then "depend" on each of those targets. The problem was the MxUnit logs were overwritten by each subsequent target and I ended up with insufficient information in the build report.

So then I started having a single target that would run the mxunit task on each directory (recursively) like so:

<target name="allUnitTests" depends="testSetup">
<mxunittask server="${host}" verbose="true" outputdir="${output.dir}" errorproperty="mxunit.error" failureproperty="mxunit.fail">
<directory path="${siteBaseDir}\unittests\model\bean" recurse="true" componentPath="lb2.unittests.model.bean" packageName="lb2.unittests.model.bean" />
<directory path="${siteBaseDir}\unittests\model\dao" recurse="true" componentPath="lb2.unittests.model.dao" packageName="lb2.unittests.model.dao" />
</mxunittask>
</target>


While this solved the problem of overwritten log files it created a new problem. When a test failed in a specific CFC the report wasn't specific enough about the cfc that failed. For instance, if the bean directory had a problem then the name of the test that failed would be identified but the class would be "bean" (the last part of the componentPath for the directory). That makes debugging a broken build kind of difficult; especially if you have some generic tests that run on each object in that directory.

The Solution


In order to get good, solid, precise reporting I had to use the testcase MxUnit task as follows:

<target name="runFullTestSuite" depends="testSetup">
<mxunittask server="${host}" verbose="false" outputdir="${output.dir}" errorproperty="mxunit.error" failureproperty="mxunit.fail">
<!-- bean test cases -->
<testcase name="lb2.unittests.model.bean.clientCustomQuestionTest" packageName="lb2.unittests.model.bean.clientCustomQuestionTest" />
<testcase name="lb2.unittests.model.bean.clientRegionTest" packageName="lb2.unittests.model.bean.clientRegionTest" />
<testcase name="lb2.unittests.model.bean.facilityTest" packageName="lb2.unittests.model.bean.facilityTest" />

<!-- dao test cases -->
<testcase name="lb2.unittests.model.dao.customGuidanceTest" packageName="lb2.unittests.model.dao.customGuidanceTest" />
<testcase name="lb2.unittests.model.dao.regionTest" packageName="lb2.unittests.model.dao.regionTest" />

<!-- gateway tests -->
<testcase name="lb2.unittests.model.gateway.clientEMSGatewayTest" packageName="lb2.unittests.model.gateway.clientEMSGatewayTest" />
</mxunittask>
</target>


This makes sure the end report specifies the exact object that had the failed test in the final report. So if the clientEmsGatewayTest had a failure on the test "testGetXbyY" the report would say failed test "testGetXbyY" in class "clientEmsGatewayTest" instead of failed test "testGetXbyY" in class "gateway" as the prior technique did.

EditPlus and MxUnit Tests Directory Testing Tool

Sometimes it is useful to be able to fairly quickly fire off a directory of unit tests using MxUnit. EditPlus and the UserTool functionality come in handy once again:


  1. Create a new user tool; I called mine MxUnitTest a Directory

  2. Command: c:\Program Files\Internet Explorer\iexplore.exe

  3. Argument: http://localhost/mxunit/runner/HtmlRunner.cfc?method=run&test=$(FileDir)&output=html&componentPath=$(Prompt)



The $(Prompt) argument is there to ask you what the component path is. For example I just ran this one against lb2.unittests.model.bean - it wants the component path to the directory so make sure you don't include the final portion that would point to a specific component.

The ($FileDir) argument basically grabs the full path to whatever directory your currently active file is in.

EditPlus with MxUnit Tests User Tool

Today I decided I wanted a quick way to launch a specific MxUnit test file from within EditPlus. It turns out the handy UserTools menu comes to the rescue again.


  1. Create a new User Tool - I called mine UnitTest

  2. Command: c:\Program Files\Internet Explorer\iexplore.exe

  3. Argument: http://localhost/$(ProjectName)/unittests/$(Prompt)/$(FileName)?method=runTestRemote



You'll notice I have the argument ${ProjectName} in there. I did this so my tool would be fairly generic and usable across each project I'm working on. In order for it to work, however, you need to use EditPlus's built in "Project" tool and create a project. Your project name should match up to whatever your projects webroot directory is called. So if you are working on a project accessible via http://localhost/myUnitTestDemo then your project name should be myUnitTestDemo even if the directory your files in is named something else (and you just have it aliased in your webserver).

I also have a $(Prompt) argument there. This lets me specify what directory, relative to my base "unittests" directory the file I am testing is in.

Finally the $(FileName) argument does limit me in requiring that I have the MxUnit test file I want open and active before using the tool. However, that seems like a decent sacrifice to me over having to type in the URL for the entire file each time I want to run a test.

Wednesday, October 15, 2008

Get CruiseControl to Read Your MxUnit Test Results

I just had a minor epiphany on getting my test results to appear in CruiseControl; in my config file I needed to merge the log files.


<log>
<merge dir="projects/${project.name}/testresults/tmp"/>
</log>


That location is where I I had my buildfile putting the MxUnit log files as it generated them. The only key to remember is to not have your build file cleanup the log files until the next time you run the build. So, now, my build file, upon starting, removes the old build log directory and then recreates it, populates it, and finishes. Then CruiseControl sucks those log files into its own log file and displays the results in the dashboard (or in the email that is sent).

Here is an example:


Test Suites (2)
lb2.unittests.model.dao.customGuidanceTest
Tests: 1, Failures: 0, Errors: 0, Duration: 16.566
lb2.unittests.model.dao.regionTest
Tests: 6, Failures: 0, Errors: 0, Duration: 16.034

Setting Up MxUnit, Ant, and CruiseControl

This is probably going to be a long post. Hopefully it will all be explained OK.

Getting Things Installed


MxUnit


MxUnit is actually pretty easy to setup. Download it and then extract the zip file into your webroot. Once it is there it is basically setup. However, in order to get it to work with Ant there are a couple other small steps.

First, go to your mxunit directory (c:\inetpub\wwwroot\mxunit\) and drill down to the \ant\lib directory. Inside that folder is the file mxunit-ant.jar. Copy this file to your personal user ant directory.

Secondly, go back to your mxunit directory and copy the ant/xsl directory to whatever project you are working on. You'll need those files later if you want to make sense of the mxunit ant output.

Your Personal Ant Directory


On windows this is c:\documents and settings\{username}\.ant\lib. In order to create the .ant directory you probably have to use the command line.

Ant


I had Ant on my machine already thanks to Eclipse. CruiseControl also comes with Ant so you don't really have to do anything special to get Ant up and ready. However, I suggest you also download the ant-contrib which has a bunch of helpful tasks you will end up needing. Once you have this file plop it into your user ant directory as well (see above).

SVN and ANT


If you are using TortoiseSVN 1.5.0 you can use SvnAnt 1.2 RC 1 without problem. Just make sure you have removed all legacy SvnAnt files. Just note that you can't use the SVN config options in CruiseControl (not too big of a deal) becuase the build process will fail saying you need to upgrade your svn client.

CruiseControl


Cruise Control is pretty easy to install and overall isn't too hard to configure. However, there are a couple of small caveats. If you try to base your build config off the example provided you will need to copy all of the Jar files you already put in your personal Ant directory and put them in the CruiseControl/apache-ant-xxx/lib directory. Else your Ant build will probably fail.


Tying It All Together


Ok so now everything is installed but you aren't sure how to get each thing working as a synchronous whole. Hopefully this will help. First off, I really did mean it when I suggested you get the ant-contrib earlier; it comes in handy when using MxUnit with Cruise Control becuase, MxUnit seems to return fairly useless information to its Ant Task.


First off Here is my very simple Build.xml - it only updates stuff from SVN and then runs the MxUnit tests. It has some conditional logic in there so that you can opt to not see the report if you don't want (best option for CruiseControl). While developing I prefer to see the report if an error occurs:


<project name="Listbuilder USAF" default="testWithReport" basedir=".">


<taskdef name="mxunittask" classname="org.mxunit.ant.MXUnitAntTask" />
<taskdef resource="net/sf/antcontrib/antcontrib.properties"/>



<!-- PROPERTY DEFINITIONS -->
<property name="host" value="localhost" />
<property name="junit.out.dir.xml" value="${basedir}/testresults" />
<property name="junit.out.dir.html" value="${basedir}/testresults/html" />
<property name="output.dir" value="${basedir}/testresults/tmp" />
<property name="style.dir" value="styles/ant/xsl" />

<property name="mxunit.error" value="false" />
<property name="mxunit.fail" value="false" />


<!-- SVN INTEGRATION -->
<target name="updateSource"><!-- if svnant wont work.. try this -->
<exec executable="C:\Program Files\TortoiseSVN\bin\TortoiseProc.exe">
<arg value="/command:update" />
<arg value="/closeonend:3" /> <!-- force the dialog to close -->
</exec>
</target>

<typedef resource="org/tigris/subversion/svnant/svnantlib.xml" />
<target name="svnAntUpdate">
<svn username="USERNAME" password="PASSWORD">
<update dir="${basedir}" recurse="true" />
</svn>
</target>



<!-- TEST WRAPPERS -->
<target name="testWithoutReport" depends="allUnitTests">
<if>
<equals arg1="${mxunit.error}" arg2="true" />
<then>
<antcall target="junitreport" />
</then>
<elseif>
<equals arg1="${mxunit.fail}" arg2="true" />
<then>
<antcall target="junitreport" />
</then>
</elseif>
</if>
<antcall target="testCleanup" />
</target>

<target name="testWithReport" depends="allUnitTests">
<antcall target="displayResultsIfProblems" />
<antcall target="testCleanup" />
</target>


<!-- TEST SUPPORT -->
<target name="testSetup" depends="svnAntUpdate">
<mkdir dir="${junit.out.dir.html}" />
<mkdir dir="${output.dir}" />
</target>
<target name="testCleanup">
<if>
<equals arg1="${mxunit.error}" arg2="true" />
<then>
<fail message="Mxunit Had one ore more errors" />
</then>
<elseif>
<equals arg1="${mxunit.fail}" arg2="true" />
<then>
<fail message="Mxunit Had one ore more failures" />
</then>
</elseif>
<else>
<delete dir="${junit.out.dir.xml}" />
</else>
</if>
</target>

<!--- TEST SUITES -->
<target name="allUnitTests" description="Run a dir of tests recursively" depends="testSetup, beanTests, daoTests" />

<target name="beanTests" depends="testSetup">
<mxunittask server="${host}" verbose="true" outputdir="${output.dir}" errorproperty="mxunit.error" failureproperty="mxunit.fail">
<testcase name="lb2.unittests.model.bean.clientCustomQuestionTest" packageName="lb2.unittests.model.bean.clientCustomQuestionTest" />
<testcase name="lb2.unittests.model.bean.clientRegionTest" packageName="lb2.unittests.model.bean.clientRegionTest" />
</mxunittask>
</target>

<target name="daoTests" depends="testSetup">
<mxunittask server="${host}" verbose="true" outputdir="${output.dir}" errorproperty="mxunit.error" failureproperty="mxunit.fail">
<testcase name="lb2.unittests.model.dao.customGuidanceTest" packageName="lb2.unittests.model.dao.customGuidanceTest" />
<testcase name="lb2.unittests.model.dao.regionTest" packageName="lb2.unittests.model.dao.regionTest" />
</mxunittask>
</target>




<!-- UNIT TEST RESULT REPORTING -->
<target name="junitreport" description="Create a report for the test result">
<mkdir dir="${junit.out.dir.html}"/>
<junitreport todir="${junit.out.dir.html}">
<fileset dir="${output.dir}">
<include name="*.xml"/>
</fileset>
<report format="frames" todir="${junit.out.dir.html}" styledir="${style.dir}"/>
</junitreport>
</target>


<target name="displayTestResults" depends="junitreport">
<exec executable="C:\Program Files\Internet Explorer\iexplore.exe">
<arg value="${junit.out.dir.html}/index.html"/>
</exec>
</target>

<target name="displayResultsIfProblems">
<if>
<equals arg1="${mxunit.error}" arg2="true" />
<then>
<antcall target="displayTestResults" />
</then>
<elseif>
<equals arg1="${mxunit.fail}" arg2="true" />
<then>
<antcall target="displayTestResults" />
</then>
</elseif>
</if>

</target>


</project>



Once I had my build file running properly (I use EditPlus with Ant but this same setup will work fine with (CF)Eclipse) I worked on getting my project configured on Cruise Control.

First I went to the CruiseControl/projects directory and checked out my project to a new folder named after my project. Then I went to the CuriseControl/logs directory and created a folder that matches my project name. Then backup to the CruiseControl directory to edit the cruisecontrol.config to add my project.

Here is my basic config file for this project:


<cruisecontrol>
<project name="listbuilder_usaf" requireModification="false">
<listeners>
<currentbuildstatuslistener file="logs/${project.name}/status.txt"/>
</listeners>

<modificationset quietperiod="30">
<filesystem folder="projects/${project.name}" />
</modificationset>

<schedule interval="600">
<ant anthome="apache-ant-1.7.0" buildfile="projects/${project.name}/build.xml" target="testWithoutReport" />
</schedule>

<log>
<merge dir="projects/${project.name}/target/test-results"/>
</log>

<publishers>
<onfailure>
<email mailhost="smtp.gmail.com" mailport="465" username="USERNAME" password="PASSWORD" subjectprefix="BUILD FAILED: " buildresultsurl="http://MYSERVER:8080/dashboard/tab/build/detail/listbuilder_usaf" usessl="true" returnaddress="bill.rawlinson@gmail.com">
<always address="EMAILADDRESS1" />
<always address="EMAILADDRESS2" />
</email>
</onfailure>
</publishers>
</project>
</cruisecontrol>


Once you add a project you have to restart the CruiseControl service. However, once it is added you can edit it and the changes will be picked up on each subsequent build run.

To test my build I open the CruiseControl/logs/wrapper.log file in my text editor (EditPlus) and then load CruiseControls dashboard in my browser (http://localhost:8080/dashboard is the default address). Then I open my projects page and click on a small "refresh" looking arrow icon which forces a build. Watching the log file helps you figure out if anything is going wrong with the build process and how to fix it. Particularly useful if you have just done something wrong with the CruiseControl config.

I'm using CruiseControl version 2.7.3 and the SVN client that is built in is NOT compatiable with any other SVN clients at version 1.5 - thus I can't use any of the handy SVN options (such as detecting a change and instigating a build). Thus I am forced to just do a timed build every 10 minutes or so. It's not ideal but it is better than nothing. If anyone knows a solution to that problem please let me know! Whatever the solution is it has to work with Tortoise 1.5.0 and SvnAnt (or at least two equally compatible versions of those two clients.

It is important to note the two different methods I have for running all of my tests. The one I use in development launches IE with the results html files. If you run this same target in CruiseControl be prepared for grief and a neverending build cycle. IE Launches, somewhere in memory, but never actually draws, and thus you will have to kill the process. Thus, whatever you do, don't let CruiseControl start IE. Interestingly enough, however, if CruiseControl launches TortoiseSVN via the Ant build it works just fine. I'm not sure what the difference is between how the two apps launch.

Finally, also pay attention to my use of conditionals in my build file. I do this so that I can report a "FAILURE" of some sore if an error or fail is reported by MxUnit. Ant, otherwise, reports a nice clean "BUILD SUCCESSFUL" build which is really misleading. True, you do have the option of using the "haltonfailure" attribute but then you don't get the nice report document at the end. I'm still undecided on which option I prefer.

I hope, eventually, that mxunittask is updated to report the actual Unit test failures back to ANT on a "haltonfailure" - until then it seems fairly useless.

I know there i a lot to consume here and that I barely scratched the surface. However, if you have any questions just leave a comment and I'll try to help.

UPDATE
HAHA! Success. One of the older versions of SvnAnt came with a JavaSVN jar. Once I removed it all of my svn needs work. Tortoise, SvnAnt, CruiseControl... Boo YA!

Cruise Control, Ant, and Subversion

I run in a windows environment generally and thus I use TortoiseSVN as my local svn client. So, when I was setting up my CruiseControl installation for my current project I used Tortoise to initially check out the source code and then wanted to use the SvnAnt task to keep that version up to date before running any tests.

Nice dream.

It seems that one of the three clients involved in my efforts just didn't get along with the others. First there is the TortoiseSVN client, then SvnAnt, and finally whatever CruiseControl is using to check the repo. One of these three is causing me all sorts of grief. Basically, what happens is, if I update the repo with one client the other's wont work due to their being "too old of a version".

I'm using TortoiseSVN 1.5. I'm using SvnAnt ver 1.2 RC 1 (for SVN 1.5). My SVN server is 1.5.5 and I'm using the latest version of CruiseControl. Maybe I'm just missing something really simple but whatever is going on I'm kind of annoyed so I've taken SvnAnt out of the picture.

Basically, all of us on the team us Windows with TortoiseSvn so now my ant file has a target like so:


<target name="updateSource">
<exec executable="C:\Program Files\TortoiseSVN\bin\TortoiseProc.exe">
<arg value="/command:update" />
<arg value="/closeonend:3" />
</exec>
</target>


The /closeonend:3 argument forces the dialog to close so long as no errors, conflicts, or merges. This will be a little annoying considering, at the moment, the CruiseControl server is on my dev box so everyonce in a while the update dialog will flash by. However, this is a small price to pay to get everything working.

If anyone can recommend a good tandem of CruiseControl, TortoiseSVN, and SvnAnt I would really appreciate it!

UPDATE
TortoiseSVN 1.5.0 and SvnAnt 1.2 RC 1 work very well together. The problem lies in the CruiseControl svn client which is older. However, I can't use the SvnAnt 1.1 RC 2 task because it won't work with SVN Server 1.5.5.

Thus, at the moment, I have to pass on using the SVN config options for CruiseControl. Hopefully someone is working on a new plugin for CC and SVN!

BETTER UPDATE
HAHA! Success. One of the older versions of SvnAnt came with a JavaSVN jar. Once I removed it all of my svn needs work. Tortoise, SvnAnt, CruiseControl... Boo YA!

Tuesday, October 14, 2008

Now Featuring Syntax Highlighting

For a long time I have been posting really ugly code snippets on this blog but thankfully, as of today, that is no longer a problem. Thanks to this post about a syntaxhighlighter javascript and this other guys "coldfusion brush" for the same script my site now has nicely formatted and highlighted code.

The script is called SyntaxHighlighter and it works very well, even with blogger blogs like this one. I am now highlighting all of my CF, PHP, RUBY, Javascript, C#, SQL, and Java code exampmles. It may take a while for me to go back through my old posts and update the code but I'll get there eventually.

The first link in this post has nice instructions for setting up the script. The CF brush is a little hidden but he has a link right to the actual brushes compressed source as well.

Configure EditPlus with ANT

Ok, so you like to use EditPlus for your lightweight coding but you still want to do Test Driven Development (TDD) using ANT to fire off your tests. What do you do?

Well, fortunately, EditPlus supports "User Tools" (just like most programming editors) that will fire off an external tool. Ant is pretty easy to configure to use with Edit Plus; and thanks to the Edit Plus Wiki's entry on Nant I was able to setup Ant pretty quickly.

This tutorial assumes you can find the dialog for adding a new user tool and that you have already clicked the "Add Tool" button.


Menu Text: ANT
Command: {path to your ANT install/ant.bat}
Argument: $(CurSel) -find
Initial Directory: $(FileDir)
SELECT "Capture Output"
-----------
OUTPUT PATTERNS
UNCHECK "use default output pattern"
Regular Expression: ^[\t ]*(\[.+\] )?([^(]+)\(([0-9]+),([0-9]+)\)
File Name: Tagged Expression 2
Line: Tagged Expression 3
Column: Tagged Expression 4


Make sure you put the path to your ant.bat file. I use the ant that comes with eclipse so mine is at C:\dev\tools\eclipse\plugins\org.apache.ant_1.7.0.v200706080842\bin\ant.bat.

The argument $(CurSel) basically lets you pick a target name and then fire off the ant tool. You just highlight a string of text before executing the ANT user tool. The -find argument tells ANT to search backwards up the file structure to find a build file. The start directory argument of $(FileDir) tells Ant to start looking for a build file in the directory that you are currently editing. Thus, you want to make sure you build file is in the root of the project you're working on.

By having these two arguments setup as they are you get a pretty generic tool setup so that you don't have to edit any of the settings for each new project you work on. You just have to create a build file.

Custom Build Tasks?


If you have custom build task jar files the best way to get them to work with ANT - regardless of how you are starting ANT is to put them in your ${user.home}/.ant/lib directory. In windows your ${user.home} directory is c:\documents and settings\{username}. You may not be able to create a directory that starts with a . in windows explorer but you can from the command prompt. Just plop your custom jar files in that directory once it is all created and they will instantly be available in Ant if you use Eclipse, EditPlus, or any other editor (or just the command line).

CF Unit Testing - CFUnit, CFCUnit, and MxUnit Part 1

I love unit testing but, to be honest, I just haven't been able to come up with an approach I really like for continuous integration and unit testing of CF apps. This week I decided I was going to try out the three main frameworks I know of; CFUnit, CFCUnit, and MXUnit and see if I can't find one that I like using for comprehensive testing.

Our shop doesn't use CFEclipse much and, in general, we don't really like loading up Eclipse that much for developing CF apps; it seems like overkill. We do, however, use Ant so I referenced a bunch of CF-Eclipse type tutorials to get some basis for making all of this work.

The first one I tried out was CFUnit. This is the one I have used the most in the past and so it seemed like it would be my choice in the end. The first problem I ran into with CFUnit was the inability to run a test suite with the ANT task. This may be a feature I just don't see but nobody else seems to be documenting how to do it either.

My normal approach to setting up CFUnit is to create a test directory under the root of my project and then create a similar directory structure as my normal object model. For instance in my object directory I might have a bean, dao, gateway, and service subdirectory. Within each of these directories I will create a test CFC and a runner CFM for each test. Then I will create a simple CFM in that directory that does a cfinclude for each CFM runner; these comprehensive runners are my test suites. Then, in the parent object directory I will create another runner CFM that includes each test suite so that I have one full suite I can quickly access.

CFCUnit does support Test Suites. However, the initial install itself seems to have some problems. For instance when I follow the installation instructions that say to run the CFCUnit's own unit tests a bunch of them fail. The failing tests are primarily concerned with the CFUnit wrapper functionality but the fact that all of those tests failed caused me some real concern.

MxUnit doesn't support Test Suites either however it does support "directory" testing and seems like the best option of the three for what I want to do. Now, instead of having to create each of my test runner files I can just create an ANT task that is responsible for each directory and then another ant task that will run each of the directory level tests to serve as my overall test suite. It's not perfect but it seems like a good option.

While messing with all of these I tried using the BuildAgent in Eclipse to get the build to happen on each save. That kind of sucked. While I liked the constant testing it was just too much. For instance, I use ColdSpring with a pretty large app and each time my tests run they have to load the ColdSpring xml file to resolve dependency injection. Perhaps that isn't the greatest structure but I want my tests to also help confirm that my coldspring configuration is valid. Furthermore, I tend to change and save files pretty quickly. Each time I would save the ant task was still firing off from the prior save and I'd end up having to cancel it. For my workflow I prefer to make my batch of changes and then hit the "test" button (or key combo).

I'll be posting a followup with my final conclusions on how I approached continuous testing along with precise instructions on how I configured everything. After I do that I'll be looking at a collection of build tools such as CruiseControl for testing automatically whenever someone checks in a change.

Breaking up my Coldspring.xml file

About a month ago I upgraded my coldspring copy so that I could reference external xml files in the ColdSpring.xml file that I was using with my Model Glue application. My primary motivation was so that it would be easier to deploy the application and to separate the small bit of locale specific configuration data out so that end users could more easily configure the app.

I mucked around with it for a bit but wasn't entirely happy with the way I was including additional files:


<import resource="./config.xml"></import>


I really didn't like that ./ part. I also didn't put 1 and 1 together and realize I could have many different included files if I wanted. Well, today I had a bit of an epiphany. My app requires a CF Mapping of "LB2" and ColdSpring evaulates out the full path of the included xml file so now I can just do the following (plus I broke out major chunks of my configuration into separate files).

<import resource="/lb2/config/config.xml"></import>
<import resource="/lb2/config/pages.xml"></import>
<import resource="/lb2/config/menus.xml"></import>
<import resource="/lb2/config/gateways.xml"></import>
<import resource="/lb2/config/services.xml"></import>
<import resource="/lb2/config/dao.xml"></import>
<import resource="/lb2/config/aop.xml"></import>
...

Now it is much easier to find a bean definition and it will be much cleaner when adding additional definitions in the future.