All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scalatest.tools.HtmlReporter.scala Maven / Gradle / Ivy

Go to download

ScalaTest is a free, open-source testing toolkit for Scala and Java programmers.

There is a newer version: 2.0.M6-SNAP4
Show newest version
/*
 * Copyright 2001-2008 Artima, Inc.
 *
 * Licensed 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.scalatest.tools

import org.scalatest._
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.OutputStream
import java.io.OutputStreamWriter
import java.io.PrintWriter
import java.util.Iterator
import java.util.Set
import java.io.StringWriter
import org.scalatest.events._
import org.scalatest.exceptions.TestFailedException
import PrintReporter.{BufferSize, makeDurationString}
import HtmlReporter._
import org.pegdown.PegDownProcessor
import scala.collection.mutable.ListBuffer
import scala.xml.NodeSeq
import scala.xml.XML
import java.util.UUID
import scala.xml.Node
import scala.annotation.tailrec
import java.net.URL
import scala.io.Source
import java.nio.channels.Channels
import java.text.DecimalFormat

/**
 * A Reporter that prints test status information in HTML format to a file.
 */
private[scalatest] class HtmlReporter(directoryPath: String, presentAllDurations: Boolean,
        presentInColor: Boolean, presentStackTraces: Boolean, presentFullStackTraces: Boolean, 
        cssUrl: Option[URL], resultHolder: Option[SuiteResultHolder]) extends ResourcefulReporter {

  private val specIndent = 15
  private val targetDir = new File(directoryPath)
  private val imagesDir = new File(targetDir, "images")
  private val jsDir = new File(targetDir, "js")
  private val cssDir = new File(targetDir, "css")
  
  if (!targetDir.exists)
    targetDir.mkdirs()
    
  if (!imagesDir.exists)
    imagesDir.mkdirs()
    
  if (!jsDir.exists)
    jsDir.mkdirs()
    
  if (!cssDir.exists)
    cssDir.mkdirs()
    
  private def copyResource(url: URL, toDir: File, targetFileName: String) {
    val inputStream = url.openStream
    try {
      val outputStream = new FileOutputStream(new File(toDir, targetFileName))
      try {
        outputStream getChannel() transferFrom(Channels.newChannel(inputStream), 0, Long.MaxValue)
      }
      finally {
        outputStream.flush()
      outputStream.close()
      }
    }
    finally {
      inputStream.close()
    }
  }
  
  private def getResource(resourceName: String): URL = 
    classOf[Suite].getClassLoader.getResource(resourceName)
  
  cssUrl match {
    case Some(cssUrl) => copyResource(cssUrl, cssDir, "custom.css")
    case None => // Do nothing.
  }
  copyResource(getResource("org/scalatest/HtmlReporter.css"), cssDir, "styles.css")
  copyResource(getResource("org/scalatest/sorttable.js"), jsDir, "sorttable.js")
  copyResource(getResource("org/scalatest/d3.v2.min.js"), jsDir, "d3.v2.min.js")
  
  copyResource(getResource("images/greenbullet.gif"), imagesDir, "testsucceeded.gif")
  copyResource(getResource("images/redbullet.gif"), imagesDir, "testfailed.gif")
  copyResource(getResource("images/yellowbullet.gif"), imagesDir, "testignored.gif")
  copyResource(getResource("images/yellowbullet.gif"), imagesDir, "testcanceled.gif")
  copyResource(getResource("images/yellowbullet.gif"), imagesDir, "testpending.gif")
  copyResource(getResource("images/graybullet.gif"), imagesDir, "infoprovided.gif")
  
  private val results = resultHolder match {
    case Some(holder) => holder
    case None => new SuiteResultHolder()
  }
  private val pegDown = new PegDownProcessor

  private def withPossibleLineNumber(stringToPrint: String, throwable: Option[Throwable]): String = {
    throwable match {
      case Some(testFailedException: TestFailedException) =>
        testFailedException.failedCodeFileNameAndLineNumberString match {
          case Some(lineNumberString) =>
            Resources("printedReportPlusLineNumber", stringToPrint, lineNumberString)
          case None => stringToPrint
        }
      case _ => stringToPrint
    }
  }
  
  private def stringsToPrintOnError(noteResourceName: String, errorResourceName: String, message: String, throwable: Option[Throwable],
    formatter: Option[Formatter], suiteName: Option[String], testName: Option[String], duration: Option[Long]): String = {

    formatter match {
      case Some(IndentedText(_, rawText, _)) =>
        Resources("specTextAndNote", rawText, Resources(noteResourceName))
      case _ =>
        // Deny MotionToSuppress directives in error events, because error info needs to be seen by users
          suiteName match {
            case Some(sn) =>
              testName match {
                case Some(tn) => Resources(errorResourceName, sn + ": " + tn)
                case None => Resources(errorResourceName, sn)
              }
            // Should not get here with built-in ScalaTest stuff, but custom stuff could get here.
            case None => Resources(errorResourceName, Resources("noNameSpecified"))
          }
      }
  }

  private def stringToPrintWhenNoError(resourceName: String, formatter: Option[Formatter], suiteName: String, testName: Option[String]): Option[String] =
    stringToPrintWhenNoError(resourceName, formatter, suiteName, testName, None)

  private def stringToPrintWhenNoError(resourceName: String, formatter: Option[Formatter], suiteName: String, testName: Option[String], duration: Option[Long]): Option[String] = {

    formatter match {
      case Some(IndentedText(_, rawText, _)) =>
        duration match {
          case Some(milliseconds) =>
            if (presentAllDurations)
              Some(Resources("withDuration", rawText, makeDurationString(milliseconds)))
            else
              Some(rawText)
          case None => Some(rawText)
        }
      case Some(MotionToSuppress) => None
      case _ =>
        val arg =
          testName match {
            case Some(tn) => suiteName + ": " + tn
            case None => suiteName
          }
        val unformattedText = Resources(resourceName, arg)
        duration match {
          case Some(milliseconds) =>
            if (presentAllDurations)
              Some(Resources("withDuration", unformattedText, makeDurationString(milliseconds)))
            else
              Some(unformattedText)
          case None => Some(unformattedText)
        }

    }
  }
  
  private def getIndentLevel(formatter: Option[Formatter]) = 
    formatter match {
      case Some(IndentedText(formattedText, rawText, indentationLevel)) => indentationLevel
      case _ => 0
  }
  
  private def getSuiteFileName(suiteResult: SuiteResult) = 
    suiteResult.suiteClassName match {
      case Some(suiteClassName) => suiteClassName
      case None => suiteResult.suiteName
    }
  
  private def makeSuiteFile(suiteResult: SuiteResult) {
    val name = getSuiteFileName(suiteResult)
    
    val pw = new PrintWriter(new BufferedOutputStream(new FileOutputStream(new File(targetDir, name + ".html")), BufferSize))
    try {
      pw.println {
        "" + "\n" + 
        "" + "\n" + 
        getSuiteHtml(name, suiteResult) 
      }
    }
    finally {
      pw.flush()
      pw.close()
    }
  }
  
  private def appendCombinedStatus(name: String, r: SuiteResult) = 
    if (r.testsFailedCount > 0)
      name + "_with_failed"
    else if (r.testsIgnoredCount > 0 || r.testsPendingCount > 0 || r.testsCanceledCount > 0)
      name + "_passed"
    else
      name + "_passed_all"
  
  private def transformStringForResult(s: String, suiteResult: SuiteResult): String =
    s + (if (suiteResult.testsFailedCount > 0) "_failed" else "_passed")

  private def getSuiteHtml(name: String, suiteResult: SuiteResult) = 
    
      
        ScalaTest Suite { name } Results
        
        
        
        
        { 
          cssUrl match {
            case Some(cssUrl) => 
              
            case None => NodeSeq.Empty
          }
        }
        
      
      
        
{ suiteResult.suiteName }
{ "Tests: total " + (suiteResult.testsSucceededCount + suiteResult.testsFailedCount + suiteResult.testsCanceledCount + suiteResult.testsIgnoredCount + suiteResult.testsPendingCount) + ", succeeded " + suiteResult.testsSucceededCount + ", failed " + suiteResult.testsFailedCount + ", canceled " + suiteResult.testsCanceledCount + ", ignored " + suiteResult.testsIgnoredCount + ", pending " + suiteResult.testsPendingCount }
{ val scopeStack = new collection.mutable.Stack[String]() suiteResult.eventList.map { e => e match { case ScopeOpened(ordinal, message, nameInfo, formatter, location, payload, threadName, timeStamp) => val testNameInfo = nameInfo.testName val stringToPrint = stringToPrintWhenNoError("scopeOpened", formatter, nameInfo.suiteName, nameInfo.testName) stringToPrint match { case Some(string) => val elementId = generateElementId scopeStack.push(elementId) scope(elementId, string, getIndentLevel(formatter) + 1) case None => NodeSeq.Empty } case ScopeClosed(ordinal, message, nameInfo, formatter, location, payload, threadName, timeStamp) => scopeStack.pop NodeSeq.Empty case TestSucceeded(ordinal, suiteName, suiteId, suiteClassName, testName, testText, recordedEvents, duration, formatter, location, rerunnable, payload, threadName, timeStamp) => val stringToPrint = stringToPrintWhenNoError("testSucceeded", formatter, suiteName, Some(testName), duration) val nodeSeq = stringToPrint match { case Some(string) => val elementId = generateElementId test(elementId, List(string), getIndentLevel(formatter) + 1, "test_passed") case None => NodeSeq.Empty } nodeSeq :: recordedEvents.map(processInfoMarkupProvided(_)).toList case TestFailed(ordinal, message, suiteName, suiteId, suiteClassName, testName, testText, recordedEvents, throwable, duration, formatter, location, rerunnable, payload, threadName, timeStamp) => val stringToPrint = stringsToPrintOnError("failedNote", "testFailed", message, throwable, formatter, Some(suiteName), Some(testName), duration) val elementId = generateElementId val nodeSeq = testWithDetails(elementId, List(stringToPrint), message, throwable, getIndentLevel(formatter) + 1, "test_failed") nodeSeq :: recordedEvents.map(processInfoMarkupProvided(_)).toList case TestIgnored(ordinal, suiteName, suiteId, suiteClassName, testName, testText, formatter, location, payload, threadName, timeStamp) => val stringToPrint = formatter match { case Some(IndentedText(_, rawText, _)) => Some(Resources("specTextAndNote", rawText, Resources("ignoredNote"))) case Some(MotionToSuppress) => None case _ => Some(Resources("testIgnored", suiteName + ": " + testName)) } stringToPrint match { case Some(string) => val elementId = generateElementId test(elementId, List(string), getIndentLevel(formatter) + 1, "test_ignored") case None => NodeSeq.Empty } case TestPending(ordinal, suiteName, suiteId, suiteClassName, testName, testText, recordedEvents, duration, formatter, location, payload, threadName, timeStamp) => val stringToPrint = formatter match { case Some(IndentedText(_, rawText, _)) => Some(Resources("specTextAndNote", rawText, Resources("pendingNote"))) case Some(MotionToSuppress) => None case _ => Some(Resources("testPending", suiteName + ": " + testName)) } val nodeSeq = stringToPrint match { case Some(string) => val elementId = generateElementId test(elementId, List(string), getIndentLevel(formatter) + 1, "test_pending") case None => NodeSeq.Empty } nodeSeq :: recordedEvents.map(processInfoMarkupProvided(_)).toList case TestCanceled(ordinal, message, suiteName, suiteId, suiteClassName, testName, testText, recordedEvents, throwable, duration, formatter, location, payload, threadName, timeStamp) => val stringToPrint = stringsToPrintOnError("canceledNote", "testCanceled", message, throwable, formatter, Some(suiteName), Some(testName), duration) val elementId = generateElementId val nodeSeq = testWithDetails(elementId, List(stringToPrint), message, throwable, getIndentLevel(formatter) + 1, "test_canceled") nodeSeq :: recordedEvents.map(processInfoMarkupProvided(_)).toList case infoProvided: InfoProvided => processInfoMarkupProvided(infoProvided) case markupProvided: MarkupProvided => processInfoMarkupProvided(markupProvided) // TO CONTINUE: XML element must be last case _ => NodeSeq.Empty } } } private def processInfoMarkupProvided(event: Event) = { event match { case InfoProvided(ordinal, message, nameInfo, throwable, formatter, location, payload, threadName, timeStamp) => val (suiteName, testName) = nameInfo match { case Some(NameInfo(suiteName, _, _, testName)) => (Some(suiteName), testName) case None => (None, None) } val infoContent = stringsToPrintOnError("infoProvidedNote", "infoProvided", message, throwable, formatter, suiteName, testName, None) val elementId = generateElementId test(elementId, List(infoContent), getIndentLevel(formatter) + 1, "info") case MarkupProvided(ordinal, text, nameInfo, formatter, location, payload, threadName, timeStamp) => val (suiteName, testName) = nameInfo match { case Some(NameInfo(suiteName, _, _, testName)) => (Some(suiteName), testName) case None => (None, None) } val elementId = generateElementId markup(elementId, text, getIndentLevel(formatter) + 1, "markup") case _ => NodeSeq.Empty } } private def makeIndexFile(resourceName: String, duration: Option[Long]) { val pw = new PrintWriter(new BufferedOutputStream(new FileOutputStream(new File(targetDir, "index.html")), BufferSize)) try { pw.println { "\n" + "\n" + getIndexHtml(resourceName, duration) } } finally { pw.flush() pw.close() } } private def getHeaderStatusColor(summary: Summary) = if (summary.testsFailedCount == 0) "scalatest-header-passed" else "scalatest-header-failed" private def getPieChartScript(summary: Summary) = { import summary._ "/* modified from http://www.permadi.com/tutorial/cssGettingBackgroundColor/index.html - */" + "\n" + "function getBgColor(elementId)" + "\n" + "{" + "\n" + " var element = document.getElementById(elementId);" + "\n" + " if (element.currentStyle)" + "\n" + " return element.currentStyle.backgroundColor;" + "\n" + " if (window.getComputedStyle)" + "\n" + " {" + "\n" + " var elementStyle=window.getComputedStyle(element,\"\");" + "\n" + " if (elementStyle)" + "\n" + " return elementStyle.getPropertyValue(\"background-color\");" + "\n" + " }" + "\n" + " // Return 0 if both methods failed." + "\n" + " return 0;" + "\n" + "}" + "\n" + "var data = [" + testsSucceededCount + ", " + testsFailedCount + ", " + testsIgnoredCount + ", " + testsPendingCount + ", " + testsCanceledCount + "];" + "\n" + "var color = [getBgColor('summary_view_row_1_legend_succeeded_label'), " + "\n" + " getBgColor('summary_view_row_1_legend_failed_label'), " + "\n" + " getBgColor('summary_view_row_1_legend_ignored_label'), " + "\n" + " getBgColor('summary_view_row_1_legend_pending_label'), " + "\n" + " getBgColor('summary_view_row_1_legend_canceled_label')" + "\n" + " ];" + "\n" + "var width = document.getElementById('chart_div').offsetWidth," + "\n" + " height = document.getElementById('chart_div').offsetHeight," + "\n" + " outerRadius = Math.min(width, height) / 2," + "\n" + " innerRadius = 0," + "\n" + " donut = d3.layout.pie()," + "\n" + " arc = d3.svg.arc().innerRadius(innerRadius).outerRadius(outerRadius);" + "\n" + "var vis = d3.select(\"#chart_div\")" + "\n" + " .append(\"svg\")" + "\n" + " .data([data])" + "\n" + " .attr(\"width\", width)" + "\n" + " .attr(\"height\", height);" + "\n" + "var arcs = vis.selectAll(\"g.arc\")" + "\n" + " .data(donut)" + "\n" + " .enter().append(\"g\")" + "\n" + " .attr(\"class\", \"arc\")" + "\n" + " .attr(\"transform\", \"translate(\" + outerRadius + \",\" + outerRadius + \")\");" + "\n" + "arcs.append(\"path\")" + "\n" + " .attr(\"fill\", function(d, i) { return color[i]; })" + "\n" + " .attr(\"d\", arc);\n" } private def getIndexHtml(resourceName: String, duration: Option[Long]) = { val summary = results.summary import summary._ val decimalFormat = new DecimalFormat("#.##") ScalaTest Results { cssUrl match { case Some(cssUrl) => case None => NodeSeq.Empty } }
{ header(resourceName, duration, summary) }
Succeeded { testsSucceededCount } ({ decimalFormat.format(testsSucceededCount * 100.0 / totalTestsCount) }%)
Failed { testsFailedCount } ({ decimalFormat.format(testsFailedCount * 100.0 / totalTestsCount) }%)
Canceled { testsCanceledCount } ({ decimalFormat.format(testsCanceledCount * 100.0 / totalTestsCount) }%)
Ignored { testsIgnoredCount } ({ decimalFormat.format(testsIgnoredCount * 100.0 / totalTestsCount) }%)
Pending { testsPendingCount } ({ decimalFormat.format(testsPendingCount * 100.0 / totalTestsCount) }%)
{ getStatistic(summary) } { suiteResults }
Click on suite name to view details.
Click on column name to sort.
} // TODO: This needs to be internationalized private def getStatistic(summary: Summary) =
private def header(resourceName: String, duration: Option[Long], summary: Summary) =
ScalaTest Results

{ getDuration(resourceName, duration) }

{ getTotalTests(summary) }

{ getSuiteSummary(summary) }

{ getTestSummary(summary) }

private def generateElementId = UUID.randomUUID.toString private def setBit(stack: collection.mutable.Stack[String], tagMap: collection.mutable.HashMap[String, Int], bit: Int) { stack.foreach { scopeElementId => val currentBits = tagMap(scopeElementId) tagMap.put(scopeElementId, currentBits | bit) } } val tagMap = collection.mutable.HashMap[String, Int]() private def suiteResults = { val sortedSuiteList = results.suiteList.sortWith { (a, b) => if (a.testsFailedCount == b.testsFailedCount) { if (a.testsCanceledCount == b.testsCanceledCount) { if (a.testsIgnoredCount == b.testsIgnoredCount) { if (a.testsPendingCount == b.testsPendingCount) a.startEvent.suiteName < b.startEvent.suiteName else a.testsPendingCount > b.testsPendingCount } else a.testsIgnoredCount > b.testsIgnoredCount } else a.testsCanceledCount > b.testsCanceledCount } else a.testsFailedCount > b.testsFailedCount }.toArray sortedSuiteList map { r => val elementId = generateElementId import r._ val bits = (if (testsSucceededCount > 0) SUCCEEDED_BIT else 0) + (if (testsFailedCount > 0) FAILED_BIT else 0) + (if (testsIgnoredCount > 0) IGNORED_BIT else 0) + (if (testsPendingCount > 0) PENDING_BIT else 0) + (if (testsCanceledCount > 0) CANCELED_BIT else 0) tagMap.put(elementId, bits) suiteSummary(elementId, getSuiteFileName(r), r) } }
Suite Duration (ms.) Succeeded Failed Canceled Ignored Pending Total
private def countStyle(prefix: String, count: Int) = if (count == 0) prefix + "_zero" else prefix private def durationDisplay(duration: Option[Long]) = duration match { case Some(duration) => duration case None => "-" } private def suiteSummary(elementId: String, suiteFileName: String, suiteResult: SuiteResult) = { import suiteResult._ { suiteName } { durationDisplay(duration) } { testsSucceededCount } { testsFailedCount } { testsCanceledCount } { testsIgnoredCount } { testsPendingCount } { testsSucceededCount + testsFailedCount + testsIgnoredCount + testsPendingCount + testsCanceledCount } } private def twoLess(indentLevel: Int): Int = indentLevel - 2 match { case lev if lev < 0 => 0 case lev => lev } private def oneLess(indentLevel: Int): Int = indentLevel - 1 match { case lev if lev < 0 => 0 case lev => lev } private def scope(elementId: String, message: String, indentLevel: Int) =
{ message }
private def test(elementId: String, lines: List[String], indentLevel: Int, styleName: String) =
{ lines.map { line =>
{ line }
} }
private def testWithDetails(elementId: String, lines: List[String], message: String, throwable: Option[Throwable], indentLevel: Int, styleName: String) = { def getHTMLForStackTrace(stackTraceList: List[StackTraceElement]) = stackTraceList.map((ste: StackTraceElement) =>
{ ste.toString }
) def getHTMLForCause(throwable: Throwable): scala.xml.NodeBuffer = { val cause = throwable.getCause if (cause != null) {
{ Resources("DetailsCause") + ":" } { cause.getClass.getName }
{ Resources("DetailsMessage") + ":" } { if (cause.getMessage != null) cause.getMessage else Resources("None") }
{ getHTMLForStackTrace(cause.getStackTrace.toList) }
&+ getHTMLForCause(cause) } else new scala.xml.NodeBuffer } val (grayStackTraceElements, blackStackTraceElements) = throwable match { case Some(throwable) => val stackTraceElements = throwable.getStackTrace.toList throwable match { case sde: exceptions.StackDepthException => (stackTraceElements.take(sde.failedCodeStackDepth), stackTraceElements.drop(sde.failedCodeStackDepth)) case _ => (List(), stackTraceElements) } case None => (List(), List()) } val throwableTitle = throwable match { case Some(throwable) => Some(throwable.getClass.getName) case None => None } val fileAndLineOption: Option[String] = throwable match { case Some(throwable) => throwable match { case stackDepth: StackDepth => stackDepth.failedCodeFileNameAndLineNumberString case _ => None } case None => None } val linkId = UUID.randomUUID.toString val contentId = UUID.randomUUID.toString
{ lines.map { line =>
{ line }
} }
} private def markup(elementId: String, text: String, indentLevel: Int, styleName: String) =
{ XML.loadString(pegDown.markdownToHtml(text)) }
private def tagMapScript = "tagMap = { \n" + tagMap.map { case (elementId, bitSet) => "\"" + elementId + "\": " + bitSet }.mkString(", \n") + "};\n" + "applyFilter();" private var eventList = new ListBuffer[Event]() private var runEndEvent: Option[Event] = None def apply(event: Event) { event match { case RunStarting(ordinal, testCount, configMap, formatter, location, payload, threadName, timeStamp) => case RunCompleted(ordinal, duration, summary, formatter, location, payload, threadName, timeStamp) => runEndEvent = Some(event) case RunStopped(ordinal, duration, summary, formatter, location, payload, threadName, timeStamp) => runEndEvent = Some(event) case RunAborted(ordinal, message, throwable, duration, summary, formatter, location, payload, threadName, timeStamp) => runEndEvent = Some(event) case SuiteCompleted(ordinal, suiteName, suiteId, suiteClassName, duration, formatter, location, rerunner, payload, threadName, timeStamp) => val (suiteEvents, otherEvents) = extractSuiteEvents(suiteId) eventList = otherEvents val sortedSuiteEvents = suiteEvents.sorted if (sortedSuiteEvents.length == 0) throw new IllegalStateException("Expected SuiteStarting for completion event: " + event + " in the head of suite events, but we got no suite event at all") sortedSuiteEvents.head match { case suiteStarting: SuiteStarting => val suiteResult = sortedSuiteEvents.foldLeft(SuiteResult(suiteId, suiteName, suiteClassName, duration, suiteStarting, event, Vector.empty ++ sortedSuiteEvents.tail, 0, 0, 0, 0, 0, true)) { case (r, e) => e match { case testSucceeded: TestSucceeded => r.copy(testsSucceededCount = r.testsSucceededCount + 1) case testFailed: TestFailed => r.copy(testsFailedCount = r.testsFailedCount + 1) case testIgnored: TestIgnored => r.copy(testsIgnoredCount = r.testsIgnoredCount + 1) case testPending: TestPending => r.copy(testsPendingCount = r.testsPendingCount + 1) case testCanceled: TestCanceled => r.copy(testsCanceledCount = r.testsCanceledCount + 1) case _ => r } } results += suiteResult makeSuiteFile(suiteResult) case other => throw new IllegalStateException("Expected SuiteStarting for completion event: " + event + " in the head of suite events, but we got: " + other) } case SuiteAborted(ordinal, message, suiteName, suiteId, suiteClassName, throwable, duration, formatter, location, rerunner, payload, threadName, timeStamp) => val (suiteEvents, otherEvents) = extractSuiteEvents(suiteId) eventList = otherEvents val sortedSuiteEvents = suiteEvents.sorted if (sortedSuiteEvents.length == 0) throw new IllegalStateException("Expected SuiteStarting for completion event: " + event + " in the head of suite events, but we got no suite event at all") sortedSuiteEvents.head match { case suiteStarting: SuiteStarting => val suiteResult = sortedSuiteEvents.foldLeft(SuiteResult(suiteId, suiteName, suiteClassName, duration, suiteStarting, event, Vector.empty ++ sortedSuiteEvents.tail, 0, 0, 0, 0, 0, false)) { case (r, e) => e match { case testSucceeded: TestSucceeded => r.copy(testsSucceededCount = r.testsSucceededCount + 1) case testFailed: TestFailed => r.copy(testsFailedCount = r.testsFailedCount + 1) case testIgnored: TestIgnored => r.copy(testsIgnoredCount = r.testsIgnoredCount + 1) case testPending: TestPending => r.copy(testsPendingCount = r.testsPendingCount + 1) case testCanceled: TestCanceled => r.copy(testsCanceledCount = r.testsCanceledCount + 1) case _ => r } } results += suiteResult makeSuiteFile(suiteResult) case other => throw new IllegalStateException("Expected SuiteStarting for completion event: " + event + " in the head of suite events, but we got: " + other) } case _ => eventList += event } } def extractSuiteEvents(suiteId: String) = eventList partition { e => e match { case e: TestStarting => e.suiteId == suiteId case e: TestSucceeded => e.suiteId == suiteId case e: TestIgnored => e.suiteId == suiteId case e: TestFailed => e.suiteId == suiteId case e: TestPending => e.suiteId == suiteId case e: TestCanceled => e.suiteId == suiteId case e: InfoProvided => e.nameInfo match { case Some(nameInfo) => nameInfo.suiteId == suiteId case None => false } case e: MarkupProvided => e.nameInfo match { case Some(nameInfo) => nameInfo.suiteId == suiteId case None => false } case e: ScopeOpened => e.nameInfo.suiteId == suiteId case e: ScopeClosed => e.nameInfo.suiteId == suiteId case e: SuiteStarting => e.suiteId == suiteId case _ => false } } def dispose() { runEndEvent match { case Some(event) => event match { case RunCompleted(ordinal, duration, summary, formatter, location, payload, threadName, timeStamp) => makeIndexFile("runCompleted", duration) case RunStopped(ordinal, duration, summary, formatter, location, payload, threadName, timeStamp) => makeIndexFile("runStopped", duration) case RunAborted(ordinal, message, throwable, duration, summary, formatter, location, payload, threadName, timeStamp) => makeIndexFile("runAborted", duration) case other => throw new IllegalStateException("Expected run ending event only, but got: " + other.getClass.getName) } case None => // If no run end event (e.g. when run in sbt), just use runCompleted with sum of suites' duration. makeIndexFile("runCompleted", Some(results.totalDuration)) } } private def getDuration(resourceName: String, duration: Option[Long]) = { duration match { case Some(msSinceEpoch) => Resources(resourceName + "In", makeDurationString(msSinceEpoch)) case None => Resources(resourceName) } } private def getTotalTests(summary: Summary) = Resources("totalNumberOfTestsRun", summary.testsCompletedCount.toString) // Suites: completed {0}, aborted {1} private def getSuiteSummary(summary: Summary) = Resources("suiteSummary", summary.suitesCompletedCount.toString, summary.suitesAbortedCount.toString) // Tests: succeeded {0}, failed {1}, canceled {4}, ignored {2}, pending {3} private def getTestSummary(summary: Summary) = Resources("testSummary", summary.testsSucceededCount.toString, summary.testsFailedCount.toString, summary.testsCanceledCount.toString, summary.testsIgnoredCount.toString, summary.testsPendingCount.toString) // We subtract one from test reports because we add "- " in front, so if one is actually zero, it will come here as -1 // private def indent(s: String, times: Int) = if (times <= 0) s else (" " * times) + s // Stupid properties file won't let me put spaces at the beginning of a property // " {0}" comes out as "{0}", so I can't do indenting in a localizable way. For now // just indent two space to the left. // if (times <= 0) s // else Resources("indentOnce", indent(s, times - 1)) } private[tools] object HtmlReporter { final val SUCCEEDED_BIT = 1 final val FAILED_BIT = 2 final val IGNORED_BIT = 4 final val PENDING_BIT = 8 final val CANCELED_BIT = 16 } private[tools] object PCDATA { def apply(in: String): Node = scala.xml.Unparsed(in) }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy