Continuous Integration with Flex – Part 4

Ok, so far so good. We had covered a lot of ground up to this point, but we were only part of the way there… The next step is to get our test results understood by ANT. Ikezi created a natty python script to parse the output of our flash logfile using some cunning regular expressions. Basically, it looks for a multiple line match of __TRACERUNNEROUTPUTBEGINS__(somestuff in here)__TRACERUNNEROUTPUTENDS__, then parses the match and places the test result (true / false) into a test status text file, and the XML portion into another log file.

Create a new file called flashLogParser.py and paste the following into it:

## ------------------------------------------
#   flashLogParser.py
#
#   Ikezi Kamanu
#   Paul Barnes-Hogget
#
#   February 2nd 2006
#
#   Python Script to parse flash log for
#   unit test results.
## ------------------------------------------
import re, sys
class LogParser:
filepath =  None
file = None
testOutput = None
testStatus = None
testLogOutput = None
# Constructor: Receives file path, sets in self
# ------------------------------------------------
def __init__(self, pathToLog):
self.filepath = pathToLog
self.setTestOutput()
self.setTestLog()
self.setTestStatus()
# Use regular expression split and capture
# groups to parse the log file for relevant
# test result data
# ------------------------------------------------
def setTestOutput(self):
file = open(self.filepath)
regexString =  "-----------------TESTRUNNEROUTPUTBEGINS----------------"
regexString += "([ws!-~]+?)"
regexString += "-----------------TESTRUNNEROUTPUTENDS----------------"
myArr = re.split(regexString ,file.read(), re.M )
if ( len(myArr) > 2 ):
lastResultIndex = len(myArr)-2
self.testOutput = myArr[lastResultIndex]
else:
self.testOutput = "No test detail found"
# Use regular expression split and capture groups
# to parse test result data further, to determine
# specifically status --if test passed or failed.
# ------------------------------------------------
def setTestStatus(self):
regexString = "Test Suite success: ([w]+)"
myArr = re.split(regexString ,self.testOutput )
if len(myArr)==3:
self.testStatus = myArr[1]
print "STATUS "+myArr[1]
else:
self.testStatus = "null"
# Use regular expression split to parse test
# result data further, to grab formatted XML
# results to be parsed by Cruise Control.
# ------------------------------------------------
def setTestLog(self):
regexString = "<testsuites>[ws!-~]+?</testsuites>"
myResult = re.search(regexString, self.testOutput, re.MULTILINE)
self.testLogOutput = myResult.group()
# Print the test output
# ------------------------------------------------
def printTestOutput(self):
print self.testOutput
# Print the test status
# ------------------------------------------------
def printTestStatus(self):
print self.testStatus
# Write the test log output to the log file
# ------------------------------------------------
def writeTestLogOutput(self, filePath):
writeFile=open(filePath, 'w')
writeFile.write("<?xml version='1.0' encoding='UTF-8'?>n"+self.testLogOutput)
# Write the test status
# ------------------------------------------------
def writeTestStatus(self, filePath):
writeFile=open(filePath, 'w')
print("WRITING -- "+self.testStatus)
writeFile.write(self.testStatus)
# Entry point
try:
# Get the filename from the command line arguments
logPath = sys.argv[1]
testStatusPath = sys.argv[2]
testLogOutputPath = sys.argv[3]
classInstance = LogParser(logPath)
classInstance.writeTestStatus(testStatusPath)
classInstance.writeTestLogOutput(testLogOutputPath)
except EOFError:
pass

Save this file as flashLogParser.py and save it in a folder called python in your project. You’ll need python up and running on your machine. Instructions for getting it up and running are here

Amend Build script to build main file *if* tests pass

Now we have our result as a simple text file with just the word true or false in it, we can load this into ANT and use it to determine if our build has succeeded or not. The ANT target looks something like this:

<target name="parseFlashLog" depends="runTestHarness">
<py-run script="pythonflashLogParser.py" optimize="0">
<arg value="${flashlog.location}"/>
<arg value="${flashStatus.location}"/>
<arg value="${flashOutput.location}"/>
</py-run>
</target>
<target name="readTestStatus" depends="parseFlashLog">
<loadfile property="testStatus" srcFile="${flashStatus.location}"/>
</target>
<target name="determineTestHarnessSuccess" depends="readTestStatus">
<fail>
<condition>
<not>
<equals arg1="true" arg2="${testStatus}" />
</not>
</condition>
</fail>
</target>

Add the following properties to your build.properties file:

# ------------------------
# file names
# ------------------------
main.mxml.source=main
testHarness.name=testHarness
main.swf.name=savings
# ------------------------
# paths
# ------------------------
lib.dir=lib
outputdir=bin
mxmlc.dir = C:/Program Files/Macromedia/Flex/bin
flash.debugPlayer=${lib.dir}/SAFlashPlayer.exe
src.path = src
asunit.dir = #PATH_TO_ASUNIT
testBuildDir = testFiles
mainBuildDir = main


flashlog.location=C:/logs/flashlog.txt
flashStatus.location=${basedir}/log/testStatus.txt
flashOutput.location=${basedir}/log/test-results/TEST-testOutput.xml
# -----------------------------------------------------------------------------
# all jar needed
# -----------------------------------------------------------------------------
svnjavahl.jar=${lib.dir}/svnjavahl.jar
svnant.jar=${lib.dir}/svnant.jar
svnClientAdapter.jar=${lib.dir}/svnClientAdapter.jar
jakarta-regexp.jar=${lib.dir}/jakarta-regexp-1.3.jar
commons-lang.jar=${lib.dir}/commons-lang-2.0.jar
jython.jar=${lib.dir}/jython.jar
pyAntTasks.jar=${lib.dir}/pyAntTasks.jar

Add the following to the beginning of your build.xml file:

<path id="project.classpath">
<pathelement location="${svnjavahl.jar}" />
<pathelement location="${svnant.jar}" />
<pathelement location="${svnClientAdapter.jar}" />
<pathelement location="${jakarta-regexp.jar}" />
<pathelement location="${commons-lang.jar}" />
<pathelement location="${pyAntTasks.jar}" />
<pathelement location="${jython.jar}" />
</path>
<taskdef resource="svntask.properties" classpathref="project.classpath"/>
<taskdef resource="pyAntTasks.properties" classpathref="project.classpath"/>

Finally, change your ‘all’ target to look like this:

<target name="all" depends="determineTestHarnessSuccess,compileMain"/>

Now, when we run our ANT build, it will only succeed if our test suite succeeds – another key piece in the jigsaw puzzle. You will also notice that we are now adding several java files to our library. These can be found here, here, here, here, and here. download them and put them in your lib directory. You will notice we have included some svn jars as well. The need for these will become clear in a step or two…

Try it out…

Give this a shot by running the ‘all’ target of your ant build. What you should find is that in your log folder there is a file called testStatus.txt and in the log/test-results folder there is a file called TEST-testOutput.xml if you open up the status file, you will see either ‘true’ or ‘false’ depending on the results of the test suite, and the XML file will have more detailed information on the test as a whole…

The next installment will tell you how to get cruiseControl up and running so the entire team can track your builds…

This entry was posted in Continuous Integration, Flash & Actionscript. Bookmark the permalink. Comments are closed, but you can leave a trackback: Trackback URL.