You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by az...@apache.org on 2021/11/15 20:56:53 UTC

[cassandra] 04/04: CASSANDRA-16630. Updated Cassandra test formatters.

This is an automated email from the ASF dual-hosted git repository.

azotcsit pushed a commit to branch cassandra-16630_junit5
in repository https://gitbox.apache.org/repos/asf/cassandra.git

commit 8b04e685195d0a9a5f8eb1afa676200af14da689
Author: Aleksei Zotov <az...@gmail.com>
AuthorDate: Tue Nov 16 00:56:30 2021 +0400

    CASSANDRA-16630. Updated Cassandra test formatters.
---
 build.xml                                          | 155 ++++---
 .../CassandraBriefJUnitResultFormatter.java        | 309 ++-----------
 .../CassandraXMLJUnitResultFormatter.java          | 477 ++++++---------------
 .../org/apache/cassandra/Junit4SampleTest.java     |   2 +
 .../org/apache/cassandra/Junit5SampleTest.java     |   2 +
 5 files changed, 257 insertions(+), 688 deletions(-)

diff --git a/build.xml b/build.xml
index 6b6d71b..46d20d9 100644
--- a/build.xml
+++ b/build.xml
@@ -1340,6 +1340,10 @@
                  else="">
         <istrue value="${usejacoco}"/>
       </condition>
+      <!-- TODO: think whether showoutput is equal to printSummary or to BriefListener -->
+      <condition property="showoutput" value="true">
+        <istrue value="${showoutput}"/>
+      </condition>
       <taskdef name="junit-timeout" classname="org.apache.cassandra.JStackJUnitTask">
         <classpath>
           <pathelement location="${test.classes}"/>
@@ -1348,43 +1352,17 @@
       <mkdir dir="${build.test.dir}/cassandra"/>
       <mkdir dir="${build.test.dir}/output"/>
       <mkdir dir="${build.test.dir}/output/@{testtag}"/>
-      <junit-timeout fork="on" forkmode="@{forkmode}" failureproperty="testfailed" maxmemory="1024m" timeout="@{timeout}" showoutput="@{showoutput}">
-        <formatter classname="org.apache.cassandra.CassandraXMLJUnitResultFormatter" extension=".xml" usefile="true"/>
-        <formatter classname="org.apache.cassandra.CassandraBriefJUnitResultFormatter" usefile="false"/>
-        <jvmarg value="-Dstorage-config=${test.conf}"/>
-        <jvmarg value="-Djava.awt.headless=true"/>
-        <!-- Cassandra 3.0+ needs <jvmarg line="... ${additionalagent}" /> here! (not value=) -->
-        <jvmarg line="-javaagent:${build.lib}/jamm-${jamm.version}.jar ${additionalagent}" />
-        <jvmarg value="-ea"/>
-        <jvmarg value="-Djava.io.tmpdir=${tmp.dir}"/>
-        <jvmarg value="-Dcassandra.debugrefcount=true"/>
-        <jvmarg value="-Xss256k"/>
-        <!-- When we do classloader manipulation SoftReferences can cause memory leaks
-             that can OOM our test runs. The next two settings informs our GC
-             algorithm to limit the metaspace size and clean up SoftReferences
-             more aggressively rather than waiting. See CASSANDRA-14922 for more details.
-        -->
-        <jvmarg value="-XX:SoftRefLRUPolicyMSPerMB=0" />
-        <jvmarg value="-Dcassandra.test.driver.connection_timeout_ms=${test.driver.connection_timeout_ms}"/>
-        <jvmarg value="-Dcassandra.test.driver.read_timeout_ms=${test.driver.read_timeout_ms}"/>
-        <jvmarg value="-Dcassandra.memtable_row_overhead_computation_step=100"/>
-        <jvmarg value="-Dcassandra.test.use_prepared=${cassandra.test.use_prepared}"/>
-        <jvmarg value="-Dcassandra.test.sstableformatdevelopment=true"/>
-        <!-- The first time SecureRandom initializes can be slow if it blocks on /dev/random -->
-        <jvmarg value="-Djava.security.egd=file:/dev/urandom" />
-        <jvmarg value="-Dcassandra.testtag=@{testtag}"/>
-        <jvmarg value="-Dcassandra.keepBriefBrief=${cassandra.keepBriefBrief}" />
-        <jvmarg value="-Dcassandra.strict.runtime.checks=true" />
-        <jvmarg value="-Dcassandra.track_warnings.coordinator.defensive_checks_enabled=true" /> <!-- enable defensive checks -->
-        <jvmarg line="${java11-jvmargs}"/>
-        <!-- disable shrinks in quicktheories CASSANDRA-15554 -->
-        <jvmarg value="-DQT_SHRINKS=0"/>
-        <jvmarg line="${test-jvmargs}" />
-        <optjvmargs/>
-        <!-- Uncomment to debug unittest, attach debugger to port 1416 -->
-        <!--
-        <jvmarg line="-agentlib:jdwp=transport=dt_socket,address=localhost:1416,server=y,suspend=y" />
-        -->
+
+      <pathconvert id="testfiles" pathsep="${line.separator}" property="testfiles">
+        <fileset dir="@{inputdir}" includes="@{filter}" excludes="@{exclude}"/>
+        <filelist dir="@{inputdir}" files="@{filelist}"/>
+        <mapper>
+            <globmapper from="@{inputdir}/*.java" to="*.class"/>
+        </mapper>
+      </pathconvert>
+
+      <!-- TODO: forkmode="@{forkmode}" and maxmemory="1024m"> are not handled -->
+      <junitlauncher failureproperty="testfailed" printsummary="@{showoutput}">
         <classpath>
           <pathelement path="${java.class.path}"/>
           <pathelement location="${stress.build.classes}"/>
