From 7d642556280e83115d1c928af11d040bd6b9831d Mon Sep 17 00:00:00 2001 From: Sameer Arora Date: Tue, 16 Jul 2019 18:00:51 -0700 Subject: [PATCH] adding stream writer to ScoverageXmlWriter.scala to work with large projects --- .../scoverage/report/ScoverageXmlWriter.scala | 192 ++++++++++-------- 1 file changed, 108 insertions(+), 84 deletions(-) diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/report/ScoverageXmlWriter.scala b/scalac-scoverage-plugin/src/main/scala/scoverage/report/ScoverageXmlWriter.scala index 33d63e10..b4ab3843 100644 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/report/ScoverageXmlWriter.scala +++ b/scalac-scoverage-plugin/src/main/scala/scoverage/report/ScoverageXmlWriter.scala @@ -4,7 +4,10 @@ import java.io.File import scoverage._ -import scala.xml.{Node, PrettyPrinter} +import javax.xml.stream.XMLOutputFactory +import javax.xml.stream.XMLStreamWriter +import java.io.FileWriter +import com.sun.xml.internal.txw2.output.IndentingXMLStreamWriter /** @author Stephen Samuel */ class ScoverageXmlWriter(sourceDirectories: Seq[File], outputDir: File, debug: Boolean) extends BaseReportWriter(sourceDirectories, outputDir) { @@ -15,105 +18,126 @@ class ScoverageXmlWriter(sourceDirectories: Seq[File], outputDir: File, debug: B def write(coverage: Coverage): Unit = { val file = IOUtils.reportFile(outputDir, debug) - IOUtils.writeToFile(file, new PrettyPrinter(120, 4).format(xml(coverage))) + + val factory = XMLOutputFactory.newInstance() + val w: XMLStreamWriter = factory.createXMLStreamWriter(new FileWriter(file)) + val writer = new IndentingXMLStreamWriter(w) + xml(coverage, writer) } - private def xml(coverage: Coverage): Node = { - - - {coverage.packages.map(pack)} - - + private def xml(coverage: Coverage, writer: XMLStreamWriter): Unit = { + writer.writeStartDocument() + writer.writeStartElement("scoverage") + writer.writeAttribute("statement-count", coverage.statementCount.toString) + writer.writeAttribute("statements-invoked", coverage.invokedStatementCount.toString) + writer.writeAttribute("statement-rate", coverage.statementCoverageFormatted) + writer.writeAttribute("branch-rate", coverage.branchCoverageFormatted) + writer.writeAttribute("version", "1.0") + writer.writeAttribute("timestamp", System.currentTimeMillis.toString) + + writer.writeStartElement("packages") + coverage.packages.foreach{m: MeasuredPackage => pack(m,writer)} + writer.writeEndElement() + writer.writeEndElement() + writer.writeEndDocument() + + writer.flush() + writer.close() } - private def statement(stmt: Statement): Node = { + private def statement(stmt: Statement, writer: XMLStreamWriter): Unit = { debug match { case true => - - {escape(stmt.desc)} - + writer.writeStartElement("statement") + writer.writeAttribute("package", stmt.location.packageName) + writer.writeAttribute("class", stmt.location.className) + writer.writeAttribute("class-type", stmt.location.classType.toString) + writer.writeAttribute("full-class-name", stmt.location.fullClassName) + writer.writeAttribute("source", stmt.source) + writer.writeAttribute("method", stmt.location.method) + writer.writeAttribute("start", stmt.start.toString) + writer.writeAttribute("end", stmt.end.toString) + writer.writeAttribute("line", stmt.line.toString) + writer.writeAttribute("symbol", escape(stmt.symbolName)) + writer.writeAttribute("tree", escape(stmt.treeName)) + writer.writeAttribute("branch", stmt.branch.toString) + writer.writeAttribute("invocation-count", stmt.count.toString) + writer.writeAttribute("ignored", stmt.ignored.toString) + writer.writeCharacters(escape(stmt.desc)) + writer.writeEndElement() case false => - + writer.writeStartElement("statement") + writer.writeAttribute("package", stmt.location.packageName) + writer.writeAttribute("class", stmt.location.className) + writer.writeAttribute("class-type", stmt.location.classType.toString) + writer.writeAttribute("full-class-name", stmt.location.fullClassName) + writer.writeAttribute("source", stmt.source) + writer.writeAttribute("method", stmt.location.method) + writer.writeAttribute("start", stmt.start.toString) + writer.writeAttribute("end", stmt.end.toString) + writer.writeAttribute("line", stmt.line.toString) + writer.writeAttribute("branch", stmt.branch.toString) + writer.writeAttribute("invocation-count", stmt.count.toString) + writer.writeAttribute("ignored", stmt.ignored.toString) + writer.writeEndElement() } } - private def method(method: MeasuredMethod): Node = { - - - {method.statements.map(statement)} - - + private def method(method: MeasuredMethod, writer: XMLStreamWriter): Unit = { + writer.writeStartElement("method") + writer.writeAttribute("name", method.name) + writer.writeAttribute("statement-count", method.statementCount.toString) + writer.writeAttribute("statements-invoked", method.invokedStatementCount.toString) + writer.writeAttribute("statement-rate", method.statementCoverageFormatted) + writer.writeAttribute("branch-rate",method.branchCoverageFormatted) + + writer.writeStartElement("statements") + method.statements.foreach{s: Statement => statement(s, writer)} + writer.writeEndElement() + writer.writeEndElement() + } - private def klass(klass: MeasuredClass): Node = { - - - {klass.methods.map(method)} - - + private def klass(klass: MeasuredClass, writer: XMLStreamWriter): Unit = { + writer.writeStartElement("class") + writer.writeAttribute("name", klass.fullClassName) + writer.writeAttribute("filename", relativeSource(klass.source)) + writer.writeAttribute("statement-count", klass.statementCount.toString) + writer.writeAttribute("statements-invoked", klass.invokedStatementCount.toString) + writer.writeAttribute("statement-rate", klass.statementCoverageFormatted) + writer.writeAttribute("branch-rate",klass.branchCoverageFormatted) + + writer.writeStartElement("methods") + klass.methods.foreach{m: MeasuredMethod => method(m, writer)} + writer.writeEndElement() + writer.writeEndElement() } - private def pack(pack: MeasuredPackage): Node = { - - - {pack.classes.map(klass)} - - + private def pack(pack: MeasuredPackage, writer: XMLStreamWriter): Unit = { + writer.writeStartElement("package") + writer.writeAttribute("name", pack.name) + writer.writeAttribute("statement-count", pack.statementCount.toString) + writer.writeAttribute("statements-invoked", pack.invokedStatementCount.toString) + writer.writeAttribute("statement-rate", pack.statementCoverageFormatted) + + writer.writeStartElement("classes") + pack.classes.foreach{k: MeasuredClass => klass(k, writer)} + writer.writeEndElement() + writer.writeEndElement() } /** - * This method ensures that the output String has only - * valid XML unicode characters as specified by the - * XML 1.0 standard. For reference, please see - * the - * standard. This method will return an empty - * String if the input is null or empty. - * - * @param in The String whose non-valid characters we want to remove. - * @return The in String, stripped of non-valid characters. - * @see http://blog.mark-mclaren.info/2007/02/invalid-xml-characters-when-valid-utf8_5873.html - * - */ + * This method ensures that the output String has only + * valid XML unicode characters as specified by the + * XML 1.0 standard. For reference, please see + * the + * standard. This method will return an empty + * String if the input is null or empty. + * + * @param in The String whose non-valid characters we want to remove. + * @return The in String, stripped of non-valid characters. + * @see http://blog.mark-mclaren.info/2007/02/invalid-xml-characters-when-valid-utf8_5873.html + * + */ def escape(in: String): String = { val out = new StringBuilder() for ( current <- Option(in).getOrElse("").toCharArray ) {