All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scalatest.tools.ScalaTestAntTask.scala Maven / Gradle / Ivy

There is a newer version: 2.0.M6-SNAP27
Show newest version
package org.scalatest.tools

import scala.collection.mutable.ListBuffer
import org.apache.tools.ant.BuildException
import org.apache.tools.ant.Task
import org.apache.tools.ant.types.Path
import org.apache.tools.ant.AntClassLoader
import org.apache.tools.ant.taskdefs.Java

/**
 * 

* An ant task to run ScalaTest. Instructions on how to specify various * options are below. See the scaladocs for the Runner class for a description * of what each of the options does. *

* *

* To use the ScalaTest ant task, you must first define it in your ant file using taskdef. * Here's an example: *

* *
 *  <path id="scalatest.classpath">
 *    <pathelement location="${lib}/scalatest.jar"/>
 *    <pathelement location="${lib}/scala-library.jar"/>
 *  </path>
 *
 *  <target name="main" depends="dist">
 *    <taskdef name="scalatest" classname="org.scalatest.tools.ScalaTestAntTask">
 *      <classpath refid="scalatest.classpath"/>
 *    </taskdef>
 *
 *    <scalatest ...
 *  </target>
 * 
* *

* Once defined, you use the task by specifying information in a scalatest element: *

* *
 *   <scalatest ...>
 *     ...
 *   </scalatest>
 * 
* *

* You can place key value pairs into the configMap using nested <config> elements, * like this: *

* *
 *   <scalatest>
 *     <config name="dbname" value="testdb"/>
 *     <config name="server" value="192.168.1.188"/>
 * 
* *

* You can specify a runpath using either a runpath attribute and/or nested * <runpath> elements, using standard ant path notation: *

* *
 *   <scalatest runpath="serviceuitest-1.1beta4.jar:myjini">
 * 
* * or * *
 *   <scalatest>
 *     <runpath>
 *       <pathelement location="serviceuitest-1.1beta4.jar"/>
 *       <pathelement location="myjini"/>
 *     </runpath>
 * 
* *

* To add a URL to your runpath, use a <runpathurl> element * (since ant paths don't support URLs): *

* *
 *   <scalatest>
 *     <runpathurl url="http://foo.com/bar.jar"/>
 * 
* *

* You can specify reporters using nested <reporter> elements, where the type * attribute must be one of the following: *

* *
    *
  • graphic
  • *
  • file
  • *
  • junitxml
  • *
  • stdout
  • *
  • stderr
  • *
  • reporterclass
  • *
* *

* Each may include a config attribute to specify the reporter configuration. * Types file, junitxml and reporterclass require additional attributes * filename, directory, and classname, respectively: *

* *
 *   <scalatest>
 *     <reporter type="stdout"        config="FAB"/>
 *     <reporter type="file"          filename="test.out"/>
 *     <reporter type="junitxml"      directory="target"/>
 *     <reporter type="reporterclass" classname="my.ReporterClass"/>
 * 
* *

* Specify tags to include and/or exclude using <tagsToInclude> and * <tagsToExclude> elements, like this: *

* *
 *   <scalatest>
 *     <tagsToInclude>
 *         CheckinTests
 *         FunctionalTests
 *     </tagsToInclude>
 *
 *     <tagsToExclude>
 *         SlowTests
 *         NetworkTests
 *     </tagsToExclude>
 * 
* *

* To specify suites to run, use either a suite attribute or nested * <suite> elements: *

* *
 *   <scalatest suite="com.artima.serviceuitest.ServiceUITestkit">
 * 
* *

* or *

* *
 *   <scalatest>
 *     <suite classname="com.artima.serviceuitest.ServiceUITestkit"/>
 * 
* *

* To specify suites using members-only or wildcard package names, use * either the membersonly or wildcard attributes, or nested * <membersonly> or <wildcard> elements: *

* *
 *   <scalatest membersonly="com.artima.serviceuitest">
 * 
* *

* or *

* *
 *   <scalatest wildcard="com.artima.joker">
 * 
* *

* or *

* *
 *   <scalatest>
 *     <membersonly package="com.artima.serviceuitest"/>
 *     <wildcard package="com.artima.joker"/>
 * 
* *

* Use attribute suffixes="[pipe-delimited list of suffixes]" * to specify that only classes whose names end in one of the specified suffixes * should be included in discovery searches for Suites to test. This can * be used to improve discovery time or to limit the scope of a test. E.g.: *

* *
 *   <scalatest suffixes="Spec|Suite">
 * 
* *

* Use attribute parallel="true" to specify parallel execution of suites. * (If the parallel attribute is left out or set to false, suites will be executed sequentially by one thread.) * When parallel is true, you can include an optional numthreads attribute to specify the number * of threads to be created in thread pool (e.g., numthreads="10"). *

* *

* Use attribute haltonfailure="true" to cause ant to fail the * build if there's a test failure. *

* *

* Use attribute fork="true" to cause ant to run the tests in * a separate process. *

* *

* When fork is true, attribute maxmemory may be used to specify * the maximum memory size that will be passed to the forked jvm.  For example, the following setting * will cause "-Xmx1280M" to be passed to the java command used to * run the tests. *

* *
 *   <scalatest maxmemory="1280M">
 * 
* *

* When fork is true, nested <jvmarg> elements may be used * to pass additional arguments to the forked jvm. * For example, if you are running into 'PermGen space' memory errors, * you could add the following jvmarg to bump up the JVM's MaxPermSize value: *

* *
 *   <jvmarg value="-XX:MaxPermSize=128m"/>
 * 
* * @author George Berger */ class ScalaTestAntTask extends Task { private var includes: String = null private var excludes: String = null private var maxMemory: String = null private var suffixes: String = null private var parallel = false private var haltonfailure = false private var fork = false private var spanScaleFactor = 1.0 private var numthreads = 0 private val runpath = new ListBuffer[String] private val jvmArgs = new ListBuffer[String] private val suites = new ListBuffer[String] private val membersonlys = new ListBuffer[String] private val wildcards = new ListBuffer[String] private val testNGSuites = new ListBuffer[String] private val chosenStyles = new ListBuffer[String] private val reporters = new ListBuffer[ReporterElement] private val properties = new ListBuffer[NameValuePair] /** * Executes the task. */ override def execute { val args = new ListBuffer[String] addSuiteArgs(args) addReporterArgs(args) addPropertyArgs(args) addIncludesArgs(args) addExcludesArgs(args) addRunpathArgs(args) addTestNGSuiteArgs(args) addParallelArg(args) addSuffixesArg(args) addChosenStyles(args) addSpanScaleFactorArg(args) val argsArray = args.toArray val success = if (fork) javaTaskRunner(args.toList) else Runner.run(argsArray) if (!success && haltonfailure) throw new BuildException("ScalaTest run failed.") } private def javaTaskRunner(args: List[String]): Boolean = { val java = new Java java.bindToOwner(this) java.init() java.setFork(true) java.setClassname("org.scalatest.tools.Runner") val classLoader = getClass.getClassLoader.asInstanceOf[AntClassLoader] java.setClasspath(new Path(getProject, classLoader.getClasspath)) if (maxMemory != null) java.createJvmarg.setValue("-Xmx" + maxMemory) for (jvmArg <- jvmArgs) java.createJvmarg.setValue(jvmArg) for (arg <- args) java.createArg.setValue(arg) val result = java.executeJava return (result == 0) } // // Adds '-R runpath' arg pair to args list if a runpath // element or attribute was specified for task. // private def addRunpathArgs(args: ListBuffer[String]) { if (runpath.size > 0) { args += "-R" args += getSpacedOutPathStr(runpath.toList) } } private def addTestNGSuiteArgs(args: ListBuffer[String]) { if (testNGSuites.size > 0) { args += "-b" args += getSpacedOutPathStr(testNGSuites.toList) } } private def addChosenStyles(args: ListBuffer[String]) { chosenStyles.foreach { style => args += "-y" args += style } } // // Adds '-P' arg to args list if 'parallel' attribute was // specified true for task. // private def addParallelArg(args: ListBuffer[String]) { if (parallel) { args += "-P" + (if (numthreads > 0) ("" + numthreads) else "") } } // // Add -F arg to args list if spanScaleFactor attribute was // specified for task // private def addSpanScaleFactorArg(args: ListBuffer[String]) { args += "-F" args += spanScaleFactor.toString } // // Adds '-q' arg to args list if 'suffixes' attribute was // specified for task. // private def addSuffixesArg(args: ListBuffer[String]) { if (suffixes != null) { args += "-q" args += suffixes } } // // Adds '-n includes-list' arg pair to args list if a // element was supplied for task. // private def addIncludesArgs(args: ListBuffer[String]) { if (includes != null) { args += "-n" args += singleSpace(includes) } } // // Adds '-l excludes-list' arg pair to args list if an // element was supplied for task. // private def addExcludesArgs(args: ListBuffer[String]) { if (excludes != null) { args += "-l" args += singleSpace(excludes) } } // // Adds '-Dname=value' argument to args list for each nested // element supplied for task. // private def addPropertyArgs(args: ListBuffer[String]) { for (pair <- properties) args += "-D" + pair.getName + "=" + pair.getValue } // // Adds '-s classname' argument to args list for each suite // specified for task. Adds '-m packagename' for each // membersonly element specified, and '-w packagename' for // each wildcard element specified. // private def addSuiteArgs(args: ListBuffer[String]) { for (suite <- suites) { if (suite == null) throw new BuildException( "missing classname attribute for element") args += "-s" args += suite } for (packageName <- membersonlys) { if (packageName == null) throw new BuildException( "missing package attribute for element") args += "-m" args += packageName } for (packageName <- wildcards) { if (packageName == null) throw new BuildException( "missing package attribute for element") args += "-w" args += packageName } } // // Adds appropriate reporter options to args list for each // nested reporter element specified for task. Defaults to // stdout if no reporter specified. // private def addReporterArgs(args: ListBuffer[String]) { if (reporters.size == 0) args += "-o" for (reporter <- reporters) { reporter.getType match { case "stdout" => addReporterOption(args, reporter, "-o") case "stderr" => addReporterOption(args, reporter, "-e") case "graphic" => addReporterOption(args, reporter, "-g") case "file" => addFileReporter(args, reporter) case "xml" => addXmlReporter(args, reporter) case "junitxml" => addXmlReporter(args, reporter) case "html" => addHtmlReporter(args, reporter) case "reporterclass" => addReporterClass(args, reporter) case t => throw new BuildException("unexpected reporter type [" + t + "]") } } } // // Adds specified option to args for reporter. Appends reporter // config string to option if specified, e.g. "-eFAB". // private def addReporterOption(args: ListBuffer[String], reporter: ReporterElement, option: String) { val config = reporter.getConfig if (config == null) args += option else args += option + config } // // Adds '-f' file reporter option to args. Appends reporter // config string to option if specified. Adds reporter's // filename as additional argument, e.g. "-fFAB", "filename". // private def addFileReporter(args: ListBuffer[String], reporter: ReporterElement) { addReporterOption(args, reporter, "-f") if (reporter.getFilename == null) throw new BuildException( "reporter type 'file' requires 'filename' attribute") args += reporter.getFilename } // // Adds '-u' xml reporter option to args. Adds reporter's // directory as additional argument, e.g. "-u", "directory". // private def addXmlReporter(args: ListBuffer[String], reporter: ReporterElement) { addReporterOption(args, reporter, "-u") if (reporter.getDirectory == null) throw new BuildException( "reporter type 'xml' requires 'directory' attribute") args += reporter.getDirectory if (reporter.getType == "xml") Console.err.println("WARNING: reporter type 'xml' is deprecated " + "- please use 'junitxml' instead") } // // Adds '-h' html reporter option to args. Appends reporter // config string to option if specified. Adds reporter's // filename as additional argument, e.g. "-hFAB", "filename". // private def addHtmlReporter(args: ListBuffer[String], reporter: ReporterElement) { addReporterOption(args, reporter, "-h") if (reporter.getFilename == null) throw new BuildException( "reporter type 'html' requires 'filename' attribute") args += reporter.getFilename } // // Adds '-C' reporter class option to args. Appends // reporter config string to option if specified. Adds // reporter's classname as additional argument, e.g. "-RFAB", // "my.ReporterClass". // private def addReporterClass(args: ListBuffer[String], reporter: ReporterElement) { addReporterOption(args, reporter, "-C") if (reporter.getClassName == null) throw new BuildException( "reporter type 'reporterclass' requires 'classname' attribute") args += reporter.getClassName } /** * Sets value of the runpath attribute. */ def setRunpath(runpath: Path) { for (element <- runpath.list) { this.runpath += element } } /** * Sets value of the haltonfailure attribute. */ def setHaltonfailure(haltonfailure: Boolean) { this.haltonfailure = haltonfailure } /** * Sets value of the fork attribute. */ def setFork(fork: Boolean) { this.fork = fork } /** * Sets value of the suffixes attribute. */ def setSuffixes(suffixes: String) { this.suffixes = suffixes } /** * Sets value of the maxmemory attribute. */ def setMaxmemory(max: String) { this.maxMemory = max } /** * Sets value of the testngsuites attribute. */ def setTestNGSuites(testNGSuitePath: Path) { for (element <- testNGSuitePath.list) this.testNGSuites += element } /** * Sets value of the concurrent attribute. * Note: The concurrent attribute has been deprecated and will be removed in a future version of ScalaTest. * Please use the parallel attribute instead. */ @deprecated("Please use parallel instead") def setConcurrent(concurrent: Boolean) { Console.err.println("WARNING: 'concurrent' attribute is deprecated " + "- please use 'parallel' instead") this.parallel = concurrent } /** * Sets value of the numthreads attribute. */ def setNumthreads(numthreads: Int) { this.numthreads = numthreads } /** * Sets value of the parallel attribute. */ def setParallel(parallel: Boolean) { this.parallel = parallel } /** * Sets value of the spanScaleFactor attribute. */ def setSpanScaleFactor(spanScaleFactor: Double) { this.spanScaleFactor = spanScaleFactor } /** * Sets value from nested element runpath. */ def addConfiguredRunpath(runpath: Path) { for (element <- runpath.list) this.runpath += element } /** * Sets value from nested element testngsuites. */ def addConfiguredTestNGSuites(testNGSuitePath: Path) { for (element <- testNGSuitePath.list) this.testNGSuites += element } /** * Sets value from nested element runpathurl. */ def addConfiguredRunpathUrl(runpathurl: RunpathUrl) { runpath += runpathurl.getUrl } /** * Sets value from nested element jvmarg. */ def addConfiguredJvmArg(arg: JvmArg) { jvmArgs += arg.getValue } /** * Sets values from nested element property. * The property attribute has been deprecated and will be removed in a future version of ScalaTest. * Please use the config attribute instead. */ @deprecated("Please use config instead") def addConfiguredProperty(property: NameValuePair) { Console.err.println("WARNING: is deprecated - " + "please use instead [name: " + property.getName + "]") properties += property } /** * Sets values from nested element config. */ def addConfiguredConfig(config: NameValuePair) { properties += config } /** * Sets value of suite attribute. */ def setSuite(suite: String) { suites += suite } /** * Sets value of membersonly attribute. */ def setMembersonly(packageName: String) { membersonlys += packageName } /** * Sets value of wildcard attribute. */ def setWildcard(packageName: String) { wildcards += packageName } /** * Sets value of style attribute. */ def setStyle(style: String) { chosenStyles += style } /** * Sets value from nested element suite. */ def addConfiguredSuite(suite: SuiteElement) { suites += suite.getClassName } /** * Sets value from nested element membersonly. */ def addConfiguredMembersOnly(membersonly: PackageElement) { membersonlys += membersonly.getPackage } /** * Sets value from nested element wildcard. */ def addConfiguredWildcard(wildcard: PackageElement) { wildcards += wildcard.getPackage } /** * Sets value from nested element reporter. */ def addConfiguredReporter(reporter: ReporterElement) { reporters += reporter } /** * Sets value from nested element tagsToInclude. */ def addConfiguredTagsToInclude(tagsToInclude: TextElement) { this.includes = tagsToInclude.getText } def addConfiguredStyle(style: StyleElement) { this.chosenStyles += style.getName } /** * Sets value from nested element includes. * The includes attribute has been deprecated and will be removed in a future version of ScalaTest. * Please use the tagsToInclude attribute instead. */ @deprecated("Please use tagsToInclude instead") def addConfiguredIncludes(includes: TextElement) { Console.err.println("WARNING: 'includes' is deprecated - " + "use 'tagsToInclude' instead [includes: " + includes.getText + "]") this.includes = includes.getText } /** * Sets value from nested element excludes. */ def addConfiguredTagsToExclude(tagsToExclude: TextElement) { this.excludes = tagsToExclude.getText } /** * Sets value from nested element excludes. * The excludes attribute has been deprecated and will be removed in a future version of ScalaTest. * Please use the tagsToExclude attribute instead. */ @deprecated("Please use tagsToExclude instead") def addConfiguredExcludes(excludes: TextElement) { Console.err.println("WARNING: 'excludes' is deprecated - " + "use 'tagsToExclude' instead [excludes: " + excludes.getText + "]") this.excludes = excludes.getText } // // Translates a list of strings making up a path into a // single space-delimited string. Uses backslashes to escape // spaces within individual path elements, since that's what // Runner's -p option expects. // private def getSpacedOutPathStr(path: List[String]): String = { path.map(_.replaceAll(" ", """\\ """)).mkString("", " ", "") } // // Translates a whitespace-delimited string into a // whitespace-delimited string, but not the same whitespace. Trims // off leading and trailing whitespace and converts inter-element // whitespace to a single space. // private def singleSpace(str: String): String = { str.trim.replaceAll("\\s+", " ") } } // // Class to hold data from