Continuous Integration with Maven, Flex, Fliunt, and Hudson

Brian LeGros | December 17th, 2008 | programming  

Recently I was tasked with streamlining our build process at work so we could get a continuous integration (CI) server up and running. We use the common stack of technologies found in most Flex shops (basic SDK, some libraries, and Flex Builder) as well as Maven. I ran into some challenges getting our CI process to work as we wanted, so I figured I’d go through some of the gotchas I encountered.

On the build side of things, when I came on, Maven was already in place using flex-mojos. Now I’m a big fan of the simplicity that Ant brings to the mix, but the issue of dependency management being baked into Maven makes it extremely appealing; I do like Ivy as an alternative when using Ant, but I wasn’t going to re-write the company’s build process. So we had flex-mojos building our source and producing artifacts for deployment to our team’s Maven repository, but we needed to integrate our unit tests into our build. We were using dpuint and were excited to see that fluint had been released with Ant support. Currently flex-mojos doesn’t support Fluint, although my colleagues tell me they’re working on it, so I knew I was going to have to use Ant. To start I had to get flex-mojos building my test SWF so I could use the Fluint Ant task. The Ant support in Fluint requires that you produce a module SWF that will work with their test runner written in AIR. After an hour of messing with flex-mojos, I was unable to get the compile, or test-compile, goal to do what I wanted, so I decided to use the maven-ant-run plugin to compile our tests as well.

Below is the snippet I was able to get working to compile our tests and execute the Fluint test runner:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<properties>
   <flex.home>PATH_TO_FLEX_SDK_HOME</flex.home>
   <fluint.testrunner>PATH_TO_FLUINT_AIR_RUNNER_EXECUTABLE</fluint.testrunner>
</properties>
...
<build>
   <plugins>
      <plugin>
         <groupId>info.flex-mojos</groupId>
         <artifactId>flex-compiler-mojo</artifactId>
         <version>2.0M9</version>
         <extensions>true</extensions>
         <configuration>
            <skipTests>true</skipTests>
         </configuration>
      </plugin>
      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-antrun-plugin</artifactId>
         <executions>
            <execution>
               <id>fluint-test-compile</id>
               <phase>test-compile</phase>
               <configuration>
                  <tasks>
                     <!-- Pull in Flex Ant Tasks -->
                     <taskdef resource="flexTasks.tasks" />
 
                     <property name="FLEX_HOME" location="${flex.home}" />
 
                     <!-- Create test-classes directory -->
                     <mkdir dir="${project.build.testOutputDirectory}" />
 
                     <mxmlc file="${project.build.testSourceDirectory}/AirRunner.mxml" 
                        output="${project.build.testOutputDirectory}/AirRunner.swf" 
                        keep-generated-actionscript="false">
 
                        <load-config filename="${FLEX_HOME}/frameworks/flex-config.xml" />
                        <source-path path-element="${FLEX_HOME}/frameworks"  />
                        <compiler.library-path dir="${FLEX_HOME}/frameworks" append="true">
                           <include name="libs" />
                        </compiler.library-path>
                        <compiler.library-path dir="${project.build.directory}/.." append="true">
                           <include name="libs" />
                        </compiler.library-path>
                        <compiler.library-path dir="${project.build.directory}" append="true">
                           <include name="*.swc" />
                        </compiler.library-path>
                     </mxmlc>      
                  </tasks>
               </configuration>
               <goals>
                  <goal>run</goal>
               </goals>
            </execution>
            <execution>
               <id>fluint-test-run</id>
               <phase>test</phase>
               <configuration>
                  <tasks>
                     <!-- Pull in Fluint Ant Task -->
                     <taskdef name="fluint" classname="net.digitalprimates.ant.tasks.fluint.Fluint" />     
 
                     <property name="test.report.loc" location="${project.build.directory}/surefire-reports" />
 
                     <!-- Create reporting directory -->
                     <mkdir dir="${test.report.loc}" />
 
                     <fluint debug="true" 
                        headless="true"
                        testRunner="${fluint.testrunner}" 
                        outputDir="${test.report.loc}" 
                        workingDir="${project.build.testOutputDirectory}">
 
                        <fileset dir="${project.build.testOutputDirectory}">
                           <include name="**/AirRunner.swf"/>
                        </fileset>
                     </fluint>      
                  </tasks>
               </configuration>
               <goals>
                  <goal>run</goal>
               </goals>
            </execution>
         </executions>
         <dependencies>
            <dependency>
               <groupId>org.apache.ant</groupId>
               <artifactId>ant</artifactId>
               <version>1.7.0</version>
            </dependency>
            <dependency>
               <groupId>flex.ant</groupId>
               <artifactId>flexTasks</artifactId>
               <version>1.0.0</version>
            </dependency>
            <dependency>
               <groupId>net.digitalprimates</groupId>
               <artifactId>FluintAnt</artifactId>
               <version>1.0.1-SNAPSHOT</version>
            </dependency>
         </dependencies>
      </plugin>
   </plugins>
