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

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

package org.specs2
package reporter

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

/**
 * 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)
        notifier.fireTestFinished(desc)
        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)                     => notifier.fireTestFailure(new notification.Failure(desc, junitFailure(f)))
          case e@Error(m, st)                             => notifier.fireTestFailure(new notification.Failure(desc, args.traceFilter(e.exception)))
          case DecoratedResult(_, f@Failure(m, e, st, d)) => notifier.fireTestFailure(new notification.Failure(desc, junitFailure(f)))
          case DecoratedResult(_, e@Error(m, st))         => notifier.fireTestFailure(new notification.Failure(desc, args.traceFilter(e.exception)))
          case Pending(_) | Skipped(_, _)                 => notifier.fireTestIgnored(desc)
          case Success(_, _) | DecoratedResult(_, _)      => ()
        }
      }
      case other => ()
    }
    result
  }

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

    exportToOthers(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, FailureDetails(expected, actual)) => 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) }
    }
  }
}


/**
 * Descriptions for a seq of Fragments to execute
 */
class JUnitDescriptionsFragments(className: String) extends JUnitDescriptions[Fragment](className) {
  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 Text(t)                    => Some(f -> createDescription(className, suiteName=testName(t)))
      case Example(description, body) => Some(f -> createDescription(className, label=nodeLabel.toString, testName=testName(description.toString, parentPath(parentNodes))))
      case Step(action,_)             => Some(f -> createDescription(className, label=nodeLabel.toString, testName="step"))
      case Action(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) {
  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 - 2025 Weber Informatics LLC | Privacy Policy