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

org.specs2.reporter.JUnitReporter.scala Maven / Gradle / Ivy

There is a newer version: 4.10.6
Show newest version
package org.specs2
package reporter

import _root_.org.junit.runner.notification.RunNotifier
import _root_.org.junit._
import _root_.org.junit.runner._
import scalaz.{Reducer, Scalaz}
import Scalaz._
import main.{SystemProperties, Arguments}
import execute._
import specification._
  import text.AnsiColors
import control.{ExecutionOrigin, Throwablex}
import junit.framework.AssertionFailedError
import text.NotNullStrings._

/**
 * The JUnitReporter reports a specification by using a JUnit RunNotifier
 *
 * To do so, it uses most of the execution pipeline of a normal reporter but overrides the fragments execution so as
 * to notify JUnit of the appropriate events
 */
trait JUnitReporter extends ExecutionOrigin with DefaultReporter with Exporters {

  /** the selected fragments to report */
  def selected: SpecificationStructure
  /** map providing a description for each fragment */
  def descriptions: Map[Fragment, Description]
  /** arguments for the specification */
  implicit def args: Arguments
  /** system properties */
  implicit def properties: SystemProperties

  /** the junit notifier to use */
  def notifier: RunNotifier

  /**
   * run the suite by executing each fragment related to a description:
   * - execute all fragments (including Steps which are reported as steps)
   * - for each result, report the failure/error/skipped or pending message as a
   *   junit failure or ignored event on the RunNotifier
  */
  def report = {
    selected |> sequence |> execute |> store |> export
  }

  override def executeFragment(implicit arguments: Arguments): Function[Fragment, ExecutedFragment] = (fragment: Fragment) => {
    def execute(aFragment: Fragment) = super.executeFragment(arguments)(aFragment)
    // the description for a given fragment. It *should* always be found by construction
    val desc = descriptions(fragment)

    fragment match {
      case f: Example => {
        notifier.fireTestStarted(desc)
        val result = execute(f)
        notifyResult(desc, result)
        result
      }
      case f: Step         => notifyResult(desc, execute(f))
      case f: Action       => notifyResult(desc, execute(f))
      case f: SpecStart    => notifier.fireTestRunStarted(desc); execute(f)
      case f: SpecEnd      => notifier.fireTestRunFinished(new org.junit.runner.Result); execute(f)
      case other           => execute(other)
    }
  }

  /**
   * notify JUnit of a new result
   */
  private def notifyResult(desc: Description, result: ExecutedFragment): ExecutedFragment = {
    result match {
      case ExecutedResult(_, r, timer, _, _) => {
        r match {
          case f @ Failure(m, e, st, d)                     => failWith(desc, junitFailure(f))
          case e @ Error(m, st)                             => failWith(desc, args.traceFilter(e.exception))
          case DecoratedResult(_, f @ Failure(m, e, st, d)) => failWith(desc, junitFailure(f))
          case DecoratedResult(_, e @ Error(m, st))         => failWith(desc, args.traceFilter(e.exception))
          case Pending(_) | Skipped(_, _)                   => notifier.fireTestIgnored(desc)
          case Success(_, _) | DecoratedResult(_, _)        => notifier.fireTestFinished(desc)
        }
      }
      case other => ()
    }
    result
  }

  private def failWith(desc: Description, failure: Throwable) = {
    notifier.fireTestFailure(new notification.Failure(desc, failure))
    notifier.fireTestFinished(desc)
  }

  def export(implicit args: Arguments) = (executing: ExecutingSpecification) => {
    def exportTo = (name: String) => properties.isDefined(name) || args.contains(name)

    exportAll(args, exportTo)(executing)
    executing.executed
  }

  /** @return a Throwable expected by JUnit Failure object */
  private def junitFailure(f: Failure)(implicit args: Arguments): Throwable = f match {
    case Failure(m, e, st, NoDetails) =>
      new SpecFailureAssertionFailedError(Throwablex.exception(AnsiColors.removeColors(m), args.traceFilter(st)))

    case Failure(m, e, st, FromNotImplementedError) =>
      new SpecFailureAssertionFailedError(Throwablex.exception(AnsiColors.removeColors(m), args.traceFilter(st)))

    case Failure(m, e, st, FromJUnitAssertionError) =>
      new SpecFailureAssertionFailedError(Throwablex.exception(AnsiColors.removeColors(m), args.traceFilter(st)))

    case Failure(m, e, st, FailureDetails(actual, expected)) => new ComparisonFailure(AnsiColors.removeColors(m), expected, actual) {
      private val e = args.traceFilter(f.exception)
      override def getStackTrace = e.getStackTrace
      override def getCause = e.getCause
      override def printStackTrace() { e.printStackTrace() }
      override def printStackTrace(w: java.io.PrintStream) { e.printStackTrace(w) }
      override def printStackTrace(w: java.io.PrintWriter) { e.printStackTrace(w) }
    }

    case Failure(m, e, st, FailureSeqDetails(actual, expected)) =>
      val details =
        if (args.diffs.showSeq(actual, expected, ordered = true)) {
          val (added, missing) = args.diffs.showSeqDiffs(actual, expected, ordered = true)
          List(showValues("Added", added), showValues("Missing", missing)).mkString(" / ")
        } else ""

      new ComparisonFailure(AnsiColors.removeColors(m+details), expected.mkString("\n"), actual.mkString("\n")) {
        private val e = args.traceFilter(f.exception)
        override def getStackTrace = e.getStackTrace
        override def getCause = e.getCause
        override def printStackTrace() { e.printStackTrace() }
        override def printStackTrace(w: java.io.PrintStream) { e.printStackTrace(w) }
        override def printStackTrace(w: java.io.PrintWriter) { e.printStackTrace(w) }
      }

    case Failure(m, e, st, details @ FailureSetDetails(actual, expected)) =>
      val details =
        if (args.diffs.showSeq(actual.toSeq, expected.toSeq, ordered = false)) {
          val (added, missing) = args.diffs.showSeqDiffs(actual.toSeq, expected.toSeq, ordered = false)
          List(showValues("Added", added.toSeq), showValues("Missing", missing.toSeq)).mkString(" / ")
        } else ""

      new ComparisonFailure(AnsiColors.removeColors(m+details), expected.mkString("\n"), actual.mkString("\n")) {
        private val e = args.traceFilter(f.exception)
        override def getStackTrace = e.getStackTrace
        override def getCause = e.getCause
        override def printStackTrace() { e.printStackTrace() }
        override def printStackTrace(w: java.io.PrintStream) { e.printStackTrace(w) }
        override def printStackTrace(w: java.io.PrintWriter) { e.printStackTrace(w) }
      }

    case Failure(m, e, st, details @ FailureMapDetails(actual, expected)) =>
      val details =
        if (args.diffs.showMap(actual, expected)) {
          val (added, missing, different) = args.diffs.showMapDiffs(actual, expected)
          List(showValues("Added", added), showValues("Missing", missing), showValues("Different", different)).mkString(" / ")
        } else ""

      new ComparisonFailure(AnsiColors.removeColors(m+details), expected.mkString("\n"), actual.mkString("\n")) {
        private val e = args.traceFilter(f.exception)
        override def getStackTrace = e.getStackTrace
        override def getCause = e.getCause
        override def printStackTrace() { e.printStackTrace() }
        override def printStackTrace(w: java.io.PrintStream) { e.printStackTrace(w) }
        override def printStackTrace(w: java.io.PrintWriter) { e.printStackTrace(w) }
      }
  }

  /** show values as a string with a description */
  def showValues(description: String, values: Seq[Any]): String =
    if (values.nonEmpty) s"$description ${values.map(notNullPair).mkString("\n", "\n", "\n\n")}" else ""
}


/**
 * Descriptions for a seq of Fragments to execute
 */
class JUnitDescriptionsFragments(className: String) extends JUnitDescriptions[Fragment](className)(Levels.FragmentLevelsReducer) {
  def initialFragment(className: String) = Text(className)
  /**
  * This function is used to map each node in a Tree[Fragment] to a pair of
  * (Description, Fragment)
  *
  * The Int argument is the numeric label of the current TreeNode being mapped.
  * It is used to create a unique description of the example to executed which is required
  * by JUnit
  */
  def mapper(className: String): (Fragment, Seq[DescribedFragment], Int) => Option[DescribedFragment] =
    (f: Fragment, parentNodes: Seq[DescribedFragment], nodeLabel: Int) => f match {
      case s: SpecStart                        => Some(f -> createDescription(className, suiteName=testName(s.name)))
      case t: Text if t.text.raw.trim.nonEmpty => Some(f -> createDescription(className, suiteName=testName(t.text.raw)))
      case t: Text                             => None
      case t: Example                          => Some(f -> createDescription(className, label=nodeLabel.toString, testName=testName(t.desc.toString, parentPath(parentNodes))))
      case t: Step                             => Some(f -> createDescription(className, label=nodeLabel.toString, testName="step"))
      case t: Action                           => Some(f -> createDescription(className, label=nodeLabel.toString, testName="action"))
      case other                               => None
    }
}
/**
 * This class refines the `AssertionFailedError` from junit
 * and provides the stackTrace of an exception which occurred during the specification execution
 */
class SpecFailureAssertionFailedError(e: Exception) extends AssertionFailedError(e.getMessage.notNull) {
  override def toString = e.toString
  override def getStackTrace = e.getStackTrace
  override def getCause = e.getCause
  override def printStackTrace() { e.printStackTrace() }
  override def printStackTrace(w: java.io.PrintStream) { e.printStackTrace(w) }
  override def printStackTrace(w: java.io.PrintWriter) { e.printStackTrace(w) }
}






© 2015 - 2024 Weber Informatics LLC | Privacy Policy