</build>

Couple of things to point out about the above snippet:

  • There are two dependencies on resources being available on the disk, the Flex SDK (flex.home property) and the Fluint Air Runner (fluint.testrunner property).
  • Using Ant I had to create the “test-classes” and “surefire-report” directory to stick with Maven conventions.
  • We adhered to the convention of naming our test runners for the Fluint Ant task “AirRunner.mxml” so we could use this snippet in a parent POM.
  • I had to change the dependency for the maven-ant-run plugin from Ant 1.6.5, which is the default, to Ant 1.7.0, which is required by the Fluint Ant task.

You may also notice that I’m using snapshot versions of the Fluint library and the Fluint Ant task. I ended up having to change the source of the Fluint library, Ant task, and AIR runner to get Fluint to work as I wanted it to with my build. Fluint is an awesome unit testing library, it just needed some tweaks. I made changes to fix the following:

  • The XML output from the Fluint AIR runner wasn’t compliant with what the Surefire Report plugin was expecting.
  • The name of output file from the Fluint AIR runner was in the convention “TEST-*.xml” which the SureFire reporting plugin expects.
  • Fluint had the notion of an error and failure being separate but it wasn’t implemented for the Flash or AIR test runners.
  • The Ant task didn’t allow the user to specify a working directory so that the AIR runner could be launched from the appropriate directory.

I later found out that AIR and relative paths don’t play nicely together (= at all, unless there is helper code), so we also had to re-factor our test suites to NOT rely on any assets unless they are embedded or referenced with absolute URIs. This made the change to the Fluint Ant task kinda worthless, but I kept it in anyway for when AIR works in the future. Additionally, it’s important to note, that the “headless” mode in the Fluint AIR runner is really just a minimized window that closes after the XML report is written; if you plan on running your CI build on an OS without a windowing solution, then FLuint will not work since AIR does not support running in a true headless mode. On a side note, my changes should address issues #5 and #22 on the Fluint Google Code site; issue #21 should be solved by the Ant dependency fix I spoke about above and issue #20 is just a matter of the fluintAnt15.jar being compiled with Java 1.6 instead of 1.5, I believe. I’ve submitted these fixes along with my code to the Fluint guys in an email, just haven’t heard anything back yet.

So at this point I had the build process working as I wanted such that I could run “mvn clean deploy site” and find a snapshot in our team’s maven repository and site documentation generated. On to CI. I have used CruiseControl many times in the past, but the idea of being entrenched in XML, especially with all the Maven and Ant fun, was discouraging so I decided to give Hudson a try this time around. Wow … Hudson is amazing improvement over CruiseControl. Completely UI driven, I have yet to find myself digging through XML and best of all. The post-build support feel a little lighter than CruiseControl’s, but I think that’s just because I haven’t come across an X10 plugin so we can get a stop light or glowing orb setup. Hudson provides trend reporting on builds and unit tests as well as embedded reporting for unit tests and xdoclet-like documentation; it also has tons of Groovy integration which I really like (not that we’re using it … yet). Initially I chose to go with the pure Maven build for our projects, but I then decided to switch back to the free-style build; I couldn’t get trend reporting for unit tests to work with the pure Maven build, so I think my conventions are off for the Fluint reporting. In the free-style build I set the “site” directory as the Javadoc location and the “surefire-report” directory as the test report directory. Even though there is more configuration in a free-style build, it was simpler in the short run to get what I wanted in Hudson running. If unit test trend reporting isn’t as important to your CI needs, then the pure maven build may be more along the lines of what you’d like to use so that you can get the additional build trigger “Build whenever a SNAPSHOT dependency is built”. On a side note, I’m working towards using the FlexCover support in flex-mojos to make our site reports complete, but haven’t had a chance to dive in yet.

