<?xml version="1.0" encoding="UTF-8"?>
<!--
    File:    nbtargets-testng-cvg.xml
    Version: 0.1

    Enable NetBeans to use TestNG to run and debug project tests.

    Audience: developers who wish to use NetBeans to debug project test
    suites written using TestNG, and are comfortable editing Ant's build.xml.

    Status: useful workaround

    Effects:
    - Test Commands run TestNG tests (annotated with @Test)
      * Test Project (project context menu):
        If test/testng.xml exists, runs its tests with TestNG, not JUnit.
        Otherwise runs tests in classes test/**/*.class with TestNG, not JUnit.
      * Run File (project/test/file context menu):
        Runs tests in test class with TestNG, not JUnit.
      * Debug File (project/test/file context menu):
        Runs tests in test class with TestNG, not JUnit.
        (Tip: breakpoint on java.lang.AssertionError to debug assert failure.)
        (Advanced tip: If Ant property testng.debug.groups is set to a comma
        separated list of group names, Debug File will run only tests marked
        with those groups.  Set it in Tools/Options/Misc/Ant/Properties.  For
        example, mark a single buggy test with @Test(groups={"debug123"}) and
        set testng.debug.groups=debug123 to run just that test with debugger.)
    - Reports (View via ~Files~ pane file context menu)
      * TestNG test results are written to build/test/results/index.html

    To install:
    0. Download TestNG (site listed below).
    1. Add TestNG jar to libraries (Tools/Library Manager)
    2. Add library to project test libraries.
    3. Import this file in the build.xml of a NetBeans project.
       (Import it *before* build-impl.xml: among imports, first def prevails.)
    Example:

    <project name="MyProject" default="default" basedir=".">
      <description>Builds, tests, and runs the project MyProject.</description>
      <import file="nbtargets-testng.xml" /> 
      <import file="nbproject/build-impl.xml" />
    </project>

    Tested using
    - NetBeans 5.5
      (www.netbeans.org)
    - TestNG 5.4
      (www.testng.org)

    Caveats of this draft
    - This file, nbtargets-testng-cvg.xml, works with the NetBeans
      codecoverage module (2.2.1, 2007.02).  But, this version uses antcall 
      to invoke conditional targets, so the result (tests.failed) is not set,
      so NetBeans incorrectly reports "BUILD SUCCESSFUL" despite failed tests.
    - In contrast, nbtargets-testng.xml correctly reports
      "BUILD FAILED" if there were test failures.  However, that version
      does not work with the NetBeans codecoverage module (2.2.1, 2007.02)
      (the classes are not instrumented until after the tests are run,
      so no code coverage data is recorded).
    - These Ant targets refer to the default directory property names,
      but adding a source or test directory more than once per project
      may cause NetBeans to use different property names for the
      directories.

    Related work
    - TestNG Support 4.6
      * Provides a TestNG test file creator in New File.
      * Provides a Show Test Results command (user avoids files tab)
      * Does not change behavior of Test Project (uses separate command).
      * Does not change behavior of Run File or Debug File on test files.

    For more info, see "NetBeans and Alternative Test Runners"
    http://www.geocities.com/nimarukan/netbeans/nbtargets/nbtargets-alt-test-runners.html
