Skip to content
This repository
Browse code

Can now run Taffy's test suite from Apache ANT - step 1 in continuous…

… integration!
  • Loading branch information...
commit 7cabf967e8fed165c2cdcff35d3eb390a7427f4d 1 parent e7a4b15
Adam Tuttle authored July 14, 2012
97  build.xml
... ...
@@ -0,0 +1,97 @@
  1
+<?xml version="1.0" encoding="ISO-8859-1"?>
  2
+<!--
  3
+
  4
+	This is an ANT script that runs the MXUnit Integration tests for Taffy
  5
+	Step 1 in continuous integration!
  6
+
  7
+	It assumes that you're running taffy from your web root. Never tried
  8
+	from an external path + mapping.
  9
+
  10
+-->
  11
+<project name="Taffy Integration Tests" basedir="." default="runtests">
  12
+
  13
+	<!-- define non-standard tasks -->
  14
+	<taskdef resource="org/eclipse/jgit/ant/ant-tasks.properties">
  15
+		<classpath>
  16
+			<pathelement location="lib/org.eclipse.jgit.ant-1.0.99.0.6-UNOFFICIAL-ROBERTO-RELEASE.jar"/>
  17
+			<pathelement location="lib/org.eclipse.jgit-2.0.0.201206130900-r.jar"/>
  18
+			<pathelement location="lib/jsch-0.1.48.jar"/>
  19
+		</classpath>
  20
+	</taskdef>
  21
+
  22
+	<target name="init">
  23
+
  24
+		<!-- ////////   DIRECTORY AND CFC PATH SETUP (used in all targets) -->
  25
+
  26
+		<!-- what's the directory name of your application? this value will be used throughout this build file;
  27
+		if you don't want that, just replace the references to ${application.name} with your desired values -->
  28
+		<property name="application.name" value="taffy" />
  29
+
  30
+		<!-- what's the name of the directory where your tests live? Note: this is just the name
  31
+		of the directory, not the full path-->
  32
+		<property name="test.dir.name" value="tests/tests" />
  33
+
  34
+		<!-- where do your tests live, relative to this build file? test.dir.location will be a
  35
+		full path to a directory -->
  36
+		<property name="test.dir.location" location="tests/tests" />
  37
+
  38
+		<!-- what is the cfc dot-notation path to that directory, as ColdFusion sees it? -->
  39
+		<property name="test.cfcpath" value="taffy.tests.tests" />
  40
+
  41
+
  42
+
  43
+		<!-- ////////   MXUNIT ANT TASK SETUP (used in runtests and junitreport targets) -->
  44
+
  45
+		<!-- what server and port should your tests run against? -->
  46
+		<property name="test.server" value="localhost" />
  47
+		<property name="test.serverport" value="80" />
  48
+		<!-- what "runner" URL should the tests hit. In this example, you'd be hitting
  49
+		http://localhost:80/DirectoryNameOfYourProject/test/HttpAntRunner.cfc
  50
+		Simply copy mxunit/samples/HttpAntRunner.cfc into your test directory! -->
  51
+		<property name="test.runner" value="/${application.name}/${test.dir.name}/HttpAntRunner.cfc" />
  52
+		<!-- this is where the xml and html will live for the report generator -->
  53
+		<property name="test.output" location="${test.dir.name}/output" />
  54
+		<property name="test.output.xml" location="${test.output}/xml" />
  55
+		<property name="test.junitoutput" location="${test.output}/html" />
  56
+
  57
+		<!-- ////////   JAR FILES WE NEED FOR EXTERNAL TASKS -->
  58
+		<!-- where does the mxunit ant jar file live? it's easiest to copy it out of the mxunit install and put it into your app
  59
+			 You can also put any other ant-relatd jars in this directory; for example, if you want to use svnant, you'll need to put those jars here
  60
+		-->
  61
+		<path id="project.classpath">
  62
+			<fileset dir="lib">
  63
+				<include name="**/*.jar" />
  64
+			</fileset>
  65
+		</path>
  66
+
  67
+		<!-- dump the properties -->
  68
+		<echoproperties prefix="test" />
  69
+	</target>
  70
+
  71
+	<target name="clean" depends="init">
  72
+		<mkdir dir="${test.output}" />
  73
+		<mkdir dir="${test.output.xml}" />
  74
+		<mkdir dir="${test.junitoutput}" />
  75
+	</target>
  76
+
  77
+	<target name="runtests" description="Make output directories and run the MXUnit task" depends="init,clean">
  78
+		<!-- <delete dir="${test.output}" failonerror="false" /> -->
  79
+		<taskdef name="mxunittask" classname="org.mxunit.ant.MXUnitAntTask" classpathref="project.classpath" />
  80
+		<mxunittask server="${test.server}" port="${test.serverport}" defaultrunner="${test.runner}" outputdir="${test.output.xml}" verbose="true">
  81
+			<directory path="${test.dir.location}" recurse="true" packageName="${test.cfcpath}" componentPath="${test.cfcpath}" />
  82
+		</mxunittask>
  83
+
  84
+		<!-- generate pretty reports -->
  85
+		<antcall target="junitreport" />
  86
+	</target>
  87
+
  88
+	<target name="junitreport" depends="init" description="Runs the report without running the tests">
  89
+		<junitreport todir="${test.junitoutput}">
  90
+			<fileset dir="${test.output.xml}">
  91
+				<include name="*.xml" />
  92
+			</fileset>
  93
+			<report format="frames" todir="${test.junitoutput}" />
  94
+		</junitreport>
  95
+	</target>
  96
+
  97
+</project>
3  lib/README.TXT
... ...
@@ -0,0 +1,3 @@
  1
+Files in the /lib folder exist solely for the purpose of running the tests.
  2
+You do not need to keep them for your installation. You can delete everything
  3
+except /core and /bonus, but I would recommend you keep /examples around too.
BIN  lib/ant-contrib-1.0b3.jar
Binary file not shown
BIN  lib/mxunit-ant.jar
Binary file not shown
207  tests/tests/DirectoryTestSuite.cfc
... ...
@@ -0,0 +1,207 @@
  1
+<cfcomponent hint="automatic test suite runner">
  2
+
  3
+	<cfset this.s_results = StructNew()>
  4
+	<cfset this.s_results.Errors = StructNew()>
  5
+	<cfset sep = createObject("java","java.lang.System").getProperty("file.separator")>
  6
+
  7
+	<cffunction name="run" access="public" hint="runs a directory of tests" returntype="any" output="false">
  8
+		<cfargument name="directory" required="true" hint="directory of tests to run">
  9
+		<cfargument name="componentPath" required="false" hint="the component path to put in front of all tests found (i.e. 'com.blah'). If no path is passed, we'll attempt to discover it ourselves" default="">
  10
+		<cfargument name="recurse" required="false" type="boolean" default="true" hint="whether to recurse down the directory tree">
  11
+		<cfargument name="excludes" required="false" default="" hint="list of Tests, in cfc notation, to exclude. uses ListContains so it's as greedy as possible. Currently does not support ant-style syntax or whole-directory filtering">
  12
+		<cfargument name="refreshCache" required="false" default="false" hint="flag to indicate whether or not to refresh the CF cache of CFCs. Maybe needed if any of the tests in the directory have not yet been compiled - to do: prove" />
  13
+
  14
+		<cfset var testResult = "">
  15
+		<cfset var files = "">
  16
+		<cfset var suite = createObject("component","mxunit.framework.TestSuite")>
  17
+		<cfset var i = 1>
  18
+
  19
+		<cfset arguments.directory = normalizeDirectory(arguments.directory)>
  20
+
  21
+		<cfif NOT len(arguments.componentPath)>
  22
+			<cfset arguments.componentPath = getComponentPath(arguments.directory, arguments.refreshCache)>
  23
+		</cfif>
  24
+
  25
+		<cfset files = getTests(arguments.directory,arguments.componentPath,arguments.recurse,trim(arguments.excludes)) />
  26
+
  27
+		<cfloop from="1" to="#ArrayLen(files)#" index="i">
  28
+			<cftry>
  29
+				<cfset suite.addAll(files[i])>
  30
+
  31
+				<cfcatch>
  32
+					<cfset this.s_results.Errors[files[i]] = cfcatch>
  33
+				</cfcatch>
  34
+			</cftry>
  35
+		</cfloop>
  36
+
  37
+		<cfset testResult = suite.run()>
  38
+
  39
+		<cfset testResult.mergeErrorsIntoTestResult(this.s_results.Errors)>
  40
+
  41
+		<cfreturn testResult>
  42
+	</cffunction>
  43
+
  44
+	<cffunction name="getTests" access="private" output="false">
  45
+		<cfargument name="directory" required="true" hint="directory of tests to run">
  46
+		<cfargument name="componentPath" required="true" hint="the component path to put in front of all tests found (i.e. 'com.blah')">
  47
+		<cfargument name="recurse" required="false" default="true" type="boolean" hint="whether to recurse down the directory tree">
  48
+		<cfargument name="excludes" required="false" default="" hint="ant-style syntax for excluding single files or directories">
  49
+		<cfset var q_tests = getDirectoryQuery(directory,recurse)>
  50
+		<cfset var a_tests = ArrayNew(1)>
  51
+		<cfset var testPath = "">
  52
+
  53
+		<cfloop query="q_tests">
  54
+			<cfset testPath = formatTestPath(arguments.directory, q_tests.directory & sep & q_tests.Name,arguments.componentPath)>
  55
+
  56
+			<cfif accept(testPath,arguments.excludes)>
  57
+				<cftry>
  58
+					<!---
  59
+					  Compile first. This ensures that any new tests NOT found in the server cache get added.
  60
+					  This may actually be quicker than searching the array and then conditionally compiling.
  61
+					  Appears not to have much overhead, but, we should monitor anyway for large test runs.
  62
+					 --->
  63
+					<cfset createObject("component","#testPath#") />
  64
+				<cfcatch type="any"><!--- no worries ---></cfcatch>
  65
+			</cftry>
  66
+				<cfset ArrayAppend(a_tests,testPath)>
  67
+			</cfif>
  68
+		</cfloop>
  69
+		<cfreturn a_tests>
  70
+	</cffunction>
  71
+
  72
+	<cffunction name="formatTestPath" access="private" output="false">
  73
+		<cfargument name="directory" required="true" hint="directory of tests to run">
  74
+		<cfargument name="fullFilePath">
  75
+		<cfargument name="componentPath" required="true" hint="the component path to put in front of all tests found (i.e. 'com.blah')">
  76
+
  77
+		<cfset var formatted = replaceNoCase(fullFilePath,directory,"")>
  78
+		<cfset formatted =  reverse(  replace(reverse(formatted),"cfc.","")    )>
  79
+		<cfset formatted = componentPath & "." & formatted>
  80
+		<cfset formatted = reReplace(formatted, "(\\|/|\.){1,}" ,".","all")>
  81
+		<cfreturn formatted>
  82
+	</cffunction>
  83
+
  84
+	<cffunction name="getDirectoryQuery" access="private" returntype="query" output="false">
  85
+		<cfargument name="directory" required="true" hint="directory of tests to run">
  86
+		<cfargument name="recurse" type="boolean" required="false" default="true" hint="whether to recurse down the directory tree">
  87
+		<cfset var files = "">
  88
+		<cfset var runnerUtils = "">
  89
+
  90
+		<cfif not DirectoryExists(arguments.directory)>
  91
+			<cfthrow message="Directory #directory# does not exist">
  92
+		</cfif>
  93
+
  94
