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:49 UTC

[cassandra] branch cassandra-16630_junit5 created (now 8b04e68)

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

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


      at 8b04e68  CASSANDRA-16630. Updated Cassandra test formatters.

This branch includes the following new commits:

     new 667a894  CASSANDRA-16630. Updated dependencies.
     new 56a32b8  CASSANDRA-16630. Copied Ant JUnit classes.
     new 31d8eea  CASSANDRA-16630. Updated Ant JUnit classes.
     new 8b04e68  CASSANDRA-16630. Updated Cassandra test formatters.

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


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


[cassandra] 03/04: CASSANDRA-16630. Updated Ant JUnit classes.

Posted by az...@apache.org.
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 31d8eea6bb9a5cb3829ccfc3de632c989e900409
Author: Aleksei Zotov <az...@gmail.com>
AuthorDate: Tue Nov 16 00:55:45 2021 +0400

    CASSANDRA-16630. Updated Ant JUnit classes.
---
 .../unit/org/apache/cassandra/JStackJUnitTask.java |  25 +---
 .../junitlauncher/LegacyBriefResultFormatter.java  |  34 ------
 .../AbstractJUnitResultFormatter.java              |  90 +++++++++++---
 .../junitlauncher2/LegacyBriefResultFormatter.java |  36 ++++++
 .../LegacyPlainResultFormatter.java                |  52 ++++----
 .../LegacyXmlResultFormatter.java                  | 135 ++++++++-------------
 6 files changed, 185 insertions(+), 187 deletions(-)

diff --git a/test/unit/org/apache/cassandra/JStackJUnitTask.java b/test/unit/org/apache/cassandra/JStackJUnitTask.java
index 69480a6..f8f806c 100644
--- a/test/unit/org/apache/cassandra/JStackJUnitTask.java
+++ b/test/unit/org/apache/cassandra/JStackJUnitTask.java
@@ -22,28 +22,14 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.lang.reflect.Field;
 
-import org.apache.tools.ant.BuildException;
 import org.apache.tools.ant.taskdefs.ExecuteWatchdog;
-import org.apache.tools.ant.taskdefs.optional.junit.JUnitTask;
+import org.apache.tools.ant.taskdefs.optional.junitlauncher.confined.JUnitLauncherTask;
 import org.apache.tools.ant.util.Watchdog;
 
-public class JStackJUnitTask extends JUnitTask
+public class JStackJUnitTask extends JUnitLauncherTask
 {
-    private Integer timeout;
-
-    public JStackJUnitTask() throws Exception
-    {
-    }
-
-    @Override
-    public void setTimeout(Integer timeout)
-    {
-        this.timeout = timeout;
-        super.setTimeout(timeout);
-    }
-
     @Override
-    public ExecuteWatchdog createWatchdog() throws BuildException
+    protected ExecuteWatchdog createExecuteWatchdog(long timeout)
     {
         return new JStackWatchDog(timeout);
     }
@@ -57,11 +43,6 @@ public class JStackJUnitTask extends JUnitTask
             super(timeout);
         }
 
-        public JStackWatchDog(int timeout)
-        {
-            super(timeout);
-        }
-
         @Override
         public synchronized void start(Process process)
         {
diff --git a/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/LegacyBriefResultFormatter.java b/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/LegacyBriefResultFormatter.java
deleted file mode 100644
index 7debbf0..0000000
--- a/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/LegacyBriefResultFormatter.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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
- *
- *      https://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.apache.tools.ant.taskdefs.optional.junitlauncher;
-
-import org.junit.platform.engine.TestExecutionResult;
-import org.junit.platform.launcher.TestIdentifier;
-
-/**
- * A {@link TestResultFormatter} which prints a brief statistic for tests that have
- * failed, aborted or skipped
- */
-class LegacyBriefResultFormatter extends LegacyPlainResultFormatter implements TestResultFormatter {
-
-    @Override
-    protected boolean shouldReportExecutionFinished(final TestIdentifier testIdentifier, final TestExecutionResult testExecutionResult) {
-        final TestExecutionResult.Status resultStatus = testExecutionResult.getStatus();
-        return resultStatus == TestExecutionResult.Status.ABORTED || resultStatus == TestExecutionResult.Status.FAILED;
-    }
-}
diff --git a/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/AbstractJUnitResultFormatter.java b/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher2/AbstractJUnitResultFormatter.java
similarity index 78%
rename from test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/AbstractJUnitResultFormatter.java
rename to test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher2/AbstractJUnitResultFormatter.java
index 221aadb..456a2cd 100644
--- a/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/AbstractJUnitResultFormatter.java
+++ b/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher2/AbstractJUnitResultFormatter.java
@@ -1,23 +1,25 @@
 /*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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
  *
- *      https://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.
+ *     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.apache.tools.ant.taskdefs.optional.junitlauncher;
+package org.apache.tools.ant.taskdefs.optional.junitlauncher2;
 
 import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.optional.junitlauncher.TestExecutionContext;
+import org.apache.tools.ant.taskdefs.optional.junitlauncher.TestResultFormatter;
 import org.apache.tools.ant.util.FileUtils;
 import org.junit.platform.engine.TestSource;
 import org.junit.platform.engine.support.descriptor.ClassSource;
@@ -39,6 +41,7 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
 
 /**
  * Contains some common behaviour that's used by our internal {@link TestResultFormatter}s
@@ -46,11 +49,18 @@ import java.util.Optional;
 abstract class AbstractJUnitResultFormatter implements TestResultFormatter {
 
     protected TestExecutionContext context;
+    protected TestPlan testPlan;
+    protected boolean useLegacyReportingName = true;
 
     private SysOutErrContentStore sysOutStore;
     private SysOutErrContentStore sysErrStore;
 
     @Override
+    public void testPlanExecutionStarted(final TestPlan testPlan) {
+        this.testPlan = testPlan;
+    }
+
+    @Override
     public void sysOutAvailable(final byte[] data) {
         if (this.sysOutStore == null) {
             this.sysOutStore = new SysOutErrContentStore(context, true);
@@ -83,7 +93,7 @@ abstract class AbstractJUnitResultFormatter implements TestResultFormatter {
      * @return Returns true if there's any stdout data, that was generated during the
      * tests, is available for use. Else returns false.
      */
-    boolean hasSysOut() {
+    protected boolean hasSysOut() {
         return this.sysOutStore != null && this.sysOutStore.hasData();
     }
 
@@ -91,7 +101,7 @@ abstract class AbstractJUnitResultFormatter implements TestResultFormatter {
      * @return Returns true if there's any stderr data, that was generated during the
      * tests, is available for use. Else returns false.
      */
-    boolean hasSysErr() {
+    protected boolean hasSysErr() {
         return this.sysErrStore != null && this.sysErrStore.hasData();
     }
 
@@ -102,7 +112,7 @@ abstract class AbstractJUnitResultFormatter implements TestResultFormatter {
      * be called
      * @throws IOException If there's any I/O problem while creating the {@link Reader}
      */
-    Reader getSysOutReader() throws IOException {
+    protected Reader getSysOutReader() throws IOException {
         return this.sysOutStore.getReader();
     }
 
@@ -113,7 +123,7 @@ abstract class AbstractJUnitResultFormatter implements TestResultFormatter {
      * be called
      * @throws IOException If there's any I/O problem while creating the {@link Reader}
      */
-    Reader getSysErrReader() throws IOException {
+    protected Reader getSysErrReader() throws IOException {
         return this.sysErrStore.getReader();
     }
 
@@ -136,7 +146,7 @@ abstract class AbstractJUnitResultFormatter implements TestResultFormatter {
      * @param writer The {@link Writer} to use. Cannot be null.
      * @throws IOException If any I/O problem occurs during writing the data
      */
-    void writeSysErr(final Writer writer) throws IOException {
+    protected void writeSysErr(final Writer writer) throws IOException {
         Objects.requireNonNull(writer, "Writer cannot be null");
         this.writeFrom(this.sysErrStore, writer);
     }
@@ -175,6 +185,16 @@ abstract class AbstractJUnitResultFormatter implements TestResultFormatter {
     }
 
     @Override
+    public void setUseLegacyReportingName(final boolean useLegacyReportingName) {
+        this.useLegacyReportingName = useLegacyReportingName;
+    }
+
+    protected String determineTestName(TestIdentifier testId) {
+        return useLegacyReportingName ? testId.getLegacyReportingName()
+                                      : testId.getDisplayName();
+    }
+
+    @Override
     public void close() throws IOException {
         FileUtils.close(this.sysOutStore);
         FileUtils.close(this.sysErrStore);
@@ -186,6 +206,38 @@ abstract class AbstractJUnitResultFormatter implements TestResultFormatter {
                                                          + AbstractJUnitResultFormatter.this.getClass().getName(), t, Project.MSG_DEBUG));
     }
 
+    protected String determineTestSuiteName() {
+        // this is really a hack to try and match the expectations of the XML report in JUnit4.x
+        // world. In JUnit5, the TestPlan doesn't have a name and a TestPlan (for which this is a
+        // listener) can have numerous tests within it
+        final Set<TestIdentifier> roots = testPlan.getRoots();
+        if (roots.isEmpty()) {
+            return "UNKNOWN";
+        }
+        for (final TestIdentifier root : roots) {
+            final Optional<ClassSource> classSource = findFirstClassSource(root);
+            if (classSource.isPresent()) {
+                return classSource.get().getClassName();
+            }
+        }
+        return "UNKNOWN";
+    }
+
+    protected Optional<ClassSource> findFirstClassSource(final TestIdentifier root) {
+        if (root.getSource().isPresent()) {
+            final TestSource source = root.getSource().get();
+            if (source instanceof ClassSource) {
+                return Optional.of((ClassSource) source);
+            }
+        }
+        for (final TestIdentifier child : testPlan.getChildren(root)) {
+            final Optional<ClassSource> classSource = findFirstClassSource(child);
+            if (classSource.isPresent()) {
+                return classSource;
+            }
+        }
+        return Optional.empty();
+    }
 
     /*
     A "store" for sysout/syserr content that gets sent to the AbstractJUnitResultFormatter.
diff --git a/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher2/LegacyBriefResultFormatter.java b/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher2/LegacyBriefResultFormatter.java
new file mode 100644
index 0000000..fda75f6
--- /dev/null
+++ b/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher2/LegacyBriefResultFormatter.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.tools.ant.taskdefs.optional.junitlauncher2;
+
+import org.junit.platform.engine.TestExecutionResult;
+import org.junit.platform.launcher.TestIdentifier;
+
+import org.apache.tools.ant.taskdefs.optional.junitlauncher.TestResultFormatter;
+
+/**
+ * A {@link TestResultFormatter} which prints a brief statistic for tests that have
+ * failed, aborted or skipped
+ */
+public class LegacyBriefResultFormatter extends org.apache.tools.ant.taskdefs.optional.junitlauncher2.LegacyPlainResultFormatter implements TestResultFormatter {
+
+    @Override
+    protected boolean shouldReportExecutionFinished(final TestIdentifier testIdentifier, final TestExecutionResult testExecutionResult) {
+        final TestExecutionResult.Status resultStatus = testExecutionResult.getStatus();
+        return resultStatus == TestExecutionResult.Status.ABORTED || resultStatus == TestExecutionResult.Status.FAILED;
+    }
+}
diff --git a/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/LegacyPlainResultFormatter.java b/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher2/LegacyPlainResultFormatter.java
similarity index 85%
rename from test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/LegacyPlainResultFormatter.java
rename to test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher2/LegacyPlainResultFormatter.java
index 7583d78..5c2c885 100644
--- a/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/LegacyPlainResultFormatter.java
+++ b/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher2/LegacyPlainResultFormatter.java
@@ -1,22 +1,22 @@
 /*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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
  *
- *      https://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.
+ *     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.apache.tools.ant.taskdefs.optional.junitlauncher;
+package org.apache.tools.ant.taskdefs.optional.junitlauncher2;
 
 import org.junit.platform.engine.TestExecutionResult;
 import org.junit.platform.engine.reporting.ReportEntry;
@@ -37,22 +37,17 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
 
+import org.apache.tools.ant.taskdefs.optional.junitlauncher.TestResultFormatter;
+
 
 /**
  * A {@link TestResultFormatter} which prints a short statistic for each of the tests
  */
-class LegacyPlainResultFormatter extends AbstractJUnitResultFormatter implements TestResultFormatter {
+public class LegacyPlainResultFormatter extends org.apache.tools.ant.taskdefs.optional.junitlauncher2.AbstractJUnitResultFormatter implements TestResultFormatter {
 
     private OutputStream outputStream;
     private final Map<TestIdentifier, Stats> testIds = new ConcurrentHashMap<>();
-    private TestPlan testPlan;
     private BufferedWriter writer;
-    private boolean useLegacyReportingName = true;
-
-    @Override
-    public void testPlanExecutionStarted(final TestPlan testPlan) {
-        this.testPlan = testPlan;
-    }
 
     @Override
     public void testPlanExecutionFinished(final TestPlan testPlan) {
@@ -63,7 +58,9 @@ class LegacyPlainResultFormatter extends AbstractJUnitResultFormatter implements
                 continue;
             }
             final Stats stats = entry.getValue();
-            final StringBuilder sb = new StringBuilder("Tests run: ").append(stats.numTestsRun.get());
+            // TODO: am I really required?
+            final StringBuilder sb = new StringBuilder("Testsuite: ").append(super.determineTestSuiteName());
+            sb.append(", Tests run: ").append(stats.numTestsRun.get());
             sb.append(", Failures: ").append(stats.numTestsFailed.get());
             sb.append(", Skipped: ").append(stats.numTestsSkipped.get());
             sb.append(", Aborted: ").append(stats.numTestsAborted.get());
@@ -112,7 +109,7 @@ class LegacyPlainResultFormatter extends AbstractJUnitResultFormatter implements
         if (testIdentifier.isTest()) {
             final StringBuilder sb = new StringBuilder();
             sb.append("Test: ");
-            sb.append(this.useLegacyReportingName ? testIdentifier.getLegacyReportingName() : testIdentifier.getDisplayName());
+            sb.append(determineTestName(testIdentifier));
             sb.append(" took ");
             stats.appendElapsed(sb);
             sb.append(" SKIPPED");
@@ -176,7 +173,7 @@ class LegacyPlainResultFormatter extends AbstractJUnitResultFormatter implements
         if (testIdentifier.isTest() && shouldReportExecutionFinished(testIdentifier, testExecutionResult)) {
             final StringBuilder sb = new StringBuilder();
             sb.append("Test: ");
-            sb.append(this.useLegacyReportingName ? testIdentifier.getLegacyReportingName() : testIdentifier.getDisplayName());
+            sb.append(determineTestName(testIdentifier));
             if (stats != null) {
                 sb.append(" took ");
                 stats.appendElapsed(sb);
@@ -231,11 +228,6 @@ class LegacyPlainResultFormatter extends AbstractJUnitResultFormatter implements
         this.writer = new BufferedWriter(new OutputStreamWriter(this.outputStream, StandardCharsets.UTF_8));
     }
 
-    @Override
-    public void setUseLegacyReportingName(final boolean useLegacyReportingName) {
-        this.useLegacyReportingName = useLegacyReportingName;
-    }
-
     protected boolean shouldReportExecutionFinished(final TestIdentifier testIdentifier, final TestExecutionResult testExecutionResult) {
         return true;
     }
diff --git a/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/LegacyXmlResultFormatter.java b/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher2/LegacyXmlResultFormatter.java
similarity index 73%
rename from test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/LegacyXmlResultFormatter.java
rename to test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher2/LegacyXmlResultFormatter.java
index bb9a963..47aaa2d 100644
--- a/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/LegacyXmlResultFormatter.java
+++ b/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher2/LegacyXmlResultFormatter.java
@@ -1,22 +1,23 @@
 /*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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
  *
- *      https://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.
+ *     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.apache.tools.ant.taskdefs.optional.junitlauncher;
+package org.apache.tools.ant.taskdefs.optional.junitlauncher2;
 
+import org.apache.tools.ant.taskdefs.optional.junitlauncher.TestResultFormatter;
 import org.apache.tools.ant.util.DOMElementWriter;
 import org.apache.tools.ant.util.DateUtils;
 import org.apache.tools.ant.util.StringUtils;
@@ -37,7 +38,6 @@ import java.util.Date;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Properties;
-import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicLong;
 
@@ -46,25 +46,22 @@ import java.util.concurrent.atomic.AtomicLong;
  * conforms to the schema of the XML that was generated by the {@code junit} task's XML
  * report formatter and can be used by the {@code junitreport} task
  */
-class LegacyXmlResultFormatter extends AbstractJUnitResultFormatter implements TestResultFormatter {
+public class LegacyXmlResultFormatter extends org.apache.tools.ant.taskdefs.optional.junitlauncher2.AbstractJUnitResultFormatter implements TestResultFormatter {
 
     private static final double ONE_SECOND = 1000.0;
 
     private OutputStream outputStream;
     private final Map<TestIdentifier, Stats> testIds = new ConcurrentHashMap<>();
-    private final Map<TestIdentifier, Optional<String>> skipped = new ConcurrentHashMap<>();
-    private final Map<TestIdentifier, Optional<Throwable>> failed = new ConcurrentHashMap<>();
-    private final Map<TestIdentifier, Optional<Throwable>> aborted = new ConcurrentHashMap<>();
+    protected final Map<TestIdentifier, Optional<String>> skipped = new ConcurrentHashMap<>();
+    protected final Map<TestIdentifier, Optional<Throwable>> failed = new ConcurrentHashMap<>();
+    protected final Map<TestIdentifier, Optional<Throwable>> aborted = new ConcurrentHashMap<>();
 
-    private TestPlan testPlan;
     private long testPlanStartedAt = -1;
     private long testPlanEndedAt = -1;
     private final AtomicLong numTestsRun = new AtomicLong(0);
     private final AtomicLong numTestsFailed = new AtomicLong(0);
     private final AtomicLong numTestsSkipped = new AtomicLong(0);
     private final AtomicLong numTestsAborted = new AtomicLong(0);
-    private boolean useLegacyReportingName = true;
-
 
     @Override
     public void testPlanExecutionStarted(final TestPlan testPlan) {
@@ -77,7 +74,7 @@ class LegacyXmlResultFormatter extends AbstractJUnitResultFormatter implements T
         this.testPlanEndedAt = System.currentTimeMillis();
         // format and print out the result
         try {
-            new XMLReportWriter().write();
+            createXMLReportWriter().write();
         } catch (IOException | XMLStreamException e) {
             handleException(e);
         }
@@ -142,9 +139,8 @@ class LegacyXmlResultFormatter extends AbstractJUnitResultFormatter implements T
         this.outputStream = os;
     }
 
-    @Override
-    public void setUseLegacyReportingName(final boolean useLegacyReportingName) {
-        this.useLegacyReportingName = useLegacyReportingName;
+    protected XMLReportWriter createXMLReportWriter() {
+        return new XMLReportWriter();
     }
 
     private final class Stats {
@@ -159,7 +155,7 @@ class LegacyXmlResultFormatter extends AbstractJUnitResultFormatter implements T
         }
     }
 
-    private final class XMLReportWriter {
+    protected class XMLReportWriter {
 
         private static final String ELEM_TESTSUITE = "testsuite";
         private static final String ELEM_PROPERTIES = "properties";
@@ -184,8 +180,8 @@ class LegacyXmlResultFormatter extends AbstractJUnitResultFormatter implements T
         private static final String ATTR_MESSAGE = "message";
         private static final String ATTR_TYPE = "type";
 
-        void write() throws XMLStreamException, IOException {
-            final XMLStreamWriter writer = XMLOutputFactory.newFactory().createXMLStreamWriter(outputStream, "UTF-8");
+        protected void write() throws XMLStreamException, IOException {
+            final XMLStreamWriter writer = createXMLStreamWriter();
             try {
                 writer.writeStartDocument();
                 writeTestSuite(writer);
@@ -195,11 +191,15 @@ class LegacyXmlResultFormatter extends AbstractJUnitResultFormatter implements T
             }
         }
 
-        void writeTestSuite(final XMLStreamWriter writer) throws XMLStreamException, IOException {
+        protected XMLStreamWriter createXMLStreamWriter() throws XMLStreamException {
+            return XMLOutputFactory.newFactory().createXMLStreamWriter(outputStream, "UTF-8");
+        }
+
+        protected void writeTestSuite(final XMLStreamWriter writer) throws XMLStreamException, IOException {
             // write the testsuite element
             writer.writeStartElement(ELEM_TESTSUITE);
-            final String testsuiteName = determineTestSuiteName();
-            writeAttribute(writer, ATTR_NAME, testsuiteName);
+            final String testSuiteName = determineTestSuiteName();
+            writeAttribute(writer, ATTR_NAME, testSuiteName);
             // time taken for the tests execution
             writeAttribute(writer, ATTR_TIME, String.valueOf((testPlanEndedAt - testPlanStartedAt) / ONE_SECOND));
             // add the timestamp of report generation
@@ -210,6 +210,8 @@ class LegacyXmlResultFormatter extends AbstractJUnitResultFormatter implements T
             writeAttribute(writer, ATTR_NUM_SKIPPED, String.valueOf(numTestsSkipped.longValue()));
             writeAttribute(writer, ATTR_NUM_ABORTED, String.valueOf(numTestsAborted.longValue()));
 
+            writeCustomAttributes(writer);
+
             // write the properties
             writeProperties(writer);
             // write the tests
@@ -220,7 +222,11 @@ class LegacyXmlResultFormatter extends AbstractJUnitResultFormatter implements T
             writer.writeEndElement();
         }
 
-        void writeProperties(final XMLStreamWriter writer) throws XMLStreamException {
+        protected void writeCustomAttributes(XMLStreamWriter writer) throws XMLStreamException
+        {
+        }
+
+        protected void writeProperties(final XMLStreamWriter writer) throws XMLStreamException {
             final Properties properties = LegacyXmlResultFormatter.this.context.getProperties();
             if (properties == null || properties.isEmpty()) {
                 return;
@@ -235,7 +241,7 @@ class LegacyXmlResultFormatter extends AbstractJUnitResultFormatter implements T
             writer.writeEndElement();
         }
 
-        void writeTestCase(final XMLStreamWriter writer) throws XMLStreamException {
+        protected void writeTestCase(final XMLStreamWriter writer) throws XMLStreamException {
             for (final Map.Entry<TestIdentifier, Stats> entry : testIds.entrySet()) {
                 final TestIdentifier testId = entry.getKey();
                 if (!testId.isTest() && !failed.containsKey(testId)) {
@@ -258,8 +264,7 @@ class LegacyXmlResultFormatter extends AbstractJUnitResultFormatter implements T
                 final String classname = (parentClassSource.get()).getClassName();
                 writer.writeStartElement(ELEM_TESTCASE);
                 writeAttribute(writer, ATTR_CLASSNAME, classname);
-                writeAttribute(writer, ATTR_NAME, useLegacyReportingName ? testId.getLegacyReportingName()
-                                                                         : testId.getDisplayName());
+                writeAttribute(writer, ATTR_NAME, determineTestName(testId));
                 final Stats stats = entry.getValue();
                 writeAttribute(writer, ATTR_TIME, String.valueOf((stats.endedAt - stats.startedAt) / ONE_SECOND));
                 // skipped element if the test was skipped
@@ -273,7 +278,7 @@ class LegacyXmlResultFormatter extends AbstractJUnitResultFormatter implements T
             }
         }
 
-        private void writeSkipped(final XMLStreamWriter writer, final TestIdentifier testIdentifier) throws XMLStreamException {
+        protected void writeSkipped(final XMLStreamWriter writer, final TestIdentifier testIdentifier) throws XMLStreamException {
             if (!skipped.containsKey(testIdentifier)) {
                 return;
             }
@@ -285,7 +290,7 @@ class LegacyXmlResultFormatter extends AbstractJUnitResultFormatter implements T
             writer.writeEndElement();
         }
 
-        private void writeFailed(final XMLStreamWriter writer, final TestIdentifier testIdentifier) throws XMLStreamException {
+        protected void writeFailed(final XMLStreamWriter writer, final TestIdentifier testIdentifier) throws XMLStreamException {
             if (!failed.containsKey(testIdentifier)) {
                 return;
             }
@@ -304,7 +309,7 @@ class LegacyXmlResultFormatter extends AbstractJUnitResultFormatter implements T
             writer.writeEndElement();
         }
 
-        private void writeAborted(final XMLStreamWriter writer, final TestIdentifier testIdentifier) throws XMLStreamException {
+        protected void writeAborted(final XMLStreamWriter writer, final TestIdentifier testIdentifier) throws XMLStreamException {
             if (!aborted.containsKey(testIdentifier)) {
                 return;
             }
@@ -323,29 +328,29 @@ class LegacyXmlResultFormatter extends AbstractJUnitResultFormatter implements T
             writer.writeEndElement();
         }
 
-        private void writeSysOut(final XMLStreamWriter writer) throws XMLStreamException, IOException {
-            if (!LegacyXmlResultFormatter.this.hasSysOut()) {
+        protected void writeSysOut(final XMLStreamWriter writer) throws XMLStreamException, IOException {
+            if (!hasSysOut()) {
                 return;
             }
             writer.writeStartElement(ELEM_SYSTEM_OUT);
-            try (final Reader reader = LegacyXmlResultFormatter.this.getSysOutReader()) {
+            try (final Reader reader = getSysOutReader()) {
                 writeCharactersFrom(reader, writer);
             }
             writer.writeEndElement();
         }
 
-        private void writeSysErr(final XMLStreamWriter writer) throws XMLStreamException, IOException {
-            if (!LegacyXmlResultFormatter.this.hasSysErr()) {
+        protected void writeSysErr(final XMLStreamWriter writer) throws XMLStreamException, IOException {
+            if (!hasSysErr()) {
                 return;
             }
             writer.writeStartElement(ELEM_SYSTEM_ERR);
-            try (final Reader reader = LegacyXmlResultFormatter.this.getSysErrReader()) {
+            try (final Reader reader = getSysErrReader()) {
                 writeCharactersFrom(reader, writer);
             }
             writer.writeEndElement();
         }
 
-        private void writeCharactersFrom(final Reader reader, final XMLStreamWriter writer) throws IOException, XMLStreamException {
+        protected void writeCharactersFrom(final Reader reader, final XMLStreamWriter writer) throws IOException, XMLStreamException {
             final char[] chars = new char[1024];
             int numRead = -1;
             while ((numRead = reader.read(chars)) != -1) {
@@ -353,12 +358,11 @@ class LegacyXmlResultFormatter extends AbstractJUnitResultFormatter implements T
             }
         }
 
-        private void writeAttribute(final XMLStreamWriter writer, final String name, final String value)
-        throws XMLStreamException {
+        protected void writeAttribute(final XMLStreamWriter writer, final String name, final String value) throws XMLStreamException {
             writer.writeAttribute(name, encode(value));
         }
 
-        private String encode(final String s) {
+        protected String encode(final String s) {
             boolean changed = false;
             final StringBuilder sb = new StringBuilder();
             for (char c : s.toCharArray()) {
@@ -372,39 +376,6 @@ class LegacyXmlResultFormatter extends AbstractJUnitResultFormatter implements T
             return changed ? sb.toString() : s;
         }
 
-        private String determineTestSuiteName() {
-            // this is really a hack to try and match the expectations of the XML report in JUnit4.x
-            // world. In JUnit5, the TestPlan doesn't have a name and a TestPlan (for which this is a
-            // listener) can have numerous tests within it
-            final Set<TestIdentifier> roots = testPlan.getRoots();
-            if (roots.isEmpty()) {
-                return "UNKNOWN";
-            }
-            for (final TestIdentifier root : roots) {
-                final Optional<ClassSource> classSource = findFirstClassSource(root);
-                if (classSource.isPresent()) {
-                    return classSource.get().getClassName();
-                }
-            }
-            return "UNKNOWN";
-        }
-
-        private Optional<ClassSource> findFirstClassSource(final TestIdentifier root) {
-            if (root.getSource().isPresent()) {
-                final TestSource source = root.getSource().get();
-                if (source instanceof ClassSource) {
-                    return Optional.of((ClassSource) source);
-                }
-            }
-            for (final TestIdentifier child : testPlan.getChildren(root)) {
-                final Optional<ClassSource> classSource = findFirstClassSource(child);
-                if (classSource.isPresent()) {
-                    return classSource;
-                }
-            }
-            return Optional.empty();
-        }
-
         private Optional<ClassSource> findFirstParentClassSource(final TestIdentifier testId) {
             final Optional<TestIdentifier> parent = testPlan.getParent(testId);
             if (!parent.isPresent()) {

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


[cassandra] 02/04: CASSANDRA-16630. Copied Ant JUnit classes.

Posted by az...@apache.org.
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 56a32b87b9d91b50d3a228dff6369ea751714435
Author: Aleksei Zotov <az...@gmail.com>
AuthorDate: Sun Nov 14 20:07:36 2021 +0400

    CASSANDRA-16630. Copied Ant JUnit classes.
---
 .../AbstractJUnitResultFormatter.java              | 308 +++++++++++++++
 .../junitlauncher/LegacyBriefResultFormatter.java  |  34 ++
 .../junitlauncher/LegacyPlainResultFormatter.java  | 290 ++++++++++++++
 .../junitlauncher/LegacyXmlResultFormatter.java    | 424 +++++++++++++++++++++
 4 files changed, 1056 insertions(+)

diff --git a/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/AbstractJUnitResultFormatter.java b/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/AbstractJUnitResultFormatter.java
new file mode 100644
index 0000000..221aadb
--- /dev/null
+++ b/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/AbstractJUnitResultFormatter.java
@@ -0,0 +1,308 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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
+ *
+ *      https://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.apache.tools.ant.taskdefs.optional.junitlauncher;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.FileUtils;
+import org.junit.platform.engine.TestSource;
+import org.junit.platform.engine.support.descriptor.ClassSource;
+import org.junit.platform.launcher.TestIdentifier;
+import org.junit.platform.launcher.TestPlan;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.Closeable;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Contains some common behaviour that's used by our internal {@link TestResultFormatter}s
+ */
+abstract class AbstractJUnitResultFormatter implements TestResultFormatter {
+
+    protected TestExecutionContext context;
+
+    private SysOutErrContentStore sysOutStore;
+    private SysOutErrContentStore sysErrStore;
+
+    @Override
+    public void sysOutAvailable(final byte[] data) {
+        if (this.sysOutStore == null) {
+            this.sysOutStore = new SysOutErrContentStore(context, true);
+        }
+        try {
+            this.sysOutStore.store(data);
+        } catch (IOException e) {
+            handleException(e);
+        }
+    }
+
+    @Override
+    public void sysErrAvailable(final byte[] data) {
+        if (this.sysErrStore == null) {
+            this.sysErrStore = new SysOutErrContentStore(context, false);
+        }
+        try {
+            this.sysErrStore.store(data);
+        } catch (IOException e) {
+            handleException(e);
+        }
+    }
+
+    @Override
+    public void setContext(final TestExecutionContext context) {
+        this.context = context;
+    }
+
+    /**
+     * @return Returns true if there's any stdout data, that was generated during the
+     * tests, is available for use. Else returns false.
+     */
+    boolean hasSysOut() {
+        return this.sysOutStore != null && this.sysOutStore.hasData();
+    }
+
+    /**
+     * @return Returns true if there's any stderr data, that was generated during the
+     * tests, is available for use. Else returns false.
+     */
+    boolean hasSysErr() {
+        return this.sysErrStore != null && this.sysErrStore.hasData();
+    }
+
+    /**
+     * @return Returns a {@link Reader} for reading any stdout data that was generated
+     * during the test execution. It is expected that the {@link #hasSysOut()} be first
+     * called to see if any such data is available and only if there is, then this method
+     * be called
+     * @throws IOException If there's any I/O problem while creating the {@link Reader}
+     */
+    Reader getSysOutReader() throws IOException {
+        return this.sysOutStore.getReader();
+    }
+
+    /**
+     * @return Returns a {@link Reader} for reading any stderr data that was generated
+     * during the test execution. It is expected that the {@link #hasSysErr()} be first
+     * called to see if any such data is available and only if there is, then this method
+     * be called
+     * @throws IOException If there's any I/O problem while creating the {@link Reader}
+     */
+    Reader getSysErrReader() throws IOException {
+        return this.sysErrStore.getReader();
+    }
+
+    /**
+     * Writes out any stdout data that was generated during the
+     * test execution. If there was no such data then this method just returns.
+     *
+     * @param writer The {@link Writer} to use. Cannot be null.
+     * @throws IOException If any I/O problem occurs during writing the data
+     */
+    void writeSysOut(final Writer writer) throws IOException {
+        Objects.requireNonNull(writer, "Writer cannot be null");
+        this.writeFrom(this.sysOutStore, writer);
+    }
+
+    /**
+     * Writes out any stderr data that was generated during the
+     * test execution. If there was no such data then this method just returns.
+     *
+     * @param writer The {@link Writer} to use. Cannot be null.
+     * @throws IOException If any I/O problem occurs during writing the data
+     */
+    void writeSysErr(final Writer writer) throws IOException {
+        Objects.requireNonNull(writer, "Writer cannot be null");
+        this.writeFrom(this.sysErrStore, writer);
+    }
+
+    static Optional<TestIdentifier> traverseAndFindTestClass(final TestPlan testPlan, final TestIdentifier testIdentifier) {
+        if (isTestClass(testIdentifier).isPresent()) {
+            return Optional.of(testIdentifier);
+        }
+        final Optional<TestIdentifier> parent = testPlan.getParent(testIdentifier);
+        return parent.isPresent() ? traverseAndFindTestClass(testPlan, parent.get()) : Optional.empty();
+    }
+
+    static Optional<ClassSource> isTestClass(final TestIdentifier testIdentifier) {
+        if (testIdentifier == null) {
+            return Optional.empty();
+        }
+        final Optional<TestSource> source = testIdentifier.getSource();
+        if (!source.isPresent()) {
+            return Optional.empty();
+        }
+        final TestSource testSource = source.get();
+        if (testSource instanceof ClassSource) {
+            return Optional.of((ClassSource) testSource);
+        }
+        return Optional.empty();
+    }
+
+    private void writeFrom(final SysOutErrContentStore store, final Writer writer) throws IOException {
+        final char[] chars = new char[1024];
+        int numRead = -1;
+        try (final Reader reader = store.getReader()) {
+            while ((numRead = reader.read(chars)) != -1) {
+                writer.write(chars, 0, numRead);
+            }
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        FileUtils.close(this.sysOutStore);
+        FileUtils.close(this.sysErrStore);
+    }
+
+    protected void handleException(final Throwable t) {
+        // we currently just log it and move on.
+        this.context.getProject().ifPresent((p) -> p.log("Exception in listener "
+                                                         + AbstractJUnitResultFormatter.this.getClass().getName(), t, Project.MSG_DEBUG));
+    }
+
+
+    /*
+    A "store" for sysout/syserr content that gets sent to the AbstractJUnitResultFormatter.
+    This store first uses a relatively decent sized in-memory buffer for storing the sysout/syserr
+    content. This in-memory buffer will be used as long as it can fit in the new content that
+    keeps coming in. When the size limit is reached, this store switches to a file based store
+    by creating a temporarily file and writing out the already in-memory held buffer content
+    and any new content that keeps arriving to this store. Once the file has been created,
+    the in-memory buffer will never be used any more and in fact is destroyed as soon as the
+    file is created.
+    Instances of this class are not thread-safe and users of this class are expected to use necessary thread
+    safety guarantees, if they want to use an instance of this class by multiple threads.
+    */
+    private static final class SysOutErrContentStore implements Closeable {
+        private static final int DEFAULT_CAPACITY_IN_BYTES = 50 * 1024; // 50 KB
+        private static final Reader EMPTY_READER = new Reader() {
+            @Override
+            public int read(final char[] cbuf, final int off, final int len) throws IOException {
+                return -1;
+            }
+
+            @Override
+            public void close() throws IOException {
+            }
+        };
+
+        private final TestExecutionContext context;
+        private final String tmpFileSuffix;
+        private ByteBuffer inMemoryStore = ByteBuffer.allocate(DEFAULT_CAPACITY_IN_BYTES);
+        private boolean usingFileStore = false;
+        private Path filePath;
+        private OutputStream fileOutputStream;
+
+        private SysOutErrContentStore(final TestExecutionContext context, final boolean isSysOut) {
+            this.context = context;
+            this.tmpFileSuffix = isSysOut ? ".sysout" : ".syserr";
+        }
+
+        private void store(final byte[] data) throws IOException {
+            if (this.usingFileStore) {
+                this.storeToFile(data, 0, data.length);
+                return;
+            }
+            // we haven't yet created a file store and the data can fit in memory,
+            // so we write it in our buffer
+            try {
+                this.inMemoryStore.put(data);
+                return;
+            } catch (BufferOverflowException boe) {
+                // the buffer capacity can't hold this incoming data, so this
+                // incoming data hasn't been transferred to the buffer. let's
+                // now fall back to a file store
+                this.usingFileStore = true;
+            }
+            // since the content couldn't be transferred into in-memory buffer,
+            // we now create a file and transfer already (previously) stored in-memory
+            // content into that file, before finally transferring this new content
+            // into the file too. We then finally discard this in-memory buffer and
+            // just keep using the file store instead
+            this.fileOutputStream = createFileStore();
+            // first the existing in-memory content
+            storeToFile(this.inMemoryStore.array(), 0, this.inMemoryStore.position());
+            storeToFile(data, 0, data.length);
+            // discard the in-memory store
+            this.inMemoryStore = null;
+        }
+
+        private void storeToFile(final byte[] data, final int offset, final int length) throws IOException {
+            if (this.fileOutputStream == null) {
+                // no backing file was created so we can't do anything
+                return;
+            }
+            this.fileOutputStream.write(data, offset, length);
+        }
+
+        private OutputStream createFileStore() throws IOException {
+            this.filePath = FileUtils.getFileUtils()
+                                     .createTempFile(context.getProject().orElse(null), null, this.tmpFileSuffix, null, true, true)
+                                     .toPath();
+            return Files.newOutputStream(this.filePath);
+        }
+
+        /*
+         * Returns a Reader for reading the sysout/syserr content. If there's no data
+         * available in this store, then this returns a Reader which when used for read operations,
+         * will immediately indicate an EOF.
+         */
+        private Reader getReader() throws IOException {
+            if (this.usingFileStore && this.filePath != null) {
+                // we use a FileReader here so that we can use the system default character
+                // encoding for reading the contents on sysout/syserr stream, since that's the
+                // encoding that System.out/System.err uses to write out the messages
+                return new BufferedReader(new FileReader(this.filePath.toFile()));
+            }
+            if (this.inMemoryStore != null) {
+                return new InputStreamReader(new ByteArrayInputStream(this.inMemoryStore.array(), 0, this.inMemoryStore.position()));
+            }
+            // no data to read, so we return an "empty" reader
+            return EMPTY_READER;
+        }
+
+        /*
+         *  Returns true if this store has any data (either in-memory or in a file). Else
+         *  returns false.
+         */
+        private boolean hasData() {
+            if (this.inMemoryStore != null && this.inMemoryStore.position() > 0) {
+                return true;
+            }
+            return this.usingFileStore && this.filePath != null;
+        }
+
+        @Override
+        public void close() throws IOException {
+            this.inMemoryStore = null;
+            FileUtils.close(this.fileOutputStream);
+            FileUtils.delete(this.filePath.toFile());
+        }
+    }
+}
diff --git a/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/LegacyBriefResultFormatter.java b/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/LegacyBriefResultFormatter.java
new file mode 100644
index 0000000..7debbf0
--- /dev/null
+++ b/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/LegacyBriefResultFormatter.java
@@ -0,0 +1,34 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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
+ *
+ *      https://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.apache.tools.ant.taskdefs.optional.junitlauncher;
+
+import org.junit.platform.engine.TestExecutionResult;
+import org.junit.platform.launcher.TestIdentifier;
+
+/**
+ * A {@link TestResultFormatter} which prints a brief statistic for tests that have
+ * failed, aborted or skipped
+ */
+class LegacyBriefResultFormatter extends LegacyPlainResultFormatter implements TestResultFormatter {
+
+    @Override
+    protected boolean shouldReportExecutionFinished(final TestIdentifier testIdentifier, final TestExecutionResult testExecutionResult) {
+        final TestExecutionResult.Status resultStatus = testExecutionResult.getStatus();
+        return resultStatus == TestExecutionResult.Status.ABORTED || resultStatus == TestExecutionResult.Status.FAILED;
+    }
+}
diff --git a/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/LegacyPlainResultFormatter.java b/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/LegacyPlainResultFormatter.java
new file mode 100644
index 0000000..7583d78
--- /dev/null
+++ b/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/LegacyPlainResultFormatter.java
@@ -0,0 +1,290 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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
+ *
+ *      https://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.apache.tools.ant.taskdefs.optional.junitlauncher;
+
+import org.junit.platform.engine.TestExecutionResult;
+import org.junit.platform.engine.reporting.ReportEntry;
+import org.junit.platform.engine.support.descriptor.ClassSource;
+import org.junit.platform.launcher.TestIdentifier;
+import org.junit.platform.launcher.TestPlan;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+
+/**
+ * A {@link TestResultFormatter} which prints a short statistic for each of the tests
+ */
+class LegacyPlainResultFormatter extends AbstractJUnitResultFormatter implements TestResultFormatter {
+
+    private OutputStream outputStream;
+    private final Map<TestIdentifier, Stats> testIds = new ConcurrentHashMap<>();
+    private TestPlan testPlan;
+    private BufferedWriter writer;
+    private boolean useLegacyReportingName = true;
+
+    @Override
+    public void testPlanExecutionStarted(final TestPlan testPlan) {
+        this.testPlan = testPlan;
+    }
+
+    @Override
+    public void testPlanExecutionFinished(final TestPlan testPlan) {
+        for (final Map.Entry<TestIdentifier, Stats> entry : this.testIds.entrySet()) {
+            final TestIdentifier testIdentifier = entry.getKey();
+            if (!isTestClass(testIdentifier).isPresent()) {
+                // we are not interested in anything other than a test "class" in this section
+                continue;
+            }
+            final Stats stats = entry.getValue();
+            final StringBuilder sb = new StringBuilder("Tests run: ").append(stats.numTestsRun.get());
+            sb.append(", Failures: ").append(stats.numTestsFailed.get());
+            sb.append(", Skipped: ").append(stats.numTestsSkipped.get());
+            sb.append(", Aborted: ").append(stats.numTestsAborted.get());
+            sb.append(", Time elapsed: ");
+            stats.appendElapsed(sb);
+            try {
+                this.writer.write(sb.toString());
+                this.writer.newLine();
+            } catch (IOException ioe) {
+                handleException(ioe);
+                return;
+            }
+        }
+        // write out sysout and syserr content if any
+        try {
+            if (this.hasSysOut()) {
+                this.writer.write("------------- Standard Output ---------------");
+                this.writer.newLine();
+                writeSysOut(writer);
+                this.writer.write("------------- ---------------- ---------------");
+                this.writer.newLine();
+            }
+            if (this.hasSysErr()) {
+                this.writer.write("------------- Standard Error ---------------");
+                this.writer.newLine();
+                writeSysErr(writer);
+                this.writer.write("------------- ---------------- ---------------");
+                this.writer.newLine();
+            }
+        } catch (IOException ioe) {
+            handleException(ioe);
+        }
+    }
+
+    @Override
+    public void dynamicTestRegistered(final TestIdentifier testIdentifier) {
+        // nothing to do
+    }
+
+    @Override
+    public void executionSkipped(final TestIdentifier testIdentifier, final String reason) {
+        final long currentTime = System.currentTimeMillis();
+        this.testIds.putIfAbsent(testIdentifier, new Stats(testIdentifier, currentTime));
+        final Stats stats = this.testIds.get(testIdentifier);
+        stats.setEndedAt(currentTime);
+        if (testIdentifier.isTest()) {
+            final StringBuilder sb = new StringBuilder();
+            sb.append("Test: ");
+            sb.append(this.useLegacyReportingName ? testIdentifier.getLegacyReportingName() : testIdentifier.getDisplayName());
+            sb.append(" took ");
+            stats.appendElapsed(sb);
+            sb.append(" SKIPPED");
+            if (reason != null && !reason.isEmpty()) {
+                sb.append(": ").append(reason);
+            }
+            try {
+                this.writer.write(sb.toString());
+                this.writer.newLine();
+            } catch (IOException ioe) {
+                handleException(ioe);
+                return;
+            }
+        }
+        // get the parent test class to which this skipped test belongs to
+        final Optional<TestIdentifier> parentTestClass = traverseAndFindTestClass(this.testPlan, testIdentifier);
+        if (!parentTestClass.isPresent()) {
+            return;
+        }
+        final Stats parentClassStats = this.testIds.get(parentTestClass.get());
+        parentClassStats.numTestsSkipped.incrementAndGet();
+    }
+
+    @Override
+    public void executionStarted(final TestIdentifier testIdentifier) {
+        final long currentTime = System.currentTimeMillis();
+        // record this testidentifier's start
+        this.testIds.putIfAbsent(testIdentifier, new Stats(testIdentifier, currentTime));
+        final Optional<ClassSource> testClass = isTestClass(testIdentifier);
+        if (testClass.isPresent()) {
+            // if this is a test class, then print it out
+            try {
+                this.writer.write("Testcase: " + testClass.get().getClassName());
+                this.writer.newLine();
+            } catch (IOException ioe) {
+                handleException(ioe);
+                return;
+            }
+        }
+        // if this is a test (method) then increment the tests run for the test class to which
+        // this test belongs to
+        if (testIdentifier.isTest()) {
+            final Optional<TestIdentifier> parentTestClass = traverseAndFindTestClass(this.testPlan, testIdentifier);
+            if (parentTestClass.isPresent()) {
+                final Stats parentClassStats = this.testIds.get(parentTestClass.get());
+                if (parentClassStats != null) {
+                    parentClassStats.numTestsRun.incrementAndGet();
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("incomplete-switch")
+    @Override
+    public void executionFinished(final TestIdentifier testIdentifier, final TestExecutionResult testExecutionResult) {
+        final long currentTime = System.currentTimeMillis();
+        final Stats stats = this.testIds.get(testIdentifier);
+        if (stats != null) {
+            stats.setEndedAt(currentTime);
+        }
+        if (testIdentifier.isTest() && shouldReportExecutionFinished(testIdentifier, testExecutionResult)) {
+            final StringBuilder sb = new StringBuilder();
+            sb.append("Test: ");
+            sb.append(this.useLegacyReportingName ? testIdentifier.getLegacyReportingName() : testIdentifier.getDisplayName());
+            if (stats != null) {
+                sb.append(" took ");
+                stats.appendElapsed(sb);
+            }
+            switch (testExecutionResult.getStatus()) {
+                case ABORTED: {
+                    sb.append(" ABORTED");
+                    appendThrowable(sb, testExecutionResult);
+                    break;
+                }
+                case FAILED: {
+                    sb.append(" FAILED");
+                    appendThrowable(sb, testExecutionResult);
+                    break;
+                }
+            }
+            try {
+                this.writer.write(sb.toString());
+                this.writer.newLine();
+            } catch (IOException ioe) {
+                handleException(ioe);
+                return;
+            }
+        }
+        // get the parent test class in which this test completed
+        final Optional<TestIdentifier> parentTestClass = traverseAndFindTestClass(this.testPlan, testIdentifier);
+        if (!parentTestClass.isPresent()) {
+            return;
+        }
+        // update the stats of the parent test class
+        final Stats parentClassStats = this.testIds.get(parentTestClass.get());
+        switch (testExecutionResult.getStatus()) {
+            case ABORTED: {
+                parentClassStats.numTestsAborted.incrementAndGet();
+                break;
+            }
+            case FAILED: {
+                parentClassStats.numTestsFailed.incrementAndGet();
+                break;
+            }
+        }
+    }
+
+    @Override
+    public void reportingEntryPublished(final TestIdentifier testIdentifier, final ReportEntry entry) {
+        // nothing to do
+    }
+
+    @Override
+    public void setDestination(final OutputStream os) {
+        this.outputStream = os;
+        this.writer = new BufferedWriter(new OutputStreamWriter(this.outputStream, StandardCharsets.UTF_8));
+    }
+
+    @Override
+    public void setUseLegacyReportingName(final boolean useLegacyReportingName) {
+        this.useLegacyReportingName = useLegacyReportingName;
+    }
+
+    protected boolean shouldReportExecutionFinished(final TestIdentifier testIdentifier, final TestExecutionResult testExecutionResult) {
+        return true;
+    }
+
+    private static void appendThrowable(final StringBuilder sb, TestExecutionResult result) {
+        if (!result.getThrowable().isPresent()) {
+            return;
+        }
+        final Throwable throwable = result.getThrowable().get();
+        sb.append(String.format(": %s%n", throwable.getMessage()));
+        final StringWriter stacktrace = new StringWriter();
+        throwable.printStackTrace(new PrintWriter(stacktrace));
+        sb.append(stacktrace.toString());
+    }
+
+    @Override
+    public void close() throws IOException {
+        if (this.writer != null) {
+            this.writer.close();
+        }
+        super.close();
+    }
+
+    private final class Stats {
+        @SuppressWarnings("unused")
+        private final TestIdentifier testIdentifier;
+        private final AtomicLong numTestsRun = new AtomicLong(0);
+        private final AtomicLong numTestsFailed = new AtomicLong(0);
+        private final AtomicLong numTestsSkipped = new AtomicLong(0);
+        private final AtomicLong numTestsAborted = new AtomicLong(0);
+        private final long startedAt;
+        private long endedAt;
+
+        private Stats(final TestIdentifier testIdentifier, final long startedAt) {
+            this.testIdentifier = testIdentifier;
+            this.startedAt = startedAt;
+        }
+
+        private void setEndedAt(final long endedAt) {
+            this.endedAt = endedAt;
+        }
+
+        private void appendElapsed(StringBuilder sb) {
+            final long timeElapsed = endedAt - startedAt;
+            if (timeElapsed < 1000) {
+                sb.append(timeElapsed).append(" milli sec(s)");
+            } else {
+                sb.append(TimeUnit.SECONDS.convert(timeElapsed, TimeUnit.MILLISECONDS)).append(" sec(s)");
+            }
+        }
+    }
+}
diff --git a/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/LegacyXmlResultFormatter.java b/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/LegacyXmlResultFormatter.java
new file mode 100644
index 0000000..bb9a963
--- /dev/null
+++ b/test/unit/org/apache/tools/ant/taskdefs/optional/junitlauncher/LegacyXmlResultFormatter.java
@@ -0,0 +1,424 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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
+ *
+ *      https://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.apache.tools.ant.taskdefs.optional.junitlauncher;
+
+import org.apache.tools.ant.util.DOMElementWriter;
+import org.apache.tools.ant.util.DateUtils;
+import org.apache.tools.ant.util.StringUtils;
+import org.junit.platform.engine.TestExecutionResult;
+import org.junit.platform.engine.TestSource;
+import org.junit.platform.engine.reporting.ReportEntry;
+import org.junit.platform.engine.support.descriptor.ClassSource;
+import org.junit.platform.launcher.TestIdentifier;
+import org.junit.platform.launcher.TestPlan;
+
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.util.Date;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * A {@link TestResultFormatter} which generates an XML report of the tests. The generated XML reports
+ * conforms to the schema of the XML that was generated by the {@code junit} task's XML
+ * report formatter and can be used by the {@code junitreport} task
+ */
+class LegacyXmlResultFormatter extends AbstractJUnitResultFormatter implements TestResultFormatter {
+
+    private static final double ONE_SECOND = 1000.0;
+
+    private OutputStream outputStream;
+    private final Map<TestIdentifier, Stats> testIds = new ConcurrentHashMap<>();
+    private final Map<TestIdentifier, Optional<String>> skipped = new ConcurrentHashMap<>();
+    private final Map<TestIdentifier, Optional<Throwable>> failed = new ConcurrentHashMap<>();
+    private final Map<TestIdentifier, Optional<Throwable>> aborted = new ConcurrentHashMap<>();
+
+    private TestPlan testPlan;
+    private long testPlanStartedAt = -1;
+    private long testPlanEndedAt = -1;
+    private final AtomicLong numTestsRun = new AtomicLong(0);
+    private final AtomicLong numTestsFailed = new AtomicLong(0);
+    private final AtomicLong numTestsSkipped = new AtomicLong(0);
+    private final AtomicLong numTestsAborted = new AtomicLong(0);
+    private boolean useLegacyReportingName = true;
+
+
+    @Override
+    public void testPlanExecutionStarted(final TestPlan testPlan) {
+        this.testPlan = testPlan;
+        this.testPlanStartedAt = System.currentTimeMillis();
+    }
+
+    @Override
+    public void testPlanExecutionFinished(final TestPlan testPlan) {
+        this.testPlanEndedAt = System.currentTimeMillis();
+        // format and print out the result
+        try {
+            new XMLReportWriter().write();
+        } catch (IOException | XMLStreamException e) {
+            handleException(e);
+        }
+    }
+
+    @Override
+    public void dynamicTestRegistered(final TestIdentifier testIdentifier) {
+        // nothing to do
+    }
+
+    @Override
+    public void executionSkipped(final TestIdentifier testIdentifier, final String reason) {
+        final long currentTime = System.currentTimeMillis();
+        this.numTestsSkipped.incrementAndGet();
+        this.skipped.put(testIdentifier, Optional.ofNullable(reason));
+        // a skipped test is considered started and ended now
+        final Stats stats = new Stats(testIdentifier, currentTime);
+        stats.endedAt = currentTime;
+        this.testIds.put(testIdentifier, stats);
+    }
+
+    @Override
+    public void executionStarted(final TestIdentifier testIdentifier) {
+        final long currentTime = System.currentTimeMillis();
+        this.testIds.putIfAbsent(testIdentifier, new Stats(testIdentifier, currentTime));
+        if (testIdentifier.isTest()) {
+            this.numTestsRun.incrementAndGet();
+        }
+    }
+
+    @Override
+    public void executionFinished(final TestIdentifier testIdentifier, final TestExecutionResult testExecutionResult) {
+        final long currentTime = System.currentTimeMillis();
+        final Stats stats = this.testIds.get(testIdentifier);
+        if (stats != null) {
+            stats.endedAt = currentTime;
+        }
+        switch (testExecutionResult.getStatus()) {
+            case SUCCESSFUL: {
+                break;
+            }
+            case ABORTED: {
+                this.numTestsAborted.incrementAndGet();
+                this.aborted.put(testIdentifier, testExecutionResult.getThrowable());
+                break;
+            }
+            case FAILED: {
+                this.numTestsFailed.incrementAndGet();
+                this.failed.put(testIdentifier, testExecutionResult.getThrowable());
+                break;
+            }
+        }
+    }
+
+    @Override
+    public void reportingEntryPublished(final TestIdentifier testIdentifier, final ReportEntry entry) {
+        // nothing to do
+    }
+
+    @Override
+    public void setDestination(final OutputStream os) {
+        this.outputStream = os;
+    }
+
+    @Override
+    public void setUseLegacyReportingName(final boolean useLegacyReportingName) {
+        this.useLegacyReportingName = useLegacyReportingName;
+    }
+
+    private final class Stats {
+        @SuppressWarnings("unused")
+        private final TestIdentifier testIdentifier;
+        private final long startedAt;
+        private long endedAt;
+
+        private Stats(final TestIdentifier testIdentifier, final long startedAt) {
+            this.testIdentifier = testIdentifier;
+            this.startedAt = startedAt;
+        }
+    }
+
+    private final class XMLReportWriter {
+
+        private static final String ELEM_TESTSUITE = "testsuite";
+        private static final String ELEM_PROPERTIES = "properties";
+        private static final String ELEM_PROPERTY = "property";
+        private static final String ELEM_TESTCASE = "testcase";
+        private static final String ELEM_SKIPPED = "skipped";
+        private static final String ELEM_FAILURE = "failure";
+        private static final String ELEM_ABORTED = "aborted";
+        private static final String ELEM_SYSTEM_OUT = "system-out";
+        private static final String ELEM_SYSTEM_ERR = "system-err";
+
+
+        private static final String ATTR_CLASSNAME = "classname";
+        private static final String ATTR_NAME = "name";
+        private static final String ATTR_VALUE = "value";
+        private static final String ATTR_TIME = "time";
+        private static final String ATTR_TIMESTAMP = "timestamp";
+        private static final String ATTR_NUM_ABORTED = "aborted";
+        private static final String ATTR_NUM_FAILURES = "failures";
+        private static final String ATTR_NUM_TESTS = "tests";
+        private static final String ATTR_NUM_SKIPPED = "skipped";
+        private static final String ATTR_MESSAGE = "message";
+        private static final String ATTR_TYPE = "type";
+
+        void write() throws XMLStreamException, IOException {
+            final XMLStreamWriter writer = XMLOutputFactory.newFactory().createXMLStreamWriter(outputStream, "UTF-8");
+            try {
+                writer.writeStartDocument();
+                writeTestSuite(writer);
+                writer.writeEndDocument();
+            } finally {
+                writer.close();
+            }
+        }
+
+        void writeTestSuite(final XMLStreamWriter writer) throws XMLStreamException, IOException {
+            // write the testsuite element
+            writer.writeStartElement(ELEM_TESTSUITE);
+            final String testsuiteName = determineTestSuiteName();
+            writeAttribute(writer, ATTR_NAME, testsuiteName);
+            // time taken for the tests execution
+            writeAttribute(writer, ATTR_TIME, String.valueOf((testPlanEndedAt - testPlanStartedAt) / ONE_SECOND));
+            // add the timestamp of report generation
+            final String timestamp = DateUtils.format(new Date(), DateUtils.ISO8601_DATETIME_PATTERN);
+            writeAttribute(writer, ATTR_TIMESTAMP, timestamp);
+            writeAttribute(writer, ATTR_NUM_TESTS, String.valueOf(numTestsRun.longValue()));
+            writeAttribute(writer, ATTR_NUM_FAILURES, String.valueOf(numTestsFailed.longValue()));
+            writeAttribute(writer, ATTR_NUM_SKIPPED, String.valueOf(numTestsSkipped.longValue()));
+            writeAttribute(writer, ATTR_NUM_ABORTED, String.valueOf(numTestsAborted.longValue()));
+
+            // write the properties
+            writeProperties(writer);
+            // write the tests
+            writeTestCase(writer);
+            writeSysOut(writer);
+            writeSysErr(writer);
+            // end the testsuite
+            writer.writeEndElement();
+        }
+
+        void writeProperties(final XMLStreamWriter writer) throws XMLStreamException {
+            final Properties properties = LegacyXmlResultFormatter.this.context.getProperties();
+            if (properties == null || properties.isEmpty()) {
+                return;
+            }
+            writer.writeStartElement(ELEM_PROPERTIES);
+            for (final String prop : properties.stringPropertyNames()) {
+                writer.writeStartElement(ELEM_PROPERTY);
+                writeAttribute(writer, ATTR_NAME, prop);
+                writeAttribute(writer, ATTR_VALUE, properties.getProperty(prop));
+                writer.writeEndElement();
+            }
+            writer.writeEndElement();
+        }
+
+        void writeTestCase(final XMLStreamWriter writer) throws XMLStreamException {
+            for (final Map.Entry<TestIdentifier, Stats> entry : testIds.entrySet()) {
+                final TestIdentifier testId = entry.getKey();
+                if (!testId.isTest() && !failed.containsKey(testId)) {
+                    // only interested in test methods unless there was a failure,
+                    // in which case we want the exception reported
+                    // (https://bz.apache.org/bugzilla/show_bug.cgi?id=63850)
+                    continue;
+                }
+                // find the associated class of this test
+                final Optional<ClassSource> parentClassSource;
+                if (testId.isTest()) {
+                    parentClassSource = findFirstParentClassSource(testId);
+                }
+                else {
+                    parentClassSource = findFirstClassSource(testId);
+                }
+                if (!parentClassSource.isPresent()) {
+                    continue;
+                }
+                final String classname = (parentClassSource.get()).getClassName();
+                writer.writeStartElement(ELEM_TESTCASE);
+                writeAttribute(writer, ATTR_CLASSNAME, classname);
+                writeAttribute(writer, ATTR_NAME, useLegacyReportingName ? testId.getLegacyReportingName()
+                                                                         : testId.getDisplayName());
+                final Stats stats = entry.getValue();
+                writeAttribute(writer, ATTR_TIME, String.valueOf((stats.endedAt - stats.startedAt) / ONE_SECOND));
+                // skipped element if the test was skipped
+                writeSkipped(writer, testId);
+                // failed element if the test failed
+                writeFailed(writer, testId);
+                // aborted element if the test was aborted
+                writeAborted(writer, testId);
+
+                writer.writeEndElement();
+            }
+        }
+
+        private void writeSkipped(final XMLStreamWriter writer, final TestIdentifier testIdentifier) throws XMLStreamException {
+            if (!skipped.containsKey(testIdentifier)) {
+                return;
+            }
+            writer.writeStartElement(ELEM_SKIPPED);
+            final Optional<String> reason = skipped.get(testIdentifier);
+            if (reason.isPresent()) {
+                writeAttribute(writer, ATTR_MESSAGE, reason.get());
+            }
+            writer.writeEndElement();
+        }
+
+        private 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.writeCData(StringUtils.getStackTrace(t));
+            }
+            writer.writeEndElement();
+        }
+
+        private void writeAborted(final XMLStreamWriter writer, final TestIdentifier testIdentifier) throws XMLStreamException {
+            if (!aborted.containsKey(testIdentifier)) {
+                return;
+            }
+            writer.writeStartElement(ELEM_ABORTED);
+            final Optional<Throwable> cause = aborted.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.writeCData(StringUtils.getStackTrace(t));
+            }
+            writer.writeEndElement();
+        }
+
+        private void writeSysOut(final XMLStreamWriter writer) throws XMLStreamException, IOException {
+            if (!LegacyXmlResultFormatter.this.hasSysOut()) {
+                return;
+            }
+            writer.writeStartElement(ELEM_SYSTEM_OUT);
+            try (final Reader reader = LegacyXmlResultFormatter.this.getSysOutReader()) {
+                writeCharactersFrom(reader, writer);
+            }
+            writer.writeEndElement();
+        }
+
+        private void writeSysErr(final XMLStreamWriter writer) throws XMLStreamException, IOException {
+            if (!LegacyXmlResultFormatter.this.hasSysErr()) {
+                return;
+            }
+            writer.writeStartElement(ELEM_SYSTEM_ERR);
+            try (final Reader reader = LegacyXmlResultFormatter.this.getSysErrReader()) {
+                writeCharactersFrom(reader, writer);
+            }
+            writer.writeEndElement();
+        }
+
+        private void writeCharactersFrom(final Reader reader, final XMLStreamWriter writer) throws IOException, XMLStreamException {
+            final char[] chars = new char[1024];
+            int numRead = -1;
+            while ((numRead = reader.read(chars)) != -1) {
+                writer.writeCharacters(encode(new String(chars, 0, numRead)));
+            }
+        }
+
+        private void writeAttribute(final XMLStreamWriter writer, final String name, final String value)
+        throws XMLStreamException {
+            writer.writeAttribute(name, encode(value));
+        }
+
+        private String encode(final 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(';');
+                } else {
+                    sb.append(c);
+                }
+            }
+            return changed ? sb.toString() : s;
+        }
+
+        private String determineTestSuiteName() {
+            // this is really a hack to try and match the expectations of the XML report in JUnit4.x
+            // world. In JUnit5, the TestPlan doesn't have a name and a TestPlan (for which this is a
+            // listener) can have numerous tests within it
+            final Set<TestIdentifier> roots = testPlan.getRoots();
+            if (roots.isEmpty()) {
+                return "UNKNOWN";
+            }
+            for (final TestIdentifier root : roots) {
+                final Optional<ClassSource> classSource = findFirstClassSource(root);
+                if (classSource.isPresent()) {
+                    return classSource.get().getClassName();
+                }
+            }
+            return "UNKNOWN";
+        }
+
+        private Optional<ClassSource> findFirstClassSource(final TestIdentifier root) {
+            if (root.getSource().isPresent()) {
+                final TestSource source = root.getSource().get();
+                if (source instanceof ClassSource) {
+                    return Optional.of((ClassSource) source);
+                }
+            }
+            for (final TestIdentifier child : testPlan.getChildren(root)) {
+                final Optional<ClassSource> classSource = findFirstClassSource(child);
+                if (classSource.isPresent()) {
+                    return classSource;
+                }
+            }
+            return Optional.empty();
+        }
+
+        private Optional<ClassSource> findFirstParentClassSource(final TestIdentifier testId) {
+            final Optional<TestIdentifier> parent = testPlan.getParent(testId);
+            if (!parent.isPresent()) {
+                return Optional.empty();
+            }
+            if (!parent.get().getSource().isPresent()) {
+                // the source of the parent is unknown, so we move up the
+                // hierarchy and try and find a class source
+                return findFirstParentClassSource(parent.get());
+            }
+            final TestSource parentSource = parent.get().getSource().get();
+            return parentSource instanceof ClassSource ? Optional.of((ClassSource) parentSource)
+                                                       : findFirstParentClassSource(parent.get());
+        }
+    }
+
+}

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


[cassandra] 01/04: CASSANDRA-16630. Updated dependencies.

Posted by az...@apache.org.
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 667a8946a01b128b61b06e96f5f09f5c11973c2a
Author: Aleksei Zotov <az...@gmail.com>
AuthorDate: Sat Nov 13 23:47:47 2021 +0400

    CASSANDRA-16630. Updated dependencies.
---
 build.xml                                          | 27 +++++++--------
 .../org/apache/cassandra/Junit4SampleTest.java     | 39 +++++++++++++++++++++
 .../org/apache/cassandra/Junit5SampleTest.java     | 40 ++++++++++++++++++++++
 3 files changed, 91 insertions(+), 15 deletions(-)

diff --git a/build.xml b/build.xml
index 085211a..6b6d71b 100644
--- a/build.xml
+++ b/build.xml
@@ -120,6 +120,7 @@
     <property name="allocation-instrumenter.version" value="3.1.0"/>
     <property name="bytebuddy.version" value="1.10.10"/>
     <property name="jflex.version" value="1.8.2"/>
+    <property name="junit.version" value="5.6.0"/>
 
     <!-- https://mvnrepository.com/artifact/net.openhft/chronicle-bom/1.16.23 -->
     <property name="chronicle-queue.version" value="5.20.123" />
@@ -524,10 +525,6 @@
           <dependency groupId="com.boundary" artifactId="high-scale-lib" version="1.0.6"/>
           <dependency groupId="com.github.jbellis" artifactId="jamm" version="${jamm.version}"/>
           <dependency groupId="org.yaml" artifactId="snakeyaml" version="1.26"/>
-          <dependency groupId="junit" artifactId="junit" version="4.12" scope="test">
-            <exclusion groupId="org.hamcrest" artifactId="hamcrest-core"/>
-          </dependency>
-          <dependency groupId="org.mockito" artifactId="mockito-core" version="3.2.4" scope="test"/>
           <dependency groupId="org.quicktheories" artifactId="quicktheories" version="0.26" scope="test"/>
           <dependency groupId="com.google.code.java-allocation-instrumenter" artifactId="java-allocation-instrumenter" version="${allocation-instrumenter.version}" scope="test">
             <exclusion groupId="com.google.guava" artifactId="guava"/>
@@ -565,8 +562,6 @@
           <dependency groupId="org.openjdk.jmh" artifactId="jmh-core" version="1.21" scope="test"/>
           <dependency groupId="org.openjdk.jmh" artifactId="jmh-generator-annprocess" version="1.21" scope="test"/>
 
-          <dependency groupId="org.apache.ant" artifactId="ant-junit" version="1.9.7" scope="test"/>
-
           <dependency groupId="org.apache.cassandra" artifactId="cassandra-all" version="${version}" />
           <dependency groupId="io.dropwizard.metrics" artifactId="metrics-core" version="3.1.5" />
           <dependency groupId="io.dropwizard.metrics" artifactId="metrics-jvm" version="3.1.5" />
@@ -650,11 +645,15 @@
           <dependency groupId="org.psjava" artifactId="psjava" version="0.1.19"/>
           <dependency groupId="javax.inject" artifactId="javax.inject" version="1"/>
           <dependency groupId="com.google.j2objc" artifactId="j2objc-annotations" version="1.3"/>
-          <!-- adding this dependency is necessary for assertj. When updating assertj, need to also update the version of
-             this that the new assertj's `assertj-parent-pom` depends on. -->
-          <dependency groupId="org.junit" artifactId="junit-bom" version="5.6.0" type="pom" scope="test"/>
-          <!-- when updating assertj, make sure to also update the corresponding junit-bom dependency -->
+
+          <dependency groupId="org.apache.ant" artifactId="ant-junitlauncher" version="1.10.11" scope="test"/>
+          <dependency groupId="org.junit.jupiter" artifactId="junit-jupiter" version="${junit.version}" scope="test"/>
+          <dependency groupId="org.junit.vintage" artifactId="junit-vintage-engine" version="${junit.version}" scope="test">
+            <exclusion groupId="org.hamcrest" artifactId="hamcrest-core"/>
+          </dependency>
+          <!-- when updating assertj, make sure to also update the corresponding junit dependencies -->
           <dependency groupId="org.assertj" artifactId="assertj-core" version="3.15.0" scope="provided"/>
+          <dependency groupId="org.mockito" artifactId="mockito-core" version="3.2.4" scope="test"/>
           <dependency groupId="org.awaitility" artifactId="awaitility" version="4.0.3"  scope="test">
             <exclusion groupId="org.hamcrest" artifactId="hamcrest"/>
           </dependency>
@@ -716,7 +715,6 @@
                 artifactId="cassandra-parent"
                 version="${version}"
                 relativePath="${final.name}-parent.pom"/>
-        <dependency groupId="junit" artifactId="junit" scope="test"/>
         <dependency groupId="commons-io" artifactId="commons-io" scope="test"/>
         <dependency groupId="org.mockito" artifactId="mockito-core" scope="test"/>
         <dependency groupId="com.puppycrawl.tools" artifactId="checkstyle" scope="test"/>
@@ -727,10 +725,9 @@
         <dependency groupId="org.openjdk.jmh" artifactId="jmh-core" scope="test"/>
         <dependency groupId="org.openjdk.jmh" artifactId="jmh-generator-annprocess" scope="test"/>
         <dependency groupId="net.ju-n.compile-command-annotations" artifactId="compile-command-annotations" scope="test"/>
-        <dependency groupId="org.apache.ant" artifactId="ant-junit" version="1.9.7" scope="test"/>
-        <!-- adding this dependency is necessary for assertj. When updating assertj, need to also update the version of
-             this that the new assertj's `assertj-parent-pom` depends on. -->
-        <dependency groupId="org.junit" artifactId="junit-bom" type="pom"/>
+        <dependency groupId="org.apache.ant" artifactId="ant-junitlauncher" scope="test"/>
+        <dependency groupId="org.junit.jupiter" artifactId="junit-jupiter" scope="test"/>
+        <dependency groupId="org.junit.vintage" artifactId="junit-vintage-engine" scope="test"/>
         <dependency groupId="org.awaitility" artifactId="awaitility"/>
         <dependency groupId="org.hamcrest" artifactId="hamcrest"/>
         <!-- coverage debs -->
diff --git a/test/unit/org/apache/cassandra/Junit4SampleTest.java b/test/unit/org/apache/cassandra/Junit4SampleTest.java
new file mode 100644
index 0000000..7eb27a5
--- /dev/null
+++ b/test/unit/org/apache/cassandra/Junit4SampleTest.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.cassandra;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class Junit4SampleTest
+{
+    @Test
+    public void testSuccess()
+    {
+        assertTrue(true);
+    }
+
+    @Test
+    public void testFailure()
+    {
+        fail("Expected failure");
+    }
+}
diff --git a/test/unit/org/apache/cassandra/Junit5SampleTest.java b/test/unit/org/apache/cassandra/Junit5SampleTest.java
new file mode 100644
index 0000000..0388d3c
--- /dev/null
+++ b/test/unit/org/apache/cassandra/Junit5SampleTest.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.cassandra;
+
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class Junit5SampleTest
+{
+    @Test
+    public void testSuccess()
+    {
+        assertTrue(true);
+    }
+
+    @Test
+    public void testFailure()
+    {
+        fail("Expected failure");
+    }
+}

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


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

Posted by az...@apache.org.
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