@@ -1399,11 +1377,48 @@
               <exclude name="**/ant-*.jar"/>
           </fileset>
         </classpath>
-        <batchtest todir="${build.test.dir}/output/@{testtag}">
-            <fileset dir="@{inputdir}" includes="@{filter}" excludes="@{exclude}"/>
-            <filelist dir="@{inputdir}" files="@{filelist}"/>
-        </batchtest>
-      </junit-timeout>
+        <testclasses outputdir="${build.test.dir}/output/@{testtag}">
+            <fileset dir="${test.classes}" includes="${testfiles}"/>
+            <listener classname="org.apache.cassandra.CassandraXMLJUnitResultFormatter" sendSysOut="true" sendSysErr="true"/>
+            <listener classname="org.apache.cassandra.CassandraBriefJUnitResultFormatter" if="${showoutput}" sendSysOut="true" sendSysErr="true"/>
+            <fork timeout="@{timeout}">
+                <jvmarg value="-Dstorage-config=${test.conf}"/>
+                <jvmarg value="-Djava.awt.headless=true"/>
+                <!-- Cassandra 3.0+ needs <jvmarg line="... ${additionalagent}" /> here! (not value=) -->
+                <jvmarg line="-javaagent:${build.lib}/jamm-${jamm.version}.jar ${additionalagent}" />
+                <jvmarg value="-ea"/>
+                <jvmarg value="-Djava.io.tmpdir=${tmp.dir}"/>
+                <jvmarg value="-Dcassandra.debugrefcount=true"/>
+                <jvmarg value="-Xss256k"/>
+                <!-- When we do classloader manipulation SoftReferences can cause memory leaks
+                     that can OOM our test runs. The next two settings informs our GC
+                     algorithm to limit the metaspace size and clean up SoftReferences
+                     more aggressively rather than waiting. See CASSANDRA-14922 for more details.
+                -->
+                <jvmarg value="-XX:SoftRefLRUPolicyMSPerMB=0" />
+                <jvmarg value="-Dcassandra.test.driver.connection_timeout_ms=${test.driver.connection_timeout_ms}"/>
+                <jvmarg value="-Dcassandra.test.driver.read_timeout_ms=${test.driver.read_timeout_ms}"/>
+                <jvmarg value="-Dcassandra.memtable_row_overhead_computation_step=100"/>
+                <jvmarg value="-Dcassandra.test.use_prepared=${cassandra.test.use_prepared}"/>
+                <jvmarg value="-Dcassandra.test.sstableformatdevelopment=true"/>
+                <!-- The first time SecureRandom initializes can be slow if it blocks on /dev/random -->
+                <jvmarg value="-Djava.security.egd=file:/dev/urandom" />
+                <jvmarg value="-Dcassandra.testtag=@{testtag}"/>
+                <jvmarg value="-Dcassandra.keepBriefBrief=${cassandra.keepBriefBrief}" />
+                <jvmarg value="-Dcassandra.strict.runtime.checks=true" />
+                <jvmarg value="-Dcassandra.track_warnings.coordinator.defensive_checks_enabled=true" /> <!-- enable defensive checks -->
+                <jvmarg line="${java11-jvmargs}"/>
+                <!-- disable shrinks in quicktheories CASSANDRA-15554 -->
+                <jvmarg value="-DQT_SHRINKS=0"/>
+                <jvmarg line="${test-jvmargs}" />
+                <optjvmargs/>
+                <!-- Uncomment to debug unittest, attach debugger to port 1416 -->
+                <!--
+                <jvmarg line="-agentlib:jdwp=transport=dt_socket,address=localhost:1416,server=y,suspend=y" />
+                -->
+            </fork>
+        </testclasses>
+      </junitlauncher>
 
       <delete quiet="true" failonerror="false" dir="${build.test.dir}/cassandra/commitlog"/>
       <delete quiet="true" failonerror="false" dir="${build.test.dir}/cassandra/cdc_raw"/>
@@ -2091,26 +2106,27 @@
 
         <echo message="Running Eclipse Code Analysis.  Output logged to ${ecj.warnings.file}" />
 