-->
<project name="nbtargets-testng-cvg">

  <!-- OVERRIDES -->

  <!-- override -do-test-run to use TestNG, not JUnit -->
  <target name="-do-test-run"
          depends="init,compile-test,-pre-test-run"
          if="have.tests" >
    <antcall target="-testng-run"/>
  </target>

  <!-- override -do-test-run-single to use TestNG, not JUnit -->
  <target name="-do-test-run-single" 
          depends="init,compile-test-single,-pre-test-run-single"
          if="have.tests">
    <antcall target="-testng-run-single"/>
  </target>

  <!-- override -debug-start-debuggee-test to use TestNG, not JUnit -->
  <target name="-debug-start-debuggee-test" 
          depends="init,compile-test"
          if="have.tests">
    <antcall target="-testng-debug-single"/>
  </target>

  <!-- TESTNG-RUN -->

  <!-- Run tests specified in test/testng.xml or if no such file, then
       run all @Test methods in classes in test directory. -->
  <target name="-testng-run">
    <available property="testng.xml"
               file="${test.src.dir}${file.separator}testng.xml"
               value="${test.src.dir}${file.separator}testng.xml"/>
    <!-- just one of these runs, depending on existence of test/testng.xml -->
    <antcall target="-testng-run-xml"/>
    <antcall target="-testng-run-classes"/>
  </target>

  <!-- If test/testng.xml exists, run its tests -->
  <target name="-testng-run-xml"
          if="testng.xml">
    <!-- Call <java> rather than <testng> so NetBeans' Ant logger
         will link output stack trace lines to source files. -->
    <java classname="org.testng.TestNG"
          classpath="${run.test.classpath}"
          fork="true" resultproperty="testng.result.code">
      <jvmarg line="-enableassertions"/> <!-- TestNG tests often assert -->
      <jvmarg value="-Dtestng.test.classpath=${build.test.classes.dir}"/>
      <arg line="-d ${build.test.results.dir}"/>
      <arg line="-log 2 -usedefaultlisteners true"/> <!-- show stack trace -->
      <arg line="-sourcedir ${test.src.dir}"/> <!-- for jdk1.4 annotations -->
      <arg line="${testng.xml}"/> <!-- must be after '-'options -->
    </java>
    <echo level="info">View test results in ${build.test.results.dir}/index.html</echo>
    <echo level="info"/>
    <condition property="tests.failed">
      <not><equals arg1="0" arg2="${testng.result.code}"/></not>
    </condition>
  </target>

  <!-- If no test/testng.xml exists, look for @Test in all test/**/classes -->
  <target name="-testng-run-classes"
          unless="testng.xml" >
    <pathconvert property="testng.classnames.args" pathsep=" ">
      <path>
        <fileset dir="${build.test.classes.dir}" includes="**/*.class"/>
      </path>
      <map from="${basedir}${file.separator}" to="-testclass "/>
    </pathconvert>

    <!-- Call <java> rather than <testng> so NetBeans' Ant logger
         will link output stack trace lines to source files. -->
    <java classname="org.testng.TestNG"
          classpath="${run.test.classpath}"
          fork="true" resultproperty="testng.result.code">
      <jvmarg line="-enableassertions"/> <!-- TestNG tests often assert -->
      <jvmarg value="-Dtestng.test.classpath=${build.test.classes.dir}"/>
      <arg line="-d ${build.test.results.dir}"/>
      <arg line="-log 2 -usedefaultlisteners true"/> <!-- show stack trace -->
      <arg line="-sourcedir ${test.src.dir}"/> <!-- for jdk1.4 annotations -->
      <arg value="-suitename"/> <arg value="All test classes"/>
      <arg value="-testname"/> <arg value="All test classes"/>
      <arg line="${testng.classnames.args}"/>
    </java>

    <echo level="info">View test results in ${build.test.results.dir}/index.html</echo>
    <echo level="info"/>

    <condition property="tests.failed">
      <not><equals arg1="0" arg2="${testng.result.code}"/></not>
    </condition>
  </target>

  <!-- TESTNG-RUN-SINGLE -->

  <!-- Run selected test.includes file with TestNG -->
  <target name="-testng-run-single">
    <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
    
    <!-- Convert p1/C1.java p2/C2.java to "-testclass p1.C1 -testclass p2.C2"
         (contrary to TestNG doc, -testclass prefixes each class, not list) -->
    <pathconvert property="testng.classnames.args" dirsep="." pathsep=" ">
      <path><filelist dir="." files="${test.includes}"/></path>
      <mapper type="glob" from="*.java" to="*"/>
      <map from="${basedir}${file.separator}" to="-testclass "/>
    </pathconvert>
    <!-- Convert p1/C1.java p2/C2.java to "p1.C1_p2.C2" for title -->
    <pathconvert property="testng.suitename" pathsep="_" dirsep=".">
      <path><filelist dir="." files="${test.includes}"/></path>
      <mapper type="glob" from="*.java" to="*"/>
      <map from="${basedir}${file.separator}" to=""/>
    </pathconvert>

    <!-- Call <java> rather than <testng> so NetBeans' Ant logger
         will link output stack trace lines to source files. -->
    <java classname="org.testng.TestNG"
          classpath="${run.test.classpath}"
          fork="true" resultproperty="testng.result.code">
      <jvmarg line="-enableassertions"/> <!-- TestNG tests often assert --> 
      <jvmarg value="-Dtestng.test.classpath=${build.test.classes.dir}"/>
      <arg line="-d ${build.test.results.dir}"/>
      <arg line="-log 2 -usedefaultlisteners true"/> <!-- show stack trace -->
      <arg line="-sourcedir ${test.src.dir}"/> <!-- for jdk1.4 annotations -->
      <arg line="-suitename ${testng.suitename}"/>
      <arg line="-testname ${testng.suitename}"/>
      <arg line="${testng.classnames.args}"/>
    </java>

    <echo level="info">View test results in ${build.test.results.dir}/index.html</echo>
    <echo level="info"/>

    <condition property="tests.failed">
      <not><equals arg1="0" arg2="${testng.result.code}"/></not>
    </condition>
  </target>

  <!-- TESTNG-DEBUG-SINGLE -->

  <!-- Run selected test.class with TestNG in the debugger.
       User may set property testng.debug.groups to restrict TestNG run to
       specific annotated groups. -->
  <target name="-testng-debug-single">
    <condition property="testng.debug.groups.nonempty">
      <and>
        <isset property="testng.debug.groups"/>
        <not><equals arg1="${testng.debug.groups}" arg2="" trim="true"/></not>
      </and>
    </condition>

    <!-- only one of these will run depending on testng.debug.groups -->
    <antcall target="-testng-debug-single-nogroups"/>
    <antcall target="-testng-debug-single-groups"/>
  </target>
  
  <target name="-testng-debug-single-nogroups"
          unless="testng.debug.groups.nonempty">
    <fail unless="test.class">Must select one file in the IDE or set test.class</fail>

    <!-- (future work: pass resultProperty, and set tests.failed if nonzero) -->
    <j3:debug xmlns:j3="http://www.netbeans.org/ns/j2se-project/3"
              classname="org.testng.TestNG" classpath="${debug.test.classpath}">
      <customize>
        <jvmarg line="-enableassertions"/> <!-- TestNG tests often assert -->
        <jvmarg value="-Dtestng.test.classpath=${build.test.classes.dir}"/>
        <arg line="-d ${build.test.results.dir}"/>
        <arg line="-log 2 -usedefaultlisteners true"/> <!-- show stack trace -->
        <arg line="-sourcedir ${test.src.dir}"/> <!-- for jdk1.4 annotations -->
        <arg line="-suitename ${test.class}"/>
        <arg line="-testname ${test.class}"/>
        <arg line="-testclass ${test.class}"/>
      </customize>
    </j3:debug>
  </target>

  <target name="-testng-debug-single-groups"
          if="testng.debug.groups.nonempty">
    <fail unless="test.class">Must select one file in the IDE or set test.class</fail>

    <!-- For suite name, convert comma separated list of groups (not files)
         to '_' separated -->
    <pathconvert property="testng.debug.groups.name" pathsep="_">
      <path><filelist dir="." files="${testng.debug.groups}"/></path>
      <map from="${basedir}${file.separator}" to=""/>
    </pathconvert>

    <!-- (future work: pass resultProperty, and set tests.failed if nonzero) -->
    <j3:debug xmlns:j3="http://www.netbeans.org/ns/j2se-project/3"
              classname="org.testng.TestNG" classpath="${debug.test.classpath}">
      <customize>
        <jvmarg line="-enableassertions"/> <!-- TestNG tests often assert -->
        <jvmarg value="-Dtestng.test.classpath=${build.test.classes.dir}"/>
        <arg line="-d ${build.test.results.dir}"/>
        <arg line="-log 2 -usedefaultlisteners true"/> <!-- show stack trace -->
        <arg line="-sourcedir ${test.src.dir}"/> <!-- for jdk1.4 annotations -->
        <arg line="-suitename ${test.class}__${testng.debug.groups.name}"/>
        <arg line="-testname ${test.class}__${testng.debug.groups.name}"/>
        <arg line="-testclass ${test.class}"/>
        <arg line="-groups ${testng.debug.groups}"/>
      </customize>
    </j3:debug>
  </target>

</project>