I hope some of the hurdles I encountered can help if you’re trying to get your CI process working with Flex. The flex-mojos, Fluint, and Hudson guys have some good walkthroughs/tutorials to cover the details I left out. I’m always up for suggestions, so definitely feel free to rip into my solutions. :)



Tags: , , , , , , ,

Discussion

  1. Maxim Porges Says:

    Nice work, Brian… CI in Flex at last! Can’t wait for us to get this rolled out of our sandbox and on to the dev LAN.

    - max

  2. Brian LeGros Says:

    @Max – Thanks for the encouragement, I’m sure it running on Windows will be enough for it to eventually fail ;) That actually reminds me that I didn’t speak to the “headless” mode in the Fluint AIR runner which may be a roadblock for some. I’ll add it now.

  3. Ken Wong Says:

    This is great, I gone throught a similar excercise you did and ended up changing the fluint ant source code to make it ant 1.5 compatible, and I also ran into the same problem where the xml output from fluint not being sure-fire compatible. Is your changes available on http://code.google.com/p/fluint/
    or is the jar avaiable on some public maven repo?

    Another idea I’d suggest is changing the air test runner to save any paramers passed into it. Example being if your unit test uses BlazeDS to make remote call, there is no way to override the channel set unless the unit test can ask the application for its parameters (still can’t access environment variables from AIR).

  4. Brian LeGros Says:

    @Ken – Thanks for stopping by. My changes are available on the “changesToWorkWithCI” branch of the fluint SVN repository. Michael is in the process of reviewing them so we can get them merged into the trunk. You should contribute your fixes to make the Ant task run with older versions of Ant; from what I can tell, the dependency on Ant 1.7 is due to the DirectoryScanner class which is sounds like you found a way to refactor it out. I was surprised how easy it was to contribute, Michael is a real easy guy to work with. Hope my changes can help.

  5. Continuous Integration with Hudson « Mario Talavera Writes Says:

    [...] a great idea from my peers, I have proposed we employ Hudson as an integration server. We already have some [...]

  6. Dmitry Serebrennikov Says:

    For Hudson, there is vncserver plugin which takes care of the “headless” issue. It will launch an X server just for your build and tear it down after the build is done. The server it runs is vncserver (need to install). It will even take a screenshot in the end.

    This all works well, but we ran into trouble of flashplayer on Linux crashing on code that works fine on Macs and Windows. So that makes the headless support a bit useless. Is the Air runtime more reliable on Linux?

  7. Brian LeGros Says:

    @Dmitry – Thanks for stopping by. I’ve heard a lot of mixed results using the stand-alone Flash Player on Linux, but better results using Flash Player 10 rather than 9. For FlexUnit4′s Ant task we use the stand-alone FP, but with Fluint we used a custom AIR app. We didn’t hear of any major issues of Linux users running AIR unless it was related to the particulars of our runner impl, and then it was really from users on all platforms.

    In FlexUnit4 I’m going to be added support for xvfb-run and adl, so you’ll still build runners in MXML which use UI/CI/etc runners, but you will optionally be able to choose what environment you run your tests within. Obviously this means using mx:WindowedApplication rather than mx:Application if you want to use adl. Ideally you’ll be able to use xvfb-run to call adl or the stand-alone FP so your CI server agrees with Flex.

    I’ve heard a lot about the xvnc plugin for Hudson and recommended to users on the FU4 forums just for the cases where tests hang and you want to see why; vnc into display and check it out. Ideally I’d like to bake xvnc into FlexUnit4 Ant tasks, but I’m not that familiar with everything the Hudson plugin does, so xvfb-run it is for now.

  8. VELO Says:

    FWIW, Flexmojos 3.4 does support fluint

    And flexunit4 will be available very soon.

Add A Comment