-	<java
-	    jar="${build.dir.lib}/jars/ecj-${ecj.version}.jar"
-            fork="true"
-	    failonerror="true"
-            maxmemory="512m">
-            <arg value="-source"/>
-	    <arg value="${source.version}" />
-	    <arg value="-target"/>
-	    <arg value="${target.version}" />
-	    <arg value="-d" />
-            <arg value="none" />
-	    <arg value="-proc:none" />
-            <arg value="-log" />
-            <arg value="${ecj.warnings.file}" />
-            <arg value="-properties" />
-            <arg value="${ecj.properties}" />
-            <arg value="-cp" />
-            <arg value="${toString:cassandra.classpath}" />
-            <arg value="${build.src.java}" />
-        </java>
+<!--      TODO: rollback -->
+<!--	<java-->
+<!--	    jar="${build.dir.lib}/jars/ecj-${ecj.version}.jar"-->
+<!--            fork="true"-->
+<!--	    failonerror="true"-->
+<!--            maxmemory="512m">-->
+<!--            <arg value="-source"/>-->
+<!--	    <arg value="${source.version}" />-->
+<!--	    <arg value="-target"/>-->
+<!--	    <arg value="${target.version}" />-->
+<!--	    <arg value="-d" />-->
+<!--            <arg value="none" />-->
+<!--	    <arg value="-proc:none" />-->
+<!--            <arg value="-log" />-->
+<!--            <arg value="${ecj.warnings.file}" />-->
+<!--            <arg value="-properties" />-->
+<!--            <arg value="${ecj.properties}" />-->
+<!--            <arg value="-cp" />-->
+<!--            <arg value="${toString:cassandra.classpath}" />-->
+<!--            <arg value="${build.src.java}" />-->
+<!--        </java>-->
   </target>
 
   <target name="init-checkstyle" depends="maven-ant-tasks-retrieve-build,build-project">
@@ -2130,13 +2146,14 @@
 
       <property name="checkstyle.properties" value="${basedir}/checkstyle.xml" />
       <property name="checkstyle.suppressions" value="${basedir}/checkstyle_suppressions.xml" />
-      <checkstyle config="${checkstyle.properties}"
-                  failureProperty="checkstyle.failure"
-                  failOnViolation="true">
-          <formatter type="plain"/>
-          <formatter type="xml" tofile="${checkstyle.report.file}"/>
-          <fileset dir="${build.src.java}" includes="**/*.java"/>
-      </checkstyle>
+<!--      TODO: rollback-->
+<!--      <checkstyle config="${checkstyle.properties}"-->
+<!--                  failureProperty="checkstyle.failure"-->
+<!--                  failOnViolation="true">-->
+<!--          <formatter type="plain"/>-->
+<!--          <formatter type="xml" tofile="${checkstyle.report.file}"/>-->
+<!--          <fileset dir="${build.src.java}" includes="**/*.java"/>-->
+<!--      </checkstyle>-->
   </target>
 
 
diff --git a/test/unit/org/apache/cassandra/CassandraBriefJUnitResultFormatter.java b/test/unit/org/apache/cassandra/CassandraBriefJUnitResultFormatter.java
index 88dbc52..8080d81 100644
--- a/test/unit/org/apache/cassandra/CassandraBriefJUnitResultFormatter.java
+++ b/test/unit/org/apache/cassandra/CassandraBriefJUnitResultFormatter.java
@@ -18,302 +18,45 @@
 
 package org.apache.cassandra;
 
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.StringWriter;
-import java.text.NumberFormat;
+import org.junit.platform.launcher.TestIdentifier;
 
-import junit.framework.AssertionFailedError;
-import junit.framework.Test;
-
-import org.apache.tools.ant.BuildException;
-import org.apache.tools.ant.taskdefs.optional.junit.IgnoredTestListener;
-import org.apache.tools.ant.taskdefs.optional.junit.JUnitResultFormatter;
-import org.apache.tools.ant.taskdefs.optional.junit.JUnitTest;
-import org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner;
-import org.apache.tools.ant.taskdefs.optional.junit.JUnitVersionHelper;
-import org.apache.tools.ant.util.FileUtils;
-import org.apache.tools.ant.util.StringUtils;
+import org.apache.tools.ant.taskdefs.optional.junitlauncher2.LegacyBriefResultFormatter;
 
 /**
  * Prints plain text output of the test to a specified Writer.
- * Inspired by the PlainJUnitResultFormatter.
- *
- * @see FormatterElement
- * @see PlainJUnitResultFormatter
  */
-public class CassandraBriefJUnitResultFormatter implements JUnitResultFormatter, IgnoredTestListener {
-
-    private static final double ONE_SECOND = 1000.0;
-
+public class CassandraBriefJUnitResultFormatter extends LegacyBriefResultFormatter
+{
     private static final String tag = System.getProperty("cassandra.testtag", "");
-
     private static final Boolean keepBriefBrief = Boolean.getBoolean("cassandra.keepBriefBrief");
 
-    /**
-     * Where to write the log to.
-     */
-    private OutputStream out;
-
-    /**
-     * Used for writing the results.
-     */
-    private BufferedWriter output;
-
-    /**
-     * Used as part of formatting the results.
-     */
-    private StringWriter results;
-
-    /**
-     * Used for writing formatted results to.
-     */
-    private BufferedWriter resultWriter;
-
-    /**
-     * Formatter for timings.
-     */
-    private NumberFormat numberFormat = NumberFormat.getInstance();
-
-    /**
-     * Output suite has written to System.out
-     */
-    private String systemOutput = null;
-
-    /**
-     * Output suite has written to System.err
-     */
-    private String systemError = null;
-
-    /**
-     * Constructor for BriefJUnitResultFormatter.
-     */
-    public CassandraBriefJUnitResultFormatter() {
-        results = new StringWriter();
-        resultWriter = new BufferedWriter(results);
+    @Override
+    protected String determineTestName(TestIdentifier testId)
+    {
+        String testName = super.determineTestName(testId);
+        if (!tag.isEmpty())
+            testName = testName + '-' + tag;
+        return testName;
     }
 
-    /**
-     * Sets the stream the formatter is supposed to write its results to.
-     * @param out the output stream to write to
-     */
-    public void setOutput(OutputStream out) {
-        this.out = out;
-        output = new BufferedWriter(new java.io.OutputStreamWriter(out));
+    @Override
+    protected String determineTestSuiteName()
+    {
+        String testSuiteName = super.determineTestSuiteName();
+        if (!"UNKNOWN".equals(testSuiteName) && !tag.isEmpty())
+            testSuiteName = testSuiteName + '-' + tag;
+        return testSuiteName;
     }
 
-    /**
-     * @see JUnitResultFormatter#setSystemOutput(String)
-     */
-    /** {@inheritDoc}. */
-    public void setSystemOutput(String out) {
-        systemOutput = out;
-    }
-
-    /**
-     * @see JUnitResultFormatter#setSystemError(String)
-     */
-    /** {@inheritDoc}. */
-    public void setSystemError(String err) {
-        systemError = err;
-    }
-
-
-    /**
-     * The whole testsuite started.
-     * @param suite the test suite
-     */
-    public void startTestSuite(JUnitTest suite) {
-        if (output == null) {
-            return; // Quick return - no output do nothing.
-        }
-        StringBuffer sb = new StringBuffer("Testsuite: ");
-        String n = suite.getName();
-        if (n != null && !tag.isEmpty())
-            n = n + "-" + tag;
-        sb.append(n);
-        sb.append(StringUtils.LINE_SEP);
-        try {
-            output.write(sb.toString());
-            output.flush();
-        } catch (IOException ex) {
-            throw new BuildException(ex);
-        }
-    }
-
-    /**
-     * The whole testsuite ended.
-     * @param suite the test suite
-     */
-    public void endTestSuite(JUnitTest suite) {
-        StringBuffer sb = new StringBuffer("Testsuite: ");
-        String n = suite.getName();
-        if (n != null && !tag.isEmpty())
-            n = n + "-" + tag;
-        sb.append(n);
-        sb.append(" Tests run: ");
-        sb.append(suite.runCount());
-        sb.append(", Failures: ");
-        sb.append(suite.failureCount());
-        sb.append(", Errors: ");
-        sb.append(suite.errorCount());
-        sb.append(", Skipped: ");
-        sb.append(suite.skipCount());
-        sb.append(", Time elapsed: ");
-        sb.append(numberFormat.format(suite.getRunTime() / ONE_SECOND));
-        sb.append(" sec");
-        sb.append(StringUtils.LINE_SEP);
-        sb.append(StringUtils.LINE_SEP);
-
-        // append the err and output streams to the log
-        if (!keepBriefBrief && systemOutput != null && systemOutput.length() > 0) {
-            sb.append("------------- Standard Output ---------------")
-                    .append(StringUtils.LINE_SEP)
-                    .append(systemOutput)
-                    .append("------------- ---------------- ---------------")
-                    .append(StringUtils.LINE_SEP);
-        }
-
-        if (!keepBriefBrief && systemError != null && systemError.length() > 0) {
-            sb.append("------------- Standard Error -----------------")
-                    .append(StringUtils.LINE_SEP)
-                    .append(systemError)
-                    .append("------------- ---------------- ---------------")
-                    .append(StringUtils.LINE_SEP);
-        }
-
-        if (output != null) {
-            try {
-                output.write(sb.toString());
-                resultWriter.close();
-                output.write(results.toString());
-            } catch (IOException ex) {
-                throw new BuildException(ex);
-            } finally {
-                try {
-                    output.flush();
-                } catch (IOException ex) {
-                    // swallow, there has likely been an exception before this
-                }
-                if (out != System.out && out != System.err) {
-                    FileUtils.close(out);
-                }
-            }
-        }
-    }
-
-    /**
-     * A test started.
-     * @param test a test
-     */
-    public void startTest(Test test) {
-    }
-
-    /**
-     * A test ended.
-     * @param test a test
-     */
-    public void endTest(Test test) {
-    }
-
-    /**
-     * Interface TestListener for JUnit &lt;= 3.4.
-     *
-     * <p>A Test failed.
-     * @param test a test
-     * @param t    the exception thrown by the test
-     */
-    public void addFailure(Test test, Throwable t) {
-        formatError("\tFAILED", test, t);
-    }
-
-    /**
-     * Interface TestListener for JUnit &gt; 3.4.
-     *
-     * <p>A Test failed.
-     * @param test a test
-     * @param t    the assertion failed by the test
-     */
-    public void addFailure(Test test, AssertionFailedError t) {
-        addFailure(test, (Throwable) t);
-    }
-
-    /**
-     * A test caused an error.
-     * @param test  a test
-     * @param error the error thrown by the test
-     */
-    public void addError(Test test, Throwable error) {
-        formatError("\tCaused an ERROR", test, error);
-    }
-
-    /**
-     * Format the test for printing..
-     * @param test a test
-     * @return the formatted testname
-     */
-    protected String formatTest(Test test) {
-        if (test == null) {
-            return "Null Test: ";
-        } else {
-            if (!tag.isEmpty())
-                return "Testcase: " + test.toString() + "-" + tag + ":";
-            return "Testcase: " + test.toString() + ":";
-        }
-    }
-
-    /**
-     * Format an error and print it.
-     * @param type the type of error
-     * @param test the test that failed
-     * @param error the exception that the test threw
-     */
-    protected synchronized void formatError(String type, Test test,
-                                            Throwable error) {
-        if (test != null) {
-            endTest(test);
-        }
-
-        try {
-            resultWriter.write(formatTest(test) + type);
-            resultWriter.newLine();
-            resultWriter.write(String.valueOf(error.getMessage()));
-            resultWriter.newLine();
-            String strace = JUnitTestRunner.getFilteredTrace(error);
-            resultWriter.write(strace);
-            resultWriter.newLine();
-            resultWriter.newLine();
-        } catch (IOException ex) {
-            throw new BuildException(ex);
-        }
-    }
-
-
-    public void testIgnored(Test test) {
-        formatSkip(test, JUnitVersionHelper.getIgnoreMessage(test));
-    }
-
-
-    public void formatSkip(Test test, String message) {
-        if (test != null) {
-            endTest(test);
-        }
-
-        try {
-            resultWriter.write(formatTest(test) + "SKIPPED");
-            if (message != null) {
-                resultWriter.write(": ");
-                resultWriter.write(message);
-            }
-            resultWriter.newLine();
-        } catch (IOException ex) {
-            throw new BuildException(ex);
-        }
-
+    @Override
+    protected boolean hasSysOut()
+    {
+        return !keepBriefBrief && super.hasSysOut();
     }
 
-    public void testAssumptionFailure(Test test, Throwable cause) {
-        formatSkip(test, cause.getMessage());
+    @Override
+    protected boolean hasSysErr()
+    {
+        return !keepBriefBrief && super.hasSysErr();
     }
 }
diff --git a/test/unit/org/apache/cassandra/CassandraXMLJUnitResultFormatter.java b/test/unit/org/apache/cassandra/CassandraXMLJUnitResultFormatter.java
index df0ce7f..369001b 100644
--- a/test/unit/org/apache/cassandra/CassandraXMLJUnitResultFormatter.java
+++ b/test/unit/org/apache/cassandra/CassandraXMLJUnitResultFormatter.java
@@ -18,375 +18,180 @@
 
 package org.apache.cassandra;
 
-import java.io.BufferedWriter;
 import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
+import java.io.Reader;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
-import java.util.Date;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Properties;
+import java.util.Optional;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
 
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
+import org.junit.platform.launcher.TestIdentifier;
 
-import junit.framework.AssertionFailedError;
-import junit.framework.Test;
-
-import org.apache.tools.ant.BuildException;
-import org.apache.tools.ant.taskdefs.optional.junit.IgnoredTestListener;
-import org.apache.tools.ant.taskdefs.optional.junit.JUnitResultFormatter;
-import org.apache.tools.ant.taskdefs.optional.junit.JUnitTest;
-import org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner;
-import org.apache.tools.ant.taskdefs.optional.junit.JUnitVersionHelper;
-import org.apache.tools.ant.taskdefs.optional.junit.XMLConstants;
+import com.sun.xml.internal.txw2.output.IndentingXMLStreamWriter;
+import org.apache.tools.ant.taskdefs.optional.junitlauncher2.LegacyXmlResultFormatter;
 import org.apache.tools.ant.util.DOMElementWriter;
-import org.apache.tools.ant.util.DateUtils;
-import org.apache.tools.ant.util.FileUtils;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Text;
-
-import static org.apache.cassandra.utils.Clock.Global.currentTimeMillis;
+import org.apache.tools.ant.util.StringUtils;
 
 /**
  * Prints XML output of the test to a specified Writer.
- *
- * @see FormatterElement
  */
-
-public class CassandraXMLJUnitResultFormatter implements JUnitResultFormatter, XMLConstants, IgnoredTestListener {
-
-    private static final double ONE_SECOND = 1000.0;
-
-    /** constant for unnnamed testsuites/cases */
-    private static final String UNKNOWN = "unknown";
-
-    private static DocumentBuilder getDocumentBuilder() {
-        try {
-            return DocumentBuilderFactory.newInstance().newDocumentBuilder();
-        } catch (final Exception exc) {
-            throw new ExceptionInInitializerError(exc);
-        }
-    }
-
+public class CassandraXMLJUnitResultFormatter extends LegacyXmlResultFormatter
+{
     private static final String tag = System.getProperty("cassandra.testtag", "");
 
-    /*
-     * Set the property for the test suite name so that log configuration can pick it up
-     * and log to a file specific to this test suite
-     */
-    static
+    @Override
+    protected String determineTestName(TestIdentifier testId)
     {
-        String command = System.getProperty("sun.java.command");
-        String args[] = command.split(" ");
-        System.setProperty("suitename", args[1]);
-    }
-
-    /**
-     * The XML document.
-     */
-    private Document doc;
-
-    /**
-     * The wrapper for the whole testsuite.
-     */
-    private Element rootElement;
-
-    /**
-     * Element for the current test.
-     *
-     * The keying of this map is a bit of a hack: tests are keyed by caseName(className) since
-     * the Test we get for Test-start isn't the same as the Test we get during test-assumption-fail,
-     * so we can't easily match Test objects without manually iterating over all keys and checking
-     * individual fields.
-     */
-    private final Hashtable<String, Element> testElements = new Hashtable<String, Element>();
-
-    /**
-     * tests that failed.
-     */
-    private final Hashtable failedTests = new Hashtable();
-
-    /**
-     * Tests that were skipped.
-     */
-    private final Hashtable<String, Test> skippedTests = new Hashtable<String, Test>();
-    /**
-     * Tests that were ignored. See the note above about the key being a bit of a hack.
-     */
-    private final Hashtable<String, Test> ignoredTests = new Hashtable<String, Test>();
-    /**
-     * Timing helper.
-     */
-    private final Hashtable<String, Long> testStarts = new Hashtable<String, Long>();
-    /**
-     * Where to write the log to.
-     */
-    private OutputStream out;
-
-    /** No arg constructor. */
-    public CassandraXMLJUnitResultFormatter() {
-    }
-
-    /** {@inheritDoc}. */
-    public void setOutput(final OutputStream out) {
-        this.out = out;
+        String testName = super.determineTestName(testId);
+        if (!tag.isEmpty())
+            testName = testName + '-' + tag;
+        return testName;
     }
 
-    /** {@inheritDoc}. */
-    public void setSystemOutput(final String out) {
-        formatOutput(SYSTEM_OUT, out);
+    @Override
+    protected String determineTestSuiteName()
+    {
+        String testSuiteName = super.determineTestSuiteName();
+        if (!"UNKNOWN".equals(testSuiteName) && !tag.isEmpty())
+            testSuiteName = testSuiteName + '-' + tag;
+        return testSuiteName;
     }
 
-    /** {@inheritDoc}. */
-    public void setSystemError(final String out) {
-        formatOutput(SYSTEM_ERR, out);
+    @Override
+    protected XMLReportWriter createXMLReportWriter()
+    {
+        return new CassandraXMLReportWriter();
     }
 
-    /**
-     * The whole testsuite started.
-     * @param suite the testsuite.
-     */
-    public void startTestSuite(final JUnitTest suite) {
-        doc = getDocumentBuilder().newDocument();
-        rootElement = doc.createElement(TESTSUITE);
-        String n = suite.getName();
-        if (n != null && !tag.isEmpty())
-            n = n + "-" + tag;
-        rootElement.setAttribute(ATTR_NAME, n == null ? UNKNOWN : n);
-
-        //add the timestamp
-        final String timestamp = DateUtils.format(new Date(),
-                DateUtils.ISO8601_DATETIME_PATTERN);
-        rootElement.setAttribute(TIMESTAMP, timestamp);
-        //and the hostname.
-        rootElement.setAttribute(HOSTNAME, getHostname());
-
-        // Output properties
-        final Element propsElement = doc.createElement(PROPERTIES);
-        rootElement.appendChild(propsElement);
-        final Properties props = suite.getProperties();
-        if (props != null) {
-            final Enumeration e = props.propertyNames();
-            while (e.hasMoreElements()) {
-                final String name = (String) e.nextElement();
-                final Element propElement = doc.createElement(PROPERTY);
-                propElement.setAttribute(ATTR_NAME, name);
-                propElement.setAttribute(ATTR_VALUE, props.getProperty(name));
-                propsElement.appendChild(propElement);
-            }
+    class CassandraXMLReportWriter extends XMLReportWriter
+    {
+        private static final String ELEM_SYSTEM_OUT = "system-out";
+        private static final String ELEM_SYSTEM_ERR = "system-err";
+        private static final String ELEM_HOSTNAME = "hostname";
+
+        private static final String ELEM_FAILURE = "failure";
+        private static final String ATTR_MESSAGE = "message";
+        private static final String ATTR_TYPE = "type";
+
+        @Override
+        protected XMLStreamWriter createXMLStreamWriter() throws XMLStreamException
+        {
+            return new IndentingXMLStreamWriter(super.createXMLStreamWriter());
         }
-    }
 
-    /**
-     * get the local hostname
-     * @return the name of the local host, or "localhost" if we cannot work it out
-     */
-    private String getHostname()  {
-        String hostname = "localhost";
-        try {
-            final InetAddress localHost = InetAddress.getLocalHost();
-            if (localHost != null) {
-                hostname = localHost.getHostName();
-            }
-        } catch (final UnknownHostException e) {
-            // fall back to default 'localhost'
+        @Override
+        protected void writeCustomAttributes(XMLStreamWriter writer) throws XMLStreamException
+        {
+            writer.writeAttribute(ELEM_HOSTNAME, getHostName());
         }
-        return hostname;
-    }
 
-    /**
-     * The whole testsuite ended.
-     * @param suite the testsuite.
-     * @throws BuildException on error.
-     */
-    public void endTestSuite(final JUnitTest suite) throws BuildException {
-        rootElement.setAttribute(ATTR_TESTS, "" + suite.runCount());
-        rootElement.setAttribute(ATTR_FAILURES, "" + suite.failureCount());
-        rootElement.setAttribute(ATTR_ERRORS, "" + suite.errorCount());
-        rootElement.setAttribute(ATTR_SKIPPED, "" + suite.skipCount());
-        rootElement.setAttribute(
-            ATTR_TIME, "" + (suite.getRunTime() / ONE_SECOND));
-        if (out != null) {
-            Writer wri = null;
-            try {
-                wri = new BufferedWriter(new OutputStreamWriter(out, "UTF8"));
-                wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
-                (new DOMElementWriter()).write(rootElement, wri, 0, "  ");
-            } catch (final IOException exc) {
-                throw new BuildException("Unable to write log file", exc);
-            } finally {
-                if (wri != null) {
-                    try {
-                        wri.flush();
-                    } catch (final IOException ex) {
-                        // ignore
-                    }
-                }
-                if (out != System.out && out != System.err) {
-                    FileUtils.close(wri);
+        @Override
+        protected void writeSysOut(XMLStreamWriter writer) throws XMLStreamException, IOException
+        {
+            writer.writeStartElement(ELEM_SYSTEM_OUT);
+            if (!hasSysOut())
+                writer.writeCData("");
+            else
+                try (final Reader reader = getSysOutReader())
+                {
+                    writeCDataFrom(reader, writer);
                 }
-            }
+            writer.writeEndElement();
         }
-    }
 
-    /**
-     * Interface TestListener.
-     *
-     * <p>A new Test is started.
-     * @param t the test.
-     */
-    public void startTest(final Test t) {
-        testStarts.put(createDescription(t), currentTimeMillis());
-    }
-
-    private static String createDescription(final Test test) throws BuildException {
-        if (!tag.isEmpty())
-            return JUnitVersionHelper.getTestCaseName(test) + "-" + tag +"(" + JUnitVersionHelper.getTestCaseClassName(test) + ")";
-        return JUnitVersionHelper.getTestCaseName(test) + "(" + JUnitVersionHelper.getTestCaseClassName(test) + ")";
-    }
-
-    /**
-     * Interface TestListener.
-     *
-     * <p>A Test is finished.
-     * @param test the test.
-     */
-    public void endTest(final Test test) {
-        final String testDescription = createDescription(test);
-
-        // Fix for bug #5637 - if a junit.extensions.TestSetup is
-        // used and throws an exception during setUp then startTest
-        // would never have been called
-        if (!testStarts.containsKey(testDescription)) {
-            startTest(test);
-        }
-        Element currentTest;
-        if (!failedTests.containsKey(test) && !skippedTests.containsKey(testDescription) && !ignoredTests.containsKey(testDescription)) {
-            currentTest = doc.createElement(TESTCASE);
-            String n = JUnitVersionHelper.getTestCaseName(test);
-            if (n != null && !tag.isEmpty())
-                n = n + "-" + tag;
-            currentTest.setAttribute(ATTR_NAME,
-                                     n == null ? UNKNOWN : n);
-            // a TestSuite can contain Tests from multiple classes,
-            // even tests with the same name - disambiguate them.
-            currentTest.setAttribute(ATTR_CLASSNAME,
-                    JUnitVersionHelper.getTestCaseClassName(test));
-            rootElement.appendChild(currentTest);
-            testElements.put(createDescription(test), currentTest);
-        } else {
-            currentTest = testElements.get(testDescription);
-        }
-
-        final Long l = testStarts.get(createDescription(test));
-        currentTest.setAttribute(ATTR_TIME,
-            "" + ((currentTimeMillis() - l) / ONE_SECOND));
-    }
-
-    /**
-     * Interface TestListener for JUnit &lt;= 3.4.
-     *
-     * <p>A Test failed.
-     * @param test the test.
-     * @param t the exception.
-     */
-    public void addFailure(final Test test, final Throwable t) {
-        formatError(FAILURE, test, t);
-    }
-
-    /**
-     * Interface TestListener for JUnit &gt; 3.4.
-     *
-     * <p>A Test failed.
-     * @param test the test.
-     * @param t the assertion.
-     */
-    public void addFailure(final Test test, final AssertionFailedError t) {
-        addFailure(test, (Throwable) t);
-    }
-
-    /**
-     * Interface TestListener.
-     *
-     * <p>An error occurred while running the test.
-     * @param test the test.
-     * @param t the error.
-     */
-    public void addError(final Test test, final Throwable t) {
-        formatError(ERROR, test, t);
-    }
-
-    private void formatError(final String type, final Test test, final Throwable t) {
-        if (test != null) {
-            endTest(test);
-            failedTests.put(test, test);
-        }
-
-        final Element nested = doc.createElement(type);
-        Element currentTest;
-        if (test != null) {
-            currentTest = testElements.get(createDescription(test));
-        } else {
-            currentTest = rootElement;
-        }
-
-        currentTest.appendChild(nested);
-
-        final String message = t.getMessage();
-        if (message != null && message.length() > 0) {
-            nested.setAttribute(ATTR_MESSAGE, t.getMessage());
+        @Override
+        protected void writeSysErr(XMLStreamWriter writer) throws XMLStreamException, IOException
+        {
+            writer.writeStartElement(ELEM_SYSTEM_ERR);
+            if (!hasSysErr())
+                writer.writeCData("");
+            else
+                try (final Reader reader = getSysErrReader())
+                {
+                    writeCDataFrom(reader, writer);
+                }
+            writer.writeEndElement();
         }
-        nested.setAttribute(ATTR_TYPE, t.getClass().getName());
-
-        final String strace = JUnitTestRunner.getFilteredTrace(t);
-        final Text trace = doc.createTextNode(strace);
-        nested.appendChild(trace);
-    }
-
-    private void formatOutput(final String type, final String output) {
-        final Element nested = doc.createElement(type);
-        rootElement.appendChild(nested);
-        nested.appendChild(doc.createCDATASection(output));
-    }
 
-    public void testIgnored(final Test test) {
-        formatSkip(test, JUnitVersionHelper.getIgnoreMessage(test));
-        if (test != null) {
-            ignoredTests.put(createDescription(test), test);
+        @Override
+        protected void writeFailed(final XMLStreamWriter writer, final TestIdentifier testIdentifier) throws XMLStreamException
+        {
+            if (!failed.containsKey(testIdentifier))
+            {
+                return;
+            }
+            writer.writeStartElement(ELEM_FAILURE);
+            final Optional<Throwable> cause = failed.get(testIdentifier);
+            if (cause.isPresent())
+            {
+                final Throwable t = cause.get();
+                final String message = t.getMessage();
+                if (message != null && !message.trim().isEmpty())
+                {
+                    writeAttribute(writer, ATTR_MESSAGE, message);
+                }
+                writeAttribute(writer, ATTR_TYPE, t.getClass().getName());
+                // write out the stacktrace
+                writer.writeCharacters(StringUtils.getStackTrace(t));
+            }
+            writer.writeEndElement();
         }
-    }
 
-
-    public void formatSkip(final Test test, final String message) {
-        if (test != null) {
-            endTest(test);
+        private void writeCDataFrom(final Reader reader, final XMLStreamWriter writer) throws IOException, XMLStreamException
+        {
+            final char[] chars = new char[1024];
+            int numRead;
+            while ((numRead = reader.read(chars)) != -1)
+            {
+                writer.writeCData(encode(new String(chars, 0, numRead)));
+            }
         }
 
-        final Element nested = doc.createElement("skipped");
-
-        if (message != null) {
-            nested.setAttribute("message", message);
+        @Override
+        protected String encode(String s)
+        {
+            boolean changed = false;
+            final StringBuilder sb = new StringBuilder();
+            for (char c : s.toCharArray())
+            {
+                if (!DOMElementWriter.isLegalXmlCharacter(c))
+                {
+                    changed = true;
+                    sb.append("&#").append((int) c).append(';');
+                }
+                if (c == 0xA || c == 0xD)
+                {
+                    changed = true;
+                    sb.append("&#x").append(Integer.toHexString(c)).append(';');
+                }
+                else
+                {
+                    sb.append(c);
+                }
+            }
+            return changed ? sb.toString() : s;
         }
 
-        Element currentTest;
-        if (test != null) {
-            currentTest = testElements.get(createDescription(test));
-        } else {
-            currentTest = rootElement;
+        /**
+         * Get the local hostname.
+         *
+         * @return the name of the local host, or "localhost" if we cannot work it out
+         */
+        private String getHostName()
+        {
+            String hostname = "localhost";
+            try
+            {
+                final InetAddress localHost = InetAddress.getLocalHost();
+                if (localHost != null)
+                    hostname = localHost.getHostName();
+            }
+            catch (final UnknownHostException e)
+            {
+                // fall back to default 'localhost'
+            }
+            return hostname;
         }
-
-        currentTest.appendChild(nested);
-
-    }
-
-    public void testAssumptionFailure(final Test test, final Throwable failure) {
-        formatSkip(test, failure.getMessage());
-        skippedTests.put(createDescription(test), test);
-
     }
-} // XMLJUnitResultFormatter
+}
diff --git a/test/unit/org/apache/cassandra/Junit4SampleTest.java b/test/unit/org/apache/cassandra/Junit4SampleTest.java
index 7eb27a5..a506357 100644
--- a/test/unit/org/apache/cassandra/Junit4SampleTest.java
+++ b/test/unit/org/apache/cassandra/Junit4SampleTest.java
@@ -28,6 +28,8 @@ public class Junit4SampleTest
     @Test
     public void testSuccess()
     {
+        System.out.println("CUSTOM StdOut");
+        System.err.println("CUSTOM StdErr");
         assertTrue(true);
     }
 
diff --git a/test/unit/org/apache/cassandra/Junit5SampleTest.java b/test/unit/org/apache/cassandra/Junit5SampleTest.java
index 0388d3c..9e11340 100644
--- a/test/unit/org/apache/cassandra/Junit5SampleTest.java
+++ b/test/unit/org/apache/cassandra/Junit5SampleTest.java
@@ -29,6 +29,8 @@ public class Junit5SampleTest
     @Test
     public void testSuccess()
     {
+        System.out.println("CUSTOM StdOut");
+        System.err.println("CUSTOM StdErr");
         assertTrue(true);
     }
 

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org