# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: teyckmans@gmail.com-20081102233319-k726v7jd3q96tx43
# target_branch: http://bazaar.launchpad.net/%7Erussel/gradle/testng/
# testament_sha1: fb1ab86cd50efba01551953a90124aa46e7cfb21
# timestamp: 2008-11-03 00:33:53 +0100
# base_revision_id: svn-v3-trunk1:004c2c75-fc45-0410-b1a2-\
#   da8352e2331b:gradle-core%2Ftrunk:1052
# 
# Begin patch
=== modified file '.bzrignore'
--- .bzrignore	2008-10-11 07:49:35 +0000
+++ .bzrignore	2008-11-01 19:08:30 +0000
@@ -5,3 +5,10 @@
 src/docs/userguide/*.out
 src/docs/userguide/*.log
 src/docs/userguide/*.toc
+out
+test-output
+tmpTest
+gradle-testng.ipr
+gradle-testng.iws
+*.ml
+testng.iml

=== modified file 'src/main/groovy/org/gradle/api/plugins/GroovyPlugin.java'
--- src/main/groovy/org/gradle/api/plugins/GroovyPlugin.java	2008-10-21 08:59:48 +0000
+++ src/main/groovy/org/gradle/api/plugins/GroovyPlugin.java	2008-11-01 19:08:30 +0000
@@ -47,7 +47,7 @@
 
         configureTestCompile(project);
 
-        ((Test) project.task(TEST)).getOptions().getForkOptions().setForkMode(ForkMode.ONCE);
+        ((Test) project.task(TEST)).groovyConfigure();
 
         configureJavadoc(project);
 

=== modified file 'src/main/groovy/org/gradle/api/plugins/JavaPlugin.java'
--- src/main/groovy/org/gradle/api/plugins/JavaPlugin.java	2008-10-29 21:50:56 +0000
+++ src/main/groovy/org/gradle/api/plugins/JavaPlugin.java	2008-11-01 19:08:30 +0000
@@ -37,7 +37,6 @@
 import org.gradle.api.tasks.ide.eclipse.EclipseWtpModule;
 import org.gradle.api.tasks.javadoc.Javadoc;
 import org.gradle.api.tasks.testing.Test;
-import org.gradle.api.tasks.testing.ForkMode;
 import org.gradle.api.tasks.util.FileSet;
 import org.gradle.util.GUtil;
 import org.gradle.util.WrapUtil;
@@ -242,10 +241,7 @@
     private void configureTest(Project project) {
         final Test test = (Test) project.createTask(GUtil.map("type", Test.class, "dependsOn", TEST_COMPILE), TEST);
         test.conventionMapping(DefaultConventionsToPropertiesMapping.TEST);
-        test.getOptions().setFork(true);
-        test.getOptions().getForkOptions().setForkMode(ForkMode.PER_TEST);
-        test.getOptions().getForkOptions().setDir(project.getProjectDir());
-        test.include("**/*Tests.class", "**/*Test.class", "**/Abstract*.class");
+        test.javaConfigure();
         test.doFirst(new TaskAction() {
             public void execute(Task task) {
                 Test test = (Test) task;

=== added file 'src/main/groovy/org/gradle/api/tasks/testing/AbstractAntTestTask.groovy'
--- src/main/groovy/org/gradle/api/tasks/testing/AbstractAntTestTask.groovy	1970-01-01 00:00:00 +0000
+++ src/main/groovy/org/gradle/api/tasks/testing/AbstractAntTestTask.groovy	2008-10-30 22:56:48 +0000
@@ -0,0 +1,17 @@
+package org.gradle.api.tasks.testing
+
+/**
+ * @author Tom Eyckmans
+ */
+abstract class AbstractAntTestTask {
+    abstract void execute(File compiledTestsClassesDir, List classPath, File testResultsDir, File testReportDir, List includes, List excludes, TestOptions testngOptions, AntBuilder ant)
+
+    protected void createAntClassPath(AntBuilder ant, List classpath) {
+        ant.path(id: CLASSPATH_ID) {
+            classpath.each {
+                logger.debug("Add {} to Ant classpath!", it)
+                pathelement(location: it)
+            }
+        }
+    }
+}
\ No newline at end of file

=== modified file 'src/main/groovy/org/gradle/api/tasks/testing/AntJunit.groovy'
--- src/main/groovy/org/gradle/api/tasks/testing/AntJunit.groovy	2008-10-29 21:50:56 +0000
+++ src/main/groovy/org/gradle/api/tasks/testing/AntJunit.groovy	2008-11-01 19:08:30 +0000
@@ -19,7 +19,7 @@
 import org.gradle.util.BootstrapUtil
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
-
+import org.gradle.api.tasks.testing.AbstractAntTestTask
 
 /**
  * @author Hans Dockter
@@ -27,14 +27,14 @@
 //todo: assertions for fork and permissions for non fork
 //todo: offer all the power of ant selectors
 //todo: Find a more stable way to find the ant junit jars
-class AntJunit {
+class AntJunit extends AbstractAntTestTask {
     private static Logger logger = LoggerFactory.getLogger(AntJunit)
 
     private static final String CLASSPATH_ID = 'runtests.classpath'
 
-    public static final String FAILURES_OR_ERRORS_PROPERTY = 'org.gradle.api.tasks.testing.failuresOrErrors'
+    public static final String FAILURES_OR_ERRORS_PROPERTY = "org.gradle.api.tasks.testing.failuresOrErrors"
 
-    void execute(File compiledTestsClassesDir, List classPath, File testResultsDir, File testReportDir, List includes, List excludes, JunitOptions junitOptions, AntBuilder ant) {
+    void execute(File compiledTestsClassesDir, List classPath, File testResultsDir, File testReportDir, List includes, List excludes, TestOptions junitOptions, AntBuilder ant) {
         ant.mkdir(dir: testResultsDir.absolutePath)
         createAntClassPath(ant, classPath + BootstrapUtil.antJunitJarFiles)
         Map otherArgs = [
@@ -73,7 +73,7 @@
         }
     }
 
-    private void createAntClassPath(AntBuilder ant, List classpath) {
+      /*private*/ protected void createAntClassPath(AntBuilder ant, List classpath) {
         ant.path(id: CLASSPATH_ID) {
             classpath.each {
                 logger.debug("Add {} to Ant classpath!", it)

=== added file 'src/main/groovy/org/gradle/api/tasks/testing/AntTestNG.groovy'
--- src/main/groovy/org/gradle/api/tasks/testing/AntTestNG.groovy	1970-01-01 00:00:00 +0000
+++ src/main/groovy/org/gradle/api/tasks/testing/AntTestNG.groovy	2008-11-02 23:33:19 +0000
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.tasks.testing
+
+import org.gradle.util.BootstrapUtil
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import org.gradle.api.tasks.compile.AbstractOptions
+import org.gradle.api.tasks.testing.AbstractAntTestTask
+
+/**
+ * @author Tom Eyckmans
+ */
+class AntTestNG extends AbstractAntTestTask {
+    private static Logger logger = LoggerFactory.getLogger(AntTestNG);
+
+    static final String CLASSPATH_ID = 'runtests.classpath'
+
+    static final String FAILURES_OR_ERRORS_PROPERTY = 'org.gradle.api.tasks.testing.failuresOrErrors'
+
+    void execute(File compiledTestsClassesDir, List classPath, File testResultsDir, File testReportDir, List includes, List excludes, TestOptions options, AntBuilder ant) {
+        final TestNGOptions testngOptions = (TestNGOptions)options;
+
+        logger.info("executing testng tests...");
+
+        ant.mkdir(dir: testResultsDir.absolutePath)
+
+        // @TODO there must be a better way of doing this...
+        File testngJarFile = null;
+        if ( classPath != null && classPath.size() > 0 ) {
+            final Iterator<File> classPathIt = classPath.iterator();
+            while ( testngJarFile == null && classPathIt.hasNext() ) {
+                final File classPathFile = classPathIt.next();
+                final String classPathFileName = classPathFile.name;
+
+                if ( classPathFile.isFile() && classPathFileName.startsWith("testng") ) {
+                    testngJarFile = classPathFile;
+                }
+            }
+        }
+
+        if ( testngJarFile == null ) {
+            throw new IllegalArgumentException("Failed to download testng dependency");
+        }
+
+        // @TODO download testng via dependency management
+        createAntClassPath(ant, classPath /*+ BootstrapUtil.antTestNGJarFiles*/);
+
+        testngOptions.outputdir = testReportDir.absolutePath;
+        testngOptions.workingDir = testResultsDir.absolutePath;
+
+        Map otherArgs = [
+                failureProperty : FAILURES_OR_ERRORS_PROPERTY,
+                // skippedProperty : ??
+        ]
+
+        ant.taskdef([resource : "testngtasks", classpath: testngJarFile.absoluteFile ])
+
+        if ( testngOptions.suiteXmlBuilder != null ) {
+            // @TODO store suiteXml file and use it to run testng
+        }
+        else {
+            // @TODO generate a suiteXml file based on user input and use it to run testng
+        }
+
+        ant.testng(otherArgs + testngOptions.optionMap()) {
+//            testngOptions.jvmArgs.each {
+//                jvmarg(value: it)
+//            }
+//            testngOptions.systemProperties.each {String key, String value ->
+//                sysproperty(key: key, value: value)
+//            }
+//            testngOptions.environment.each {String key, String value ->
+//                env(key: key, value: value)
+//            }
+            classpath {
+                classPath.each {
+                    pathelement(location : it)
+                }
+            }
+            classfileset (dir: compiledTestsClassesDir.absolutePath ) {
+                includes.each {
+                    include(name: it)
+                }
+                excludes.each {
+                    exclude(name: it)
+                }
+            }
+        }
+
+        logger.info("testng tests executed.");
+
+        /*
+        not happy with the output: class and package information is lost
+
+        logger.info("generating test report...")
+
+        ant.junitreport(todir: testReportDir.absolutePath) {
+            fileset(file: new File(testResultsDir, 'Ant suite/Ant test.xml'))
+            report(todir: testReportDir.absolutePath, format: 'noframes') // currently the testng docs mention an incompatability with the frames format
+        }
+        
+        logger.info("test report generated.")
+        */
+    }
+
+
+}

=== renamed file 'src/main/groovy/org/gradle/api/tasks/testing/JunitForkOptions.groovy' => 'src/main/groovy/org/gradle/api/tasks/testing/ForkOptions.groovy'
--- src/main/groovy/org/gradle/api/tasks/testing/JunitForkOptions.groovy	2008-03-10 06:51:58 +0000
+++ src/main/groovy/org/gradle/api/tasks/testing/ForkOptions.groovy	2008-10-30 22:56:48 +0000
@@ -21,7 +21,7 @@
 /**
  * @author Hans Dockter
  */
-class JunitForkOptions extends AbstractOptions {
+class ForkOptions extends AbstractOptions {
     ForkMode forkMode = ForkMode.PER_TEST
     String timeout
     String maxMemory

=== modified file 'src/main/groovy/org/gradle/api/tasks/testing/JunitOptions.groovy'
--- src/main/groovy/org/gradle/api/tasks/testing/JunitOptions.groovy	2008-03-10 06:51:58 +0000
+++ src/main/groovy/org/gradle/api/tasks/testing/JunitOptions.groovy	2008-10-30 22:56:48 +0000
@@ -21,8 +21,7 @@
 /**
  * @author Hans Dockter
  */
-class JunitOptions extends AbstractOptions {
-    boolean fork = true
+class JunitOptions extends TestOptions {
     boolean filterTrace = true
     boolean showOutput = false
     boolean outputToFormatters = true
@@ -32,7 +31,7 @@
     String printSummary = 'true'
 
     FormatterOptions formatterOptions = new FormatterOptions()
-    JunitForkOptions forkOptions = new JunitForkOptions()
+
 
     Map systemProperties = [:]
     Map environment = [:]

=== added file 'src/main/groovy/org/gradle/api/tasks/testing/JunitTestPolicy.groovy'
--- src/main/groovy/org/gradle/api/tasks/testing/JunitTestPolicy.groovy	1970-01-01 00:00:00 +0000
+++ src/main/groovy/org/gradle/api/tasks/testing/JunitTestPolicy.groovy	2008-11-01 19:08:30 +0000
@@ -0,0 +1,36 @@
+package org.gradle.api.tasks.testing
+
+import org.gradle.api.Project
+
+/**
+ * @author Tom Eyckmans
+ */
+class JunitTestPolicy extends TestPolicy {
+
+    void init(Project project, Test test)
+    {
+        test.options = new JunitOptions();
+        test.antTestTask = new AntJunit();
+    }
+
+    public void javaConfigure(Project project, Test test)
+    {
+        final JunitOptions options = (JunitOptions)test.getOptions();
+
+        options.setFork(true);
+        options.getForkOptions().setForkMode(ForkMode.PER_TEST);
+        options.getForkOptions().setDir(project.getProjectDir());
+
+        options.getForkOptions().setForkMode(ForkMode.ONCE);
+
+        super.javaConfigure(project, test);
+    }
+
+    void groovyConfigure(Project project, Test test)
+    {
+        final JunitOptions options = (JunitOptions)test.getOptions();
+
+        options.getForkOptions().setForkMode(ForkMode.ONCE);
+    }
+
+}
\ No newline at end of file

=== modified file 'src/main/groovy/org/gradle/api/tasks/testing/Test.java'
--- src/main/groovy/org/gradle/api/tasks/testing/Test.java	2008-10-29 21:50:56 +0000
+++ src/main/groovy/org/gradle/api/tasks/testing/Test.java	2008-11-01 19:08:30 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 the original author or authors.
+ * Copyright 2007-8 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,40 +20,72 @@
 import org.gradle.api.internal.ConventionTask;
 import org.gradle.api.tasks.compile.ClasspathConverter;
 import org.gradle.api.tasks.util.ExistingDirsFilter;
+import org.gradle.api.tasks.testing.AbstractAntTestTask;
 import org.gradle.util.GUtil;
 import org.gradle.util.WrapUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+//import org.gradle.api.tasks.compile.AbstractOptions;
+
 import java.io.File;
 import java.util.Arrays;
 import java.util.List;
 
 /**
- * A task for executin Junit 3.8.x and Junit 4 tests.
+ * A task for executing Junit 3.8.x and Junit 4 tests.
  * 
  * @author Hans Dockter
  */
 public class Test extends ConventionTask {
     private static Logger logger = LoggerFactory.getLogger(Test.class);
 
-    private File testClassesDir = null;
+     /**
+     * The directory with the compiled test classes
+     */
+   private File testClassesDir = null;
 
+    /**
+     * The directory where the test results are put. Right now only the xml format of the test results is supported.
+     */
     private File testResultsDir = null;
 
+    /**
+     * The directory to generate the test report into.
+     */
     private File testReportDir = null;
 
-    private JunitOptions options = new JunitOptions();
-
+    /**
+     * Include pattern for the filess in the test classes directory (e.g. '**&#2F;*Test.class')).
+     */
     private List<String> includes;
 
+    /**
+     * Exclude pattern for the filess in the test classes directory (e.g. '**&#2F;Abstract*.class').
+     */
     private List<String> excludes;
 
+    /**
+     * If true the build stops with an excepton in case a test fails or throws an uncatched exception. The build
+     * stops in this case after ALL tests have been executed.
+     */
     private boolean stopAtFailuresOrErrors = true;
 
+    /**
+     * This property is used internally by Gradle. It is usually not used by build scripts.
+     * A list of files added to the compile classpath. The files should point to jars or directories containing
+     * class files. The files added here are not shared in a multi-project build and are not mentioned in
+     * a dependency descriptor if you upload your library to a repository.
+     */
     private List unmanagedClasspath = null;
 
-    private AntJunit antJunit = new AntJunit();
+    private TestPolicy testPolicy = null;
+    /**
+     * This task delegates to Ants Junit task for the execution of the tests. This options contain most of the options
+     * of Ants Junit task.
+     */
+    private TestOptions options = null;
+    private AbstractAntTestTask antTestTask = null;
 
     private DependencyManager dependencyManager = null;
 
@@ -71,16 +103,26 @@
     }
 
     protected void executeTests(Task task) {
-        if (getTestClassesDir() == null) throw new InvalidUserDataException(
+        testClassesDir = getTestClassesDir();
+        testResultsDir = getTestResultsDir();
+        testReportDir = getTestReportDir();
+
+        if (testClassesDir == null) throw new InvalidUserDataException(
                 "The testClassesDir property is not set, testing can't be triggered!");
-        if (getTestResultsDir() == null) throw new InvalidUserDataException(
+        if (testResultsDir == null) throw new InvalidUserDataException(
                 "The testResultsDir property is not set, testing can't be triggered!");
 
-        existingDirsFilter.checkExistenceAndThrowStopActionIfNot(getTestClassesDir());
-
-        antJunit.execute(getTestClassesDir(), getClasspath(), getTestResultsDir(), getTestReportDir(), includes,
-                excludes, options, getProject().getAnt());
-        if (stopAtFailuresOrErrors && GUtil.isTrue(getProject().getAnt().getProject().getProperty(AntJunit.FAILURES_OR_ERRORS_PROPERTY))) {
+        existingDirsFilter.checkExistenceAndThrowStopActionIfNot(testClassesDir);
+
+        testResultsDir.mkdir();
+        testReportDir.mkdir();
+
+        antTestTask.execute(testClassesDir, getClasspath(), testResultsDir, testReportDir, includes, excludes, options, getProject().getAnt());
+
+        final String encounteredFailuresOrErrorsString = getProject().getAnt().getProject().getProperty(AntJunit.FAILURES_OR_ERRORS_PROPERTY);
+        final boolean encounteredFailuresOrErrors = GUtil.isTrue(encounteredFailuresOrErrorsString);
+
+        if (stopAtFailuresOrErrors && encounteredFailuresOrErrors) {
             throw new GradleException("There were failing tests. See the report at " + getTestReportDir() + ".");
         }
     }
@@ -177,7 +219,7 @@
      * Returns an JUnit option instance to read or modify.
      * @return
      */
-    public JunitOptions getOptions() {
+    public TestOptions getOptions() {
         return options;
     }
 
@@ -186,7 +228,7 @@
      * 
      * @param options The new instance
      */
-    public void setOptions(JunitOptions options) {
+    public void setOptions(TestOptions options) {
         this.options = options;
     }
 
@@ -263,17 +305,17 @@
     /**
      * Returns the AntJunit instance the test execution is delegated to.
      */
-    public AntJunit getAntJunit() {
-        return antJunit;
+    public AbstractAntTestTask getAntTestTask() {
+        return antTestTask;
     }
 
     /**
      * Sets the AntJunit instance the test execution is delegated to.
      * 
-     * @param antJunit The new instance
+     * @param antTestTask The new instance
      */
-    public void setAntJunit(AntJunit antJunit) {
-        this.antJunit = antJunit;
+    public void setAntTestTask(AbstractAntTestTask antTestTask) {
+        this.antTestTask = antTestTask;
     }
 
     /**
@@ -291,4 +333,78 @@
     public void setDependencyManager(DependencyManager dependencyManager) {
         this.dependencyManager = dependencyManager;
     }
+
+    /**
+     * Base method to initialize the test policy to use.
+     *
+     * Note: this function provides an entry point of other TestPolcies that are not yet bundled in the distribution.
+     *
+     * @param testPolicy The test policy
+     */
+    public void initTestPolicy(TestPolicy testPolicy) {
+        if ( testPolicy == null ) throw new IllegalArgumentException("testPolicy == null!");
+
+        this.testPolicy = testPolicy;
+        testPolicy.init(getProject(), this);
+    }
+
+    /**
+     * Initialize this test with the JunitTestPolicy.
+     *
+     * @see JunitTestPolicy
+     */
+    public void useJunit() {
+        initTestPolicy(new JunitTestPolicy());
+    }
+
+    /**
+     * Initialize this test with the TestNGTestPolicy
+     *
+     * @see TestNGTestPolicy
+     */
+    public void useTestNG () {
+        initTestPolicy(new TestNGTestPolicy());
+    }
+
+    /**
+     * Configures this test for Java test execution.
+     *
+     * If no policy is initialized yet the default test policy will be used (currently hard coded to the JunitTestPolciy).
+     */
+    public void javaConfigure()
+    {
+        initDefaultTestPolicyIfNeeded();
+
+        testPolicy.javaConfigure(getProject(), this);
+    }
+
+    /**
+     * Configures this test for Groovy test execution.
+     *
+     * If no policy is initialized yet the default test policy will be used (currently hard coded to the JunitTestPolciy).
+     */
+    public void groovyConfigure()
+    {
+        initDefaultTestPolicyIfNeeded();
+
+        testPolicy.groovyConfigure(getProject(), this);
+    }
+
+    /**
+     * Initializes this test with the default TestPolicy if no TestPolicy is set yet.
+     *
+     * Currently the default TestPolciy is hard coded to use the JunitTestPolciy.
+     */
+    private void initDefaultTestPolicyIfNeeded()
+    {
+        if ( testPolicy == null ) {
+            // @TODO make configurable
+            useJunit();
+        }
+    }
+
+    public TestPolicy getTestPolicy()
+    {
+        return testPolicy;
+    }
 }

=== added file 'src/main/groovy/org/gradle/api/tasks/testing/TestNGOptions.groovy'
--- src/main/groovy/org/gradle/api/tasks/testing/TestNGOptions.groovy	1970-01-01 00:00:00 +0000
+++ src/main/groovy/org/gradle/api/tasks/testing/TestNGOptions.groovy	2008-11-02 23:33:19 +0000
@@ -0,0 +1,57 @@
+package org.gradle.api.tasks.testing
+
+import org.gradle.api.tasks.compile.AbstractOptions
+import groovy.xml.MarkupBuilder
+
+/**
+ * @author Tom Eyckmans
+ */
+class TestNGOptions extends TestOptions {
+    String annotations = 'JDK'
+    boolean dumpCommand = false
+    boolean enableAssert = true
+    boolean haltonfailure = false
+    boolean haltonskipped = false
+
+//    List<String> groups = new ArrayList<String>()
+//    List<String> excludedgroups = new ArrayList<String>()
+    String jvm = "java"
+
+    String outputdir
+    String sourcedir
+//    String suiteRunnerClass
+//    boolean parallel = false
+//    int threadCount = 1
+//    String testJar
+//    long timeOut = Long.MAX_VALUE
+//    boolean useDefaultListeners = true
+    String workingDir
+//    String suitename = 'Ant suite'
+//    String testname = 'Ant test'
+
+//    List jvmArgs = []
+//    Map systemProperties = [:]
+//    Map environment = [:]
+
+//    List excludedFieldsFromOptionMap() {
+//        ['systemProperties', 'jvmArgs', 'environment']
+//    }
+    Map optionMap() {
+        super.optionMap()
+    }
+
+    JunitOptions fork(Map forkArgs) {
+        fork = true
+        forkOptions.define(forkArgs)
+        this
+    }
+
+    StringWriter suiteXmlWriter = null;
+    MarkupBuilder suiteXmlBuilder = null;
+    MarkupBuilder suiteXmlBuilder()
+    {
+        suiteXmlWriter = new StringWriter()
+        suiteXmlBuilder = new MarkupBuilder(suiteXmlWriter)
+        return suiteXmlBuilder
+    }
+}
\ No newline at end of file

=== added file 'src/main/groovy/org/gradle/api/tasks/testing/TestNGTestPolicy.groovy'
--- src/main/groovy/org/gradle/api/tasks/testing/TestNGTestPolicy.groovy	1970-01-01 00:00:00 +0000
+++ src/main/groovy/org/gradle/api/tasks/testing/TestNGTestPolicy.groovy	2008-11-02 23:33:19 +0000
@@ -0,0 +1,28 @@
+package org.gradle.api.tasks.testing
+
+import org.gradle.api.Project
+import org.gradle.api.DependencyManager
+import org.gradle.api.plugins.JavaPlugin
+import org.gradle.api.dependencies.Configuration
+
+/**
+ * @author Tom Eyckmans
+ */
+class TestNGTestPolicy extends TestPolicy {
+    void init(Project project, Test test)
+    {
+        System.out.println("TestNGTestPolicy [init]")
+        test.options = new TestNGOptions()
+        test.antTestTask = new AntTestNG();
+
+        final DependencyManager dependencies = project.getDependencies();
+        
+        dependencies.dependencies([JavaPlugin.TEST_COMPILE], "org.testng:testng:5.8:jdk15");
+    }
+
+    void javaConfigure(Project project, Test test)
+    {
+        // ??
+        super.javaConfigure(project, test);
+    }
+}
\ No newline at end of file

=== added file 'src/main/groovy/org/gradle/api/tasks/testing/TestOptions.groovy'
--- src/main/groovy/org/gradle/api/tasks/testing/TestOptions.groovy	1970-01-01 00:00:00 +0000
+++ src/main/groovy/org/gradle/api/tasks/testing/TestOptions.groovy	2008-10-30 22:56:48 +0000
@@ -0,0 +1,11 @@
+package org.gradle.api.tasks.testing
+
+import org.gradle.api.tasks.compile.AbstractOptions
+
+/**
+ * @author Tom Eyckmans
+ */
+abstract class TestOptions extends AbstractOptions {
+    boolean fork = true;
+    ForkOptions forkOptions = new ForkOptions()
+}
\ No newline at end of file

=== added file 'src/main/groovy/org/gradle/api/tasks/testing/TestPolicy.groovy'
--- src/main/groovy/org/gradle/api/tasks/testing/TestPolicy.groovy	1970-01-01 00:00:00 +0000
+++ src/main/groovy/org/gradle/api/tasks/testing/TestPolicy.groovy	2008-11-01 19:08:30 +0000
@@ -0,0 +1,20 @@
+package org.gradle.api.tasks.testing
+
+import org.gradle.api.Project
+
+/**
+ * @author Tom Eyckmans
+ */
+abstract class TestPolicy {
+    abstract void init(Project project, Test test);
+
+    void javaConfigure(Project project, Test test)
+    {
+        test.include("**/*Tests.class", "**/*Test.class", "**/Abstract*.class");
+    }
+
+    void groovyConfigure(Project project, Test test)
+    {
+        
+    }
+}
\ No newline at end of file

=== modified file 'src/test/groovy/org/gradle/api/internal/project/DefaultProjectTest.groovy'
--- src/test/groovy/org/gradle/api/internal/project/DefaultProjectTest.groovy	2008-09-27 07:09:15 +0000
+++ src/test/groovy/org/gradle/api/internal/project/DefaultProjectTest.groovy	2008-11-01 19:08:30 +0000
@@ -450,7 +450,7 @@
         Task mockTask = context.mock(Task)
         project.tasks[TEST_TASK_NAME] = mockTask
         context.checking {
-            one(mockTask).configure(testConfigureClosure); will(returnValue(mockTask))
+            one(mockTask).javaConfigure(testConfigureClosure); will(returnValue(mockTask))
         }
 
         assertThat(project.task(TEST_TASK_NAME, testConfigureClosure), sameInstance(mockTask))

=== modified file 'src/test/groovy/org/gradle/api/tasks/testing/JunitForkOptionsTest.groovy'
--- src/test/groovy/org/gradle/api/tasks/testing/JunitForkOptionsTest.groovy	2008-07-07 11:43:37 +0000
+++ src/test/groovy/org/gradle/api/tasks/testing/JunitForkOptionsTest.groovy	2008-10-30 22:56:48 +0000
@@ -24,10 +24,10 @@
  * @author Hans Dockter
  */
 class JunitForkOptionsTest {
-    JunitForkOptions junitForkOptions
+    ForkOptions junitForkOptions
 
     @Before public void setUp()  {
-        junitForkOptions = new JunitForkOptions()
+        junitForkOptions = new ForkOptions()
     }
 
     @Test public void testJunitForkOptions() {

=== modified file 'src/test/groovy/org/gradle/api/tasks/testing/JunitOptionsTest.groovy'
--- src/test/groovy/org/gradle/api/tasks/testing/JunitOptionsTest.groovy	2008-07-07 11:43:37 +0000
+++ src/test/groovy/org/gradle/api/tasks/testing/JunitOptionsTest.groovy	2008-10-30 22:56:48 +0000
@@ -32,7 +32,7 @@
     @Before public void setUp()  {
         junitOptions = new JunitOptions()
         junitOptions.formatterOptions  = [optionMap: {TEST_FORMATTER_OPTION_MAP}] as FormatterOptions
-        junitOptions.forkOptions = [optionMap: {TEST_FORK_OPTION_MAP}] as JunitForkOptions
+        junitOptions.forkOptions = [optionMap: {TEST_FORK_OPTION_MAP}] as ForkOptions
     }
 
     @Test public void testCompileOptions() {
@@ -104,7 +104,7 @@
         junitOptions.forkOptions = [define: {Map args ->
             forkUseCalled = true
             assertEquals(TEST_FORK_OPTION_MAP, args)
-        }] as JunitForkOptions
+        }] as ForkOptions
         assert junitOptions.fork(TEST_FORK_OPTION_MAP).is(junitOptions)
         assertTrue(junitOptions.fork)
         assertTrue(forkUseCalled)

=== modified file 'src/test/groovy/org/gradle/api/tasks/testing/TestTest.java'
--- src/test/groovy/org/gradle/api/tasks/testing/TestTest.java	2008-10-29 21:50:56 +0000
+++ src/test/groovy/org/gradle/api/tasks/testing/TestTest.java	2008-11-01 19:31:49 +0000
@@ -16,7 +16,6 @@
 
 package org.gradle.api.tasks.testing;
 
-import groovy.mock.interceptor.MockFor;
 import groovy.util.AntBuilder;
 import org.gradle.api.DependencyManager;
 import org.gradle.api.GradleException;
@@ -34,7 +33,6 @@
 import org.hamcrest.Description;
 import static org.junit.Assert.*;
 import org.junit.Before;
-import org.junit.Assert;
 import org.jmock.lib.legacy.ClassImposteriser;
 import org.jmock.integration.junit4.JUnit4Mockery;
 import org.jmock.Expectations;
@@ -42,7 +40,6 @@
 import org.jmock.api.Invocation;
 
 import java.io.File;
-import java.io.PrintWriter;
 import java.util.List;
 
 
@@ -70,6 +67,7 @@
 
     private JUnit4Mockery context = new JUnit4Mockery();
 
+    TestPolicy testPolicyMock;
     AntJunit antJunitMock;
 
     DependencyManager dependencyManagerMock;
@@ -83,10 +81,10 @@
         classpathConverterMock = context.mock(ClasspathConverter.class);
         existentDirsFilterMock = context.mock(ExistingDirsFilter.class);
         dependencyManagerMock = context.mock(DependencyManager.class);
+        testPolicyMock = context.mock(TestPolicy.class);
         super.setUp();
         test = new Test(getProject(), AbstractTaskTest.TEST_TASK_NAME);
         ((AbstractProject) test.getProject()).setProjectDir(TEST_ROOT_DIR);
-        test.setAntJunit(antJunitMock);
     }
 
     public AbstractTask getTask() {
@@ -94,8 +92,9 @@
     }
 
     @org.junit.Test public void testInit() {
-        assertNotNull(test.getAntJunit());
-        assertNotNull(test.getOptions());
+        assertNull(test.getTestPolicy());
+        assertNull(test.getAntTestTask());
+        assertNull(test.getOptions());
         assertNotNull(test.existingDirsFilter);
         assertNotNull(test.classpathConverter);
         assertNull(test.getTestClassesDir());
@@ -105,13 +104,22 @@
         assertNull(test.getIncludes());
         assertNull(test.getExcludes());
         assertNull(test.getUnmanagedClasspath());
+
         assert test.isStopAtFailuresOrErrors();
+
+        test.javaConfigure();
+
+        assertNotNull(test.getTestPolicy());
+        assertNotNull(test.getAntTestTask());
+        assertNotNull(test.getOptions());
+        assertNotNull(test.getIncludes());
     }
 
     @org.junit.Test
     public void testExecute() {
         setUpMocks(test);
         setExistingDirsFilter();
+        test.setAntTestTask(antJunitMock);
         context.checking(new Expectations() {{
             one(antJunitMock).execute(TEST_TEST_CLASSES_DIR, TEST_CONVERTED_CLASSPATH, TEST_TEST_RESULTS_DIR, TEST_TEST_REPORT_DIR,
                     test.getIncludes(), test.getExcludes(), test.getOptions(), getProject().getAnt());
@@ -121,25 +129,27 @@
     }
 
     @org.junit.Test(expected = GradleException.class) 
-    public void testExecuteWithTestFailuresAndStopAtFailures() {
+    public void testJavaExecuteWithTestFailuresAndStopAtFailures() {
         setUpMocks(test);
         setExistingDirsFilter();
+        test.setAntTestTask(antJunitMock);
         context.checking(new Expectations() {{
             one(antJunitMock).execute(TEST_TEST_CLASSES_DIR, TEST_CONVERTED_CLASSPATH, TEST_TEST_RESULTS_DIR, TEST_TEST_REPORT_DIR,
                     test.getIncludes(), test.getExcludes(), test.getOptions(), getProject().getAnt());
-            will(new AntPropertyAction(AntJunit.FAILURES_OR_ERRORS_PROPERTY, "somevalue"));
+            will(new AntPropertyAction("org.gradle.api.tasks.testing.failuresOrErrors", "somevalue"));
         }});
         test.execute();
     }
 
-    @org.junit.Test public void testExecuteWithTestFailuresAndContinueWithFailures() {
+    @org.junit.Test public void testJavaExecuteWithTestFailuresAndContinueWithFailures() {
         setUpMocks(test);
         setExistingDirsFilter();
         test.setStopAtFailuresOrErrors(false);
+        test.setAntTestTask(antJunitMock);
         context.checking(new Expectations() {{
             one(antJunitMock).execute(TEST_TEST_CLASSES_DIR, TEST_CONVERTED_CLASSPATH, TEST_TEST_RESULTS_DIR, TEST_TEST_REPORT_DIR,
                     test.getIncludes(), test.getExcludes(), test.getOptions(), getProject().getAnt());
-            will(new AntPropertyAction(AntJunit.FAILURES_OR_ERRORS_PROPERTY, "somevalue"));
+            will(new AntPropertyAction("org.gradle.api.tasks.testing.failuresOrErrors", "somevalue"));
         }});
         test.execute();
     }
@@ -198,6 +208,13 @@
                     GUtil.addLists(WrapUtil.toList(TEST_TEST_CLASSES_DIR), TEST_UNMANAGED_CLASSPATH, TEST_DEPENDENCY_MANAGER_CLASSPATH));
             will(returnValue(TEST_CONVERTED_CLASSPATH));
         }});
+
+//        context.checking(new Expectations() {{
+//            one(testPolicyMock).init(getProject(), test);
+//        }});
+//        test.initTestPolicy(testPolicyMock);
+        test.setAntTestTask(antJunitMock);
+        test.setOptions(new JunitOptions());
     }
 
     @org.junit.Test public void testIncludes() {
@@ -235,6 +252,7 @@
         String propertyValue;
 
         public AntPropertyAction(String propertyKey, String propertyValue) {
+            if ( propertyKey == null ) throw new IllegalArgumentException(" propertyKey == null! ");
             this.propertyKey = propertyKey;
             this.propertyValue = propertyValue;
         }

=== modified file 'src/test/groovy/org/gradle/integtests/ProjectLoadingIntegrationTest.java'
--- src/test/groovy/org/gradle/integtests/ProjectLoadingIntegrationTest.java	2008-10-10 19:36:41 +0000
+++ src/test/groovy/org/gradle/integtests/ProjectLoadingIntegrationTest.java	2008-10-30 22:56:48 +0000
@@ -15,10 +15,8 @@
  */
 package org.gradle.integtests;
 
-import static org.apache.commons.io.FileUtils.*;
 import static org.junit.Assert.*;
 import org.junit.Test;
-import org.junit.Ignore;
 
 import java.io.File;
 import java.io.IOException;
@@ -26,7 +24,7 @@
 public class ProjectLoadingIntegrationTest extends AbstractIntegrationTest {
     @Test
     public void handlesSimilarlyNamedBuildFilesInSameDirectory() {
-        File buildFile1 = getTestBuildFile("similarly-named build.gradle");
+        File buildFile1 = getTestBuildFile("similarly-named-build.gradle");
         File buildFile2 = getTestBuildFile("similarly_named_build_gradle");
         assertEquals(buildFile1.getParentFile(), buildFile2.getParentFile());
 

# Begin bundle
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
