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

org.specs2.specification.BaseSpecification.scala Maven / Gradle / Ivy

package org.specs2
package specification

import org.specs2.internal.scalaz._
import Scalaz._
import Fragments._
import reflect.Classes._
import main.{CommandLineArguments, Arguments}

/**
 * A Base specification contains the minimum elements for a Specification
 * 
 * - a Seq of Fragments, available through the SpecificationStructure trait
 * - methods for creating Fragments from the FragmentsBuilder trait
 * - methods to include other specifications
 *
 */
trait BaseSpecification extends SpecificationStructure with FragmentsBuilder with SpecificationInclusion

/**
 * additional methods to include other specifications or Fragments
 */
trait SpecificationInclusion { this: FragmentsBuilder =>
  def include(f: Fragments): FragmentsFragment = fragmentsFragments(f)
  def include(f: Fragments, fs: Fragments*): FragmentsFragment = include(ma(f +: fs).sum)
  implicit def include(s: SpecificationStructure): FragmentsFragment = include(s.content)
  def include(s: SpecificationStructure, ss: SpecificationStructure*): FragmentsFragment = include(s.content, ss.map(_.content):_*)
  def include(args: Arguments, s: SpecificationStructure): FragmentsFragment = include(args, s.applyArguments(args).content)
  def include(args: Arguments, s: SpecificationStructure, ss: SpecificationStructure*): FragmentsFragment = include(args, s.applyArguments(args).content, ss.map(_.applyArguments(args).content):_*)
  def include(args: Arguments, f: Fragments): FragmentsFragment = include(f.overrideArgs(args))
  def include(args: Arguments, f: Fragments, fs: Fragments*): FragmentsFragment = include(ma(f +: fs).sum.overrideArgs(args))

  /** add the fragments of another specification without start and end */
  def inline(specs: SpecificationStructure*): Fragments = Fragments.createList(specs.flatMap(s => s.map(s.is).middle):_*)
}
/**
 * The structure of a Specification is simply defined as a sequence of fragments
 */
trait SpecificationStructure { 
  /** declaration of Fragments from the user */
  def is: Fragments
  /** this method can be overriden to map additional behavior in the user-defined fragments */
  def map(fs: =>Fragments): Fragments = fs
  /** specName provides useful information identifying the specification: title, className, url... */
  def identification: SpecIdentification = content.specName
  /** automatically convert a specification to its identification */
  implicit def identifySpecificationStructure(s: SpecificationStructure): SpecIdentification = s.identification
  /** 
   * this "cached" version of the Fragments is kept hidden from the user to avoid polluting
   * the Specification namespace.
   * SpecStart and SpecEnd fragments are added if the user haven't inserted any
   *
   * A creation path is possibly set on Examples and Actions if they haven't any
   */
  private[specs2] lazy val content: Fragments = map(Fragments.withCreationPaths(Fragments.withSpecName(is, this)))

  /** apply command-line arguments if there are any */
  private[specs2] def applyArguments(implicit args: Arguments) =
    this match {
      case withCommandLineArguments : CommandLineArguments => withCommandLineArguments.set(args); this
      case other                                           => this
    }

}

/**
 * methods for creating SpecificationStructure instances from fragments
 */
private[specs2]
object SpecificationStructure {
  import collection.Iterablex._
  
  def apply(fs: Fragments): SpecificationStructure = new SpecificationStructure {
    def is = fs.fragments match {
      case SpecStart(n,a,l) +: middle :+ SpecEnd(_,_) => Fragments(Some(n), middle, a, l)
      case other                                      => fs
    }
  }
  def apply(fs: Seq[Fragment]): SpecificationStructure = apply(Fragments.create(fs:_*))

  def apply(spec: SpecificationStructure, arguments: Arguments): SpecificationStructure = apply(spec.content add arguments)

  /**
   * create a SpecificationStructure from a className, throwing an Error if that's not possible
   */
  def createSpecification(className: String, classLoader: ClassLoader = Thread.currentThread.getContextClassLoader)
                         (implicit args: Arguments = Arguments()): SpecificationStructure = {
    createSpecificationOption(className, classLoader) match {
      case Some(s) => s
      case None    => sys.error("can not create specification: "+className)
    }
  }

  /**
   * create a SpecificationStructure from a className, returning None if that's not possible
   */
  def createSpecificationOption(className: String, classLoader: ClassLoader = Thread.currentThread.getContextClassLoader)
                               (implicit args: Arguments = Arguments()) : Option[SpecificationStructure] = {
    // finally retry the original class name to display the error messages
    createSpecificationFromClassOrObject(className, classLoader).
      orElse(tryToCreateObject[SpecificationStructure](className, loader = classLoader)).map(applyCommandLineArguments)
  }

  /**
   * create a SpecificationStructure from a className, returning an Exception if that's not possible
   */
  def createSpecificationEither(className: String, classLoader: ClassLoader = Thread.currentThread.getContextClassLoader)
                               (implicit args: Arguments = Arguments()) : Either[Throwable, SpecificationStructure] = {
    // try to create the specification from a class name, without displaying possible errors
    createSpecificationFromClassOrObject(className, classLoader).map(Right(_)).
      // try to create the specification from an object class name
      getOrElse(tryToCreateObjectEither[SpecificationStructure](className, classLoader, Some(args))).map(applyCommandLineArguments)
  }

  private def createSpecificationFromClassOrObject(className: String,
                                                   classLoader: ClassLoader = Thread.currentThread.getContextClassLoader)
                                                  (implicit args: Arguments = Arguments()) : Option[SpecificationStructure] = {
    // try to create the specification from a class name, without displaying possible errors
    tryToCreateObject[SpecificationStructure](className,
      printMessage = false,
      printStackTrace = false,
      loader = classLoader,
      parameter = Some(args)).
      // try to create the specification from an object class name
      orElse(tryToCreateObject[SpecificationStructure](className+"$",
      printMessage = false,
      printStackTrace = false,
      loader = classLoader,
      parameter = Some(args)))
  }

  /**
   * store the command-line arguments in the CommandLineArguments trait if necessary
   */
  private def applyCommandLineArguments(implicit args: Arguments) = (spec: SpecificationStructure) => spec.applyArguments(args)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy