diff --git a/WHATSNEW b/WHATSNEW index dcc72fd75..f98e27324 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -91,6 +91,10 @@ Other changes: * has a new outputencoding attribute. Bugzilla Report 39541 + * changes to JUnitTestRunner and PlainJUnitResultFormatter to make + OutOfMemoryErrors less likely. + Bugzilla Report 45536 + Changes from Ant 1.9.2 TO Ant 1.9.3 =================================== diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java b/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java index a6d36c051..81e71aa84 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java @@ -558,8 +558,18 @@ public class JUnitTestRunner implements TestListener, JUnitTaskMirror.JUnitTestR systemOut.close(); systemOut = null; if (startTestSuiteSuccess) { - sendOutAndErr(new String(outStrm.toByteArray()), - new String(errStrm.toByteArray())); + String out, err; + try { + out = new String(outStrm.toByteArray()); + } catch (OutOfMemoryError ex) { + out = "out of memory on output stream"; + } + try { + err = new String(errStrm.toByteArray()); + } catch (OutOfMemoryError ex) { + err = "out of memory on error stream"; + } + sendOutAndErr(out, err); } } fireEndTestSuite(); diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java b/src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java index af6d5c7e6..122312aae 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java @@ -117,6 +117,7 @@ public class PlainJUnitResultFormatter implements JUnitResultFormatter, IgnoredT * @throws BuildException if unable to write the output */ public void endTestSuite(JUnitTest suite) throws BuildException { + try { StringBuffer sb = new StringBuffer("Tests run: "); sb.append(suite.runCount()); sb.append(", Failures: "); @@ -129,40 +130,49 @@ public class PlainJUnitResultFormatter implements JUnitResultFormatter, IgnoredT sb.append(nf.format(suite.getRunTime() / ONE_SECOND)); sb.append(" sec"); sb.append(StringUtils.LINE_SEP); + write(sb.toString()); - // append the err and output streams to the log + // write the err and output streams to the log if (systemOutput != null && systemOutput.length() > 0) { - sb.append("------------- Standard Output ---------------") - .append(StringUtils.LINE_SEP) - .append(systemOutput) - .append("------------- ---------------- ---------------") - .append(StringUtils.LINE_SEP); + write("------------- Standard Output ---------------"); + write(StringUtils.LINE_SEP); + write(systemOutput); + write("------------- ---------------- ---------------"); + write(StringUtils.LINE_SEP); } if (systemError != null && systemError.length() > 0) { - sb.append("------------- Standard Error -----------------") - .append(StringUtils.LINE_SEP) - .append(systemError) - .append("------------- ---------------- ---------------") - .append(StringUtils.LINE_SEP); + write("------------- Standard Error -----------------"); + write(StringUtils.LINE_SEP); + write(systemError); + write("------------- ---------------- ---------------"); + write(StringUtils.LINE_SEP); } - sb.append(StringUtils.LINE_SEP); - + write(StringUtils.LINE_SEP); + if (out != null) { + try { + wri.flush(); + write(inner.toString()); + } catch (IOException ioex) { + throw new BuildException("Unable to write output", ioex); + } + } + } finally { if (out != null) { try { - out.write(sb.toString().getBytes()); wri.close(); - out.write(inner.toString().getBytes()); - out.flush(); } catch (IOException ioex) { - throw new BuildException("Unable to write output", ioex); + throw new BuildException("Unable to flush output", ioex); } finally { if (out != System.out && out != System.err) { FileUtils.close(out); } + wri = null; + out = null; } } + } } /** @@ -286,4 +296,22 @@ public class PlainJUnitResultFormatter implements JUnitResultFormatter, IgnoredT public void testAssumptionFailure(Test test, Throwable throwable) { formatSkip(test, throwable.getMessage()); } + + /** + * Print out some text, and flush the output stream; encoding in the platform + * local default encoding. + * @param text text to write. + * @throws BuildException on IO Problems. + */ + private void write(String text) { + if (out == null) { + return; + } + try { + out.write(text.getBytes()); + out.flush(); + } catch (IOException ex) { + throw new BuildException("Unable to write output " + ex, ex); + } + } } // PlainJUnitResultFormatter diff --git a/src/main/org/apache/tools/ant/util/DOMElementWriter.java b/src/main/org/apache/tools/ant/util/DOMElementWriter.java index cf37ff67b..b9b22730a 100644 --- a/src/main/org/apache/tools/ant/util/DOMElementWriter.java +++ b/src/main/org/apache/tools/ant/util/DOMElementWriter.java @@ -21,6 +21,7 @@ package org.apache.tools.ant.util; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.io.StringWriter; import java.io.Writer; import java.util.ArrayList; import java.util.HashMap; @@ -233,7 +234,7 @@ public class DOMElementWriter { case Node.CDATA_SECTION_NODE: out.write(""); break; @@ -486,19 +487,59 @@ public class DOMElementWriter { * href="http://www.w3.org/TR/1998/REC-xml-19980210#sec-cdata-sect">http://www.w3.org/TR/1998/REC-xml-19980210#sec-cdata-sect.

* @param value the value to be encoded. * @return the encoded value. - */ public String encodedata(final String value) { + final StringWriter out = new StringWriter(); + try { + encodedata(out, value); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + return out.toString(); + } + + /** + * Drop characters that are illegal in XML documents and write the + * rest to the given writer. + * + *

Also ensure that we are not including an ]]> + * marker by replacing that sequence with + * ]]>.

+ * + *

See XML 1.0 2.2 + * http://www.w3.org/TR/1998/REC-xml-19980210#charsets and + * 2.7 http://www.w3.org/TR/1998/REC-xml-19980210#sec-cdata-sect.

+ * @param value the value to be encoded. + * @param out where to write the encoded data to. + */ + public void encodedata(final Writer out, final String value) throws IOException { final int len = value.length(); - StringBuffer sb = new StringBuffer(len); - for (int i = 0; i < len; ++i) { - final char c = value.charAt(i); - if (isLegalCharacter(c)) { - sb.append(c); + int prevEnd = 0, cdataEndPos = value.indexOf("]]>"); + while (prevEnd < len) { + final int end = (cdataEndPos < 0 ? len : cdataEndPos); + // Write out stretches of legal characters in the range [prevEnd, end). + for (int prevLegalCharPos = prevEnd; prevLegalCharPos < end; /*empty*/) { + int illegalCharPos; + for (illegalCharPos = prevLegalCharPos; true; ++illegalCharPos) { + if (illegalCharPos >= end + || !isLegalCharacter(value.charAt(illegalCharPos))) { + break; + } + } + out.write(value, prevLegalCharPos, illegalCharPos - prevLegalCharPos); + prevLegalCharPos = illegalCharPos + 1; } - } - return sb.substring(0).replace("]]>", "]]]]>"); + if (cdataEndPos >= 0) { + out.write("]]]]>"); + prevEnd = cdataEndPos + 3; + cdataEndPos = value.indexOf("]]>", prevEnd); + } else { + prevEnd = end; + } + } } /**