+		<cfdirectory action="list" directory="#arguments.directory#" name="files" recurse="#arguments.recurse#" filter="*.cfc">
  95
+
  96
+		<cfreturn files>
  97
+	</cffunction>
  98
+
  99
+	<cffunction name="getCatastrophicErrors" output="false">
  100
+		<cfreturn this.s_results.Errors>
  101
+	</cffunction>
  102
+
  103
+	<cffunction name="accept" hint="rudimentary initial implementation." access="private" output="false">
  104
+		<cfargument name="test" required="true">
  105
+		<cfargument name="excludes" required="true">
  106
+
  107
+		<cfset var testName = ListLast(arguments.test,".")>
  108
+		<cfset var thisExclude = "">
  109
+
  110
+		<cfif NOT reFindNoCase("^test",testName) AND NOT reFindNoCase("test$",testName)>
  111
+			<cfreturn false>
  112
+		</cfif>
  113
+		<cfif not len(excludes)>
  114
+			<cfreturn true>
  115
+		</cfif>
  116
+
  117
+		<cfset arguments.test = ListChangeDelims(arguments.test,"/",".")>
  118
+		<cfloop list="#excludes#" index="thisExclude" delimiters=",">
  119
+			<cfset thisExclude = replace(thisExclude,"\","/")>
  120
+			<cfif reFindNoCase("\b#thisExclude#\b",arguments.test)>
  121
+				<cfreturn false>
  122
+			</cfif>
  123
+		</cfloop>
  124
+		<cfreturn true>
  125
+	</cffunction>
  126
+
  127
+	<cffunction name="normalizeDirectory" hint="makes sure every directory has ending slashes" access="private" output="false">
  128
+		<cfargument name="Directory" type="string" required="true"/>
  129
+		<cfset var dir = createObject("java","java.io.File")>
  130
+
  131
+		<cfif not DirectoryExists(arguments.directory)>
  132
+			<cfset arguments.directory = expandPath(arguments.directory) />
  133
+			<cfif not DirectoryExists(arguments.directory)>
  134
+				<cfthrow message="Directory #directory# does not exist">
  135
+			</cfif>
  136
+		</cfif>
  137
+
  138
+		<cfset dir.init(arguments.directory)>
  139
+		<cfset arguments.directory = dir.getCanonicalPath()>
  140
+		<cfset arguments.directory = replaceList(arguments.directory,"/,\","#sep#,#sep#")>
  141
+		<cfset arguments.directory = arguments.directory & sep>
  142
+		<cfreturn arguments.directory>
  143
+	</cffunction>
  144
+
  145
+	<cffunction name="getComponentPath" access="remote" returntype="string" hint="Given a directory path, returns the corresponding CFC package according to CFMX" output="false">
  146
+		<cfargument name="path" type="string" required="true" />
  147
+		<cfargument name="refreshCache" type="boolean" required="false" default="false" />
  148
+		<cfscript>
  149
+		var explorer = createObject("component", "CFIDE.componentutils.cfcexplorer");
  150
+		var target = explorer.normalizePath(arguments.path);
  151
+		var cfcs = explorer.getcfcs(refreshCache); //true == refresh cache
  152
+		var cfc = "";
  153
+		var i = 1 ;
  154
+		var package = "";
  155
+		var cfcpath = "";
  156
+
  157
+		for(i = 1; i lte arraylen(cfcs); i = i+1){
  158
+			cfc = cfcs[i];
  159
+			//Assumes that CF always stores path info with fwd slash
  160
+			//and strip last element to get path.
  161
+			cfcpath = ListDeleteAt(cfc.path, listlen(cfc.path,"/"), "/");
  162
+
  163
+			//Array of structs. Doesn't seem possible to do binary search
  164
+			if(cfcpath eq target){
  165
+
  166
+				package = cfc.package;
  167
+				break;
  168
+			}
  169
+		}
  170
+
  171
+		return package;
  172
+		</cfscript>
  173
+	</cffunction>
  174
+
  175
+
  176
+	<cffunction name="getComponentPathx" access="remote" returntype="string" hint="Given a directory path, returns the corresponding CFC package" output="false">
  177
+		<cfargument name="path" type="string" required="true" />
  178
+		<cfargument name="refreshCache" type="boolean" required="false" default="false" />
  179
+
  180
+			<cfset var files = '' />
  181
+			<cfset var package = '' />
  182
+
  183
+			<cfdirectory action="list" type="file" name="files" directory="#arguments.path#" filter="*.cfc" />
  184
+
  185
+			<cfif files.recordCount>
  186
+				<cfset package = replace(normalizePath(files.directory), '/', '.', 'all') />
  187
+			</cfif>
  188
+
  189
+			<cfreturn package />
  190
+		</cffunction>
  191
+
  192
+	<cffunction name="normalizePath" access="public" returntype="string" output="false">
  193
+		<cfargument name="path" type="string" required="true" />
  194
+
  195
+		<cfif not len(arguments.path)>
  196
+			<cfreturn arguments.path />
  197
+		</cfif>
  198
+
  199
+		<cfset arguments.path = replace(arguments.path, '\', '/', 'all') />
  200
+
  201
+		<cfif right(arguments.path, 1) eq '/'>
  202
+			<cfset arguments.path = left(arguments.path, len(arguments.path) - 1) />
  203
+		</cfif>
  204
+
  205
+		<cfreturn arguments.path />
  206
+	</cffunction>
  207
+</cfcomponent>
92  tests/tests/HttpAntRunner.cfc
... ...
@@ -0,0 +1,92 @@
  1
+<cfcomponent output="true">
  2
+<cfsetting enablecfoutputonly="true" showdebugoutput="false">
  3
+
  4
+  <cffunction name="run" access="remote" output="true" returntype="void" hint="Generates and prints HTML, JUnit style XML, or XML data based on a directory of tests.">
  5
+    <cfargument name="type" type="string" hint="Specifies the type to run: TestCase(testcase) or Directory Path (dir)" required="true" />
  6
+    <cfargument name="value" type="string" hint="The value for the type: com.foo.MyTestCase or C:/my/tests/" required="true" />
  7
+    <cfargument name="packagename" type="string" hint="The package name for JUnitReportTask" required="false" default="mxunit.testresults" />
  8
+    <cfargument name="outputformat" type="string" hint="Valid Values: HTML,XML, or JUNITXML" required="false" default="junitxml" />
  9
+    <cfargument name="recurse" required="false" default="true" hint="whether to recurse down the directory tree">
  10
+	<cfargument name="excludes" required="false" default="" hint="List of files to exclude if type is DIR">
  11
+    <cfargument name="componentPath" required="false" hint="performance improver: pass the cfc-notation. See DirectoryTestSuite for details." default="">
  12
+
  13
+    <cfset var suite = createObject("component","mxunit.framework.TestSuite").TestSuite()/>
  14
+    <cfset var results = createObject("component","mxunit.framework.TestResult").TestResult()/>
  15
+    <cfscript>
  16
+      if(arguments.type is "testcase"){
  17
+       suite = createObject("component","mxunit.framework.TestSuite").TestSuite();
  18
+       suite.addAll(arguments.value);
  19
+       results = suite.run();
  20
+       }
  21
+      if(arguments.type is "dir"){
  22
+        //To Do: add args for recursion, includes, and excludes
  23
+        if(not isBoolean(arguments.recurse)){
  24
+        	arguments.recurse = false;
  25
+        }
  26
+
  27
+        results = createObject("component","DirectoryTestSuite").run(directory=arguments.value, componentPath=arguments.componentPath, recurse=arguments.recurse, excludes=arguments.excludes);
  28
+      }
  29
+      //package name for JUnit reports
  30
+      results.setPackage(arguments.packagename);
  31
+    </cfscript>
  32
+    <!--- Read by Ant client and used to print summary to stdout --->
  33
+    <cfcookie name="mxunit_summary" value="#results.testRuns#,#results.testErrors#,#results.testFailures#,#results.totalExecutionTime#" />
  34
+    <!--- write the cookie first --->
  35
+    <cfset printResults(arguments.outputformat,results) />
  36
+    <cfreturn />
  37
+ </cffunction>
  38
+
  39
+  <cffunction name="printResults" access="private">
  40
+    <cfargument name="type">
  41
+    <cfargument name="results">
  42
+    <cfoutput>
  43
+	    <cfswitch expression="#type#">
  44
+	      <cfcase value="html">
  45
+	        #trim(arguments.results.getHTMLResults())#
  46
+	      </cfcase>
  47
+	      <cfcase value="xml">
  48
+	        #trim(arguments.results.getXMLResults())#
  49
+	      </cfcase>
  50
+	      <cfcase value="junitxml">
  51
+	        #trim(arguments.results.getJUnitXMLResults())#
  52
+	      </cfcase>
  53
+	      <cfdefaultcase>#trim(arguments.results.getJUnitXMLResults())#</cfdefaultcase>
  54
+	    </cfswitch>
  55
+    </cfoutput>
  56
+  </cffunction>
  57
+
  58
+    <cffunction name="runFast" access="remote" output="true" returntype="void" hint="Generates and prints HTML, JUnit style XML, or XML data based on a directory of tests.">
  59
+    <cfargument name="type" type="string" hint="Specifies the type to run: TestCase(testcase) or Directory Path (dir)" required="true" />
  60
+    <cfargument name="value" type="string" hint="The value for the type: com.foo.MyTestCase or C:/my/tests/" required="true" />
  61
+    <cfargument name="packagename" type="string" hint="The package name for JUnitReportTask" required="false" default="mxunit.testresults" />
  62
+    <cfargument name="outputformat" type="string" hint="Valid Values: HTML,XML, or JUNITXML" required="false" default="junitxml" />
  63
+    <cfargument name="recurse" required="false" default="true" hint="whether to recurse down the directory tree">
  64
+	  <cfargument name="excludes" required="false" default="" hint="List of files to exclude if type is DIR">
  65
+    <cfargument name="componentPath" required="false" hint="performance improver: pass the cfc-notation. See DirectoryTestSuite for details." default="">
  66
+
  67
+    <cfset var suite = createObject("component","mxunit.framework.TestSuite").TestSuite()/>
  68
+    <cfset var results = createObject("component","mxunit.framework.TestResult").TestResult()/>
  69
+    <cfscript>
  70
+      if(arguments.type is "testcase"){
  71
+       suite = createObject("component","mxunit.framework.TestSuite").TestSuite();
  72
+       suite.addAll(arguments.value);
  73
+       results = suite.run();
  74
+       }
  75
+      if(arguments.type is "dir"){
  76
+        //To Do: add args for recursion, includes, and excludes
  77
+        if(not isBoolean(arguments.recurse)){
  78
+        	arguments.recurse = false;
  79
+        }
  80
+
  81
+        results = createObject("component","DirectoryTestSuite").run(directory=arguments.value, componentPath=arguments.componentPath, recurse=arguments.recurse, excludes=arguments.excludes);
  82
+      }
  83
+      //package name for JUnit reports
  84
+      results.setPackage(arguments.packagename);
  85
+    </cfscript>
  86
+    <!--- Read by Ant client and used to print summary to stdout --->
  87
+    <cfcookie name="mxunit_summary" value="#results.testRuns#,#results.testErrors#,#results.testFailures#,#results.totalExecutionTime#" />
  88
+    <!--- write the cookie first
  89
+    <cfset printResults(arguments.outputformat,results) /> --->
  90
+    <cfreturn />
  91
+ </cffunction>
  92
+</cfcomponent>

0 notes on commit 7cabf96

Please sign in to comment.
Something went wrong with that request. Please try again.