Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package org.specs2
package specification
import control._
import Exceptions._
import LazyParameters._
import main.Arguments
import execute._
import text._
import Regexes._
import scalaz.Monoid
import io.Location
import scala.Either
import org.specs2.data.{NamedTag, SeparatedTags}
import TagFragments._
import scala.xml.NodeSeq
/**
* A Fragment is a piece of a specification. It can be a piece of text, an action or
* an Example
*/
sealed trait Fragment {
def location: Location
def matches(s: String) = true
}
/**
* Start of a specification.
*
* This fragment keeps 2 important pieces of information:
*
* - the name of the specification (which is derived from the specification class or from a user-defined title)
* That name stores a unique id for the specification
* - the arguments for that specification
*/
case class SpecStart(specName: SpecName, arguments: Arguments = Arguments(), linked: Linked = Linked(), location: Location = new Location) extends Fragment {
def name = specName.name
def title = specName.title
override def matches(s: String) = name matches s
override def toString = "SpecStart("+title+linkToString+")"
def linkToString = linked.linkToString
/** the new arguments take precedence over the old ones */
def withArgs(args: Arguments) = copy(arguments = args)
/** the new arguments take override the old ones where defined */
def overrideArgs(args: Arguments) = copy(arguments = arguments.overrideWith(args))
/** @return true if this spec starts only contains a link referencing another specification */
def isSeeOnlyLink = linked.isSeeOnlyLink
/** @return true if this spec starts only contains a link including another specification */
def isIncludeLink = linked.isIncludeLink
/** @return true if this spec starts only contains a link to another specification */
def isLink = linked.isLink
/** @return true if this spec must not be displayed */
def hidden = linked.hidden
/** @return the html link if any */
def link = linked.link
/** The name of the specification can be overriden with a user defined title */
def withName(n: SpecName) = copy(specName = specName.overrideWith(n))
/** @return a non-linked start*/
def unlink = SpecStart(specName, arguments)
/** set the url for the generated documentation */
def urlIs(url: String) = copy(specName = specName.urlIs(url), linked = linked.urlIs(url))
/** set the base directory for the generated documentation */
def baseDirIs(dir: String) = copy(specName = specName.baseDirIs(dir), linked = linked.baseDirIs(dir))
}
/**
* End of a specification.
*
* This marks the end of the Specification and must have the same name as the corresponding SpecStart.
*
* There is a Boolean flag on a SpecEnd indicating if the whole specification was just executed as a link (for an index page for example)
* In this case we must not store statistics for this specification (see Storing.scala)
*/
case class SpecEnd(specName: SpecName, isSeeOnlyLink: Boolean = false, location: Location = new Location) extends Fragment {
def name = specName.name
def title = specName.title
def seeOnlyLinkIs(s: Boolean) = copy(isSeeOnlyLink = s)
override def matches(s: String) = name.matchesSafely(s, ".*")
override def toString = "SpecEnd("+title+")"
}
/**
* Free text, describing the system to specify
*/
case class Text(text: SimpleFormattedString, location: Location = new Location) extends Fragment {
def t = text.raw
override def matches(s: String) = t.matches(s)
def flow = text.flow
def add(other: Text) = copy(text.append(other.t))
override def toString = s"Text($text)"
override def equals(a: Any) = a match {
case t: Text => text == t.text
case _ => false
}
override def hashCode = text.hashCode
}
object Text {
def apply(s: String): Text = new Text(FormattedString(s))
def create(formattedString: SimpleFormattedString): Text = new Text(formattedString)
}
trait FormattedString {
type F <: FormattedString
def raw: String
def formatting: Formatting
def isEmpty: Boolean
def map(f: String => String): F
def append(s: String) = map(t => t+s)
def prepend(s: String) = map(t => s+t)
def withMarkdown: FormattedString
def withFlow: FormattedString
def formatWithTagNames(names: Seq[String]): F
def flow = formatting.flow
def toXml: NodeSeq
override def toString = raw
}
case class SimpleFormattedString(t: String = "", formatting: Formatting = Formatting(), isEmpty: Boolean = false, isCode: Boolean = true) extends FormattedString {
type F = SimpleFormattedString
def raw: String = t
def map(f: String => String) = copy(t = f(t))
def withMarkdown = copy(formatting = formatting.copy(markdown = true))
def withoutMarkdown = copy(formatting = formatting.copy(markdown = false))
def withFlow = copy(formatting = formatting.copy(flow = true))
def withoutFlow = copy(formatting = formatting.copy(flow = false))
def toXml = if (formatting.markdown) {asCode} else if (isEmpty) else {raw}
def formatWithTagNames(names: Seq[String]) = copy(formatting = formatting.fromTagNames(names: Seq[String]))
override def toString = raw
private def asCode =
if (isCode) if (t.contains("\n")) "```\n"+t+"\n```" else "`"+t+"`"
else raw
}
object FormattedString {
def apply(t: String) = SimpleFormattedString(t)
def code(t: String) = SimpleFormattedString(t, isCode = true).withMarkdown
def empty = SimpleFormattedString(isEmpty = true)
}
/** Formatting for Text fragments */
case class Formatting(flow: Boolean = false, markdown: Boolean = true, verbatim: Boolean = true) {
def fromTagNames(names: Seq[String]) = copy(flow = tagValue(names, "flow", flow), markdown = tagValue(names, "markdown", markdown), verbatim = tagValue(names, "verbatim", verbatim))
private def tagValue(names: Seq[String], name: String, defaultValue: Boolean) = {
val nameFound = names.exists(_ == FormattingTags.internal+name)
val negatedNameFound = names.exists(_ == "!"+FormattingTags.internal+name)
if (nameFound && !negatedNameFound) true
else if (negatedNameFound) false
else defaultValue
}
}
/**
* A Example is:
*
* - a description: some text, with possibly some markdown annotations for rendering code fragments (used in AutoExamples)
* - a body: some executable code returning a Result
*/
case class Example private[specification] (desc: FormattedString = FormattedString(""), body: () => Result,
location: Location = new Location,
isolable: Boolean = true, private[specs2] val creationPath: Option[CreationPath] = None) extends Fragment with Executable with Isolable { outer =>
def execute = body()
/**
* match the description of an example with a regular expression.
*
* If the regexp doesn't compile, it is used literally by quoting it. However this regexp is usually passed as the
* Arguments.ex value, it is enclosed with '.*' characters, so they are removed and added back before quotation
*/
override def matches(s: String) = desc.toString.matchesSafely(s, enclosing = ".*")
override def toString = "Example("+desc+")"
override def map(f: Result => Result) = Example(desc, f(body()))
override def equals(a: Any) = {
a match {
case e: Example => desc == e.desc
case _ => false
}
}
/** this fragment can not be executed in a separate specification */
def global = copy(isolable = false)
/** replace the current formatted string with another one */
def formatWith(formatted: FormattedString) = copy(desc = formatted)
/** set a creation path, if not already set, on this example to possibly isolate it during its execution */
private[specs2]
def creationPathIs(path: CreationPath) = copy(creationPath = if (outer.creationPath.isDefined) outer.creationPath else Some(path))
}
case object Example {
def apply[T : AsResult](desc: String, body: =>T) = new Example(FormattedString(desc), () => AsResult(body))
def apply[T : AsResult](fs: FormattedString, body: =>T) = new Example(fs, () => AsResult(body))
}
/**
* An Step creates a fragment that will either return an
* Error Result if there is an exception or a Success.
*
* It is usually used to do some initialisation or cleanup before or after all
* the Fragments.
*
* Note that a Step fragment will not be reported in the output.
*
* @see the ContextSpec specification
*
*/
case class Step(step: LazyParameter[Result], stopOnFail: Boolean = false, location: Location = new Location, isolable: Boolean = true) extends Fragment with Executable with Isolable {
def execute = step.value
override def toString = "Step"
override def map(f: Result => Result) = Step(step map f)
/** this fragment can not be executed in a separate specification */
def global = copy(isolable = false)
// we must override the case class equality to avoid evaluating the step
override def equals(any: Any) = any match {
case s: Step => s eq this
case _ => false
}
// we must override the case class hashCode to avoid evaluating the step
override def hashCode = super.hashCode
}
case object Step extends ImplicitParameters {
/** create a Step object from either a previous result, or a value to evaluate */
def fromEither[T](r: =>Either[Result, T]) = new Step(either(r))
/** empty step doing nothing */
def empty = Step(())
private[specs2]
def either[T](r: =>Either[Result, T]): LazyParameter[Result] = lazyParameter {
r match {
case Left(l) => l
case Right(result: Result) => result
case Right(other) => Success()
}
}
/** create a Step object from any value */
def apply[T](r: =>T) = fromEither(catchAll {
r match {
case r1: Result => r1
case other => DecoratedResult(other, Success())
}
}((e: Throwable) => Error(e)))
/** create a Step object from a boolean condition. Make sure that the boolean evaluation doesn't fail */
def stopOnFail(when: =>Boolean): Step = {
val stop = catchAll(when)(Error(_))
fromEither(stop).copy(stopOnFail = stop.fold(_ => false, b => b))
}
/** always stop on fail */
def stopOnFail: Step = stopOnFail(when = true)
}
/**
* An Action is similar to a Step but can be executed concurrently with other examples.
*
* It is only reported in case of a failure
*/
case class Action (action: LazyParameter[Result] = lazyParameter(Success()),
location: Location = new Location,
isolable: Boolean = true, private[specs2] val creationPath: Option[CreationPath] = None) extends Fragment with Executable with Isolable { outer =>
def execute = action.value
override def toString = "Action"
override def map(f: Result => Result) = Action(action map f)
/** this fragment can not be executed in a separate specification */
def global = copy(isolable = false)
/** set a creation path, if not already set, on this action to possibly isolate it during its execution */
private[specs2] def creationPathIs(path: CreationPath) = copy(creationPath = if (outer.creationPath.isDefined) outer.creationPath else Some(path))
// we must override the case class equality to avoid evaluating the action
override def equals(any: Any) = any match {
case a: Action => a eq this
case _ => false
}
// we must override the case class hashCode to avoid evaluating the action
override def hashCode = super.hashCode
}
case object Action {
/** create an Action object from any value */
def apply[T](r: =>T) = fromEither(trye(r)(Error(_)))
/** create an Action object from either a previous result, or a value to evaluate */
def fromEither[T](r: =>Either[Result, T]) = new Action(Step.either(r))
}
/**
* Those standard Fragments are used to format the specification text:
* - End() can be used to "reset" the indentation of text
* - Br() can be used to insert a newline
* - Tab() can be used to increment the indentation level
* - Backtab() can be used to decrement the indentation level
*/
object StandardFragments {
case class End() extends Fragment { val location = new Location() }
case class Br() extends Fragment { val location = new Location() }
case class Tab(n: Int = 1) extends Fragment { val location = new Location() }
case class Backtab(n: Int = 1) extends Fragment { val location = new Location() }
}
/**
* Those fragments are used to tag other fragments in a specification\
*/
object TagFragments {
trait TagFragment extends Fragment { outer =>
val location = new Location()
def tag: NamedTag
def names: Seq[String] = tag.names
/** @return true if this fragment starts a section */
def isSection: Boolean
/** @return true if this fragment tags the next fragment */
def isTaggingNext: Boolean
/** @return true if the arguments indicate that this tag must be kept */
def keep(args: Arguments): Boolean = tag.keep(args)
/** @return a tag where both "keep" conditions apply and where new names are used for evaluating the "keep" condition */
def overrideWith(other: TagFragment): TagFragment = new TagFragment {
def tag = outer.tag.overrideWith(other.tag)
def isSection = outer.isSection || other.isSection
def isTaggingNext = false
}
def removeNames(otherNames: Seq[String]): TagFragment =
setNames(outer.names.filterNot(otherNames.contains))
def setNames(otherNames: Seq[String]): TagFragment = new TagFragment {
def tag = outer.tag.setNames(otherNames)
def isSection = outer.isSection
def isTaggingNext = false
}
override def equals(o: Any) = {
o match {
case t: TagFragment =>
t.tag == tag &&
isSection == t.isSection &&
isTaggingNext == t.isTaggingNext
case _ => false
}
}
override def toString =
if (isSection) s"Section(${names.distinct.mkString(",")})"
else tag.toString
}
object AlwaysTag extends TagFragment {
def tag = data.AlwaysTag
def isSection = false
def isTaggingNext = true
}
object AlwaysWhenNoIncludeTag extends TagFragment {
def tag = data.AlwaysWhenNoIncludeTag
def isSection = false
def isTaggingNext = true
}
object TaggedAsAlways extends TagFragment {
def tag = data.AlwaysTag
def isSection = false
def isTaggingNext = false
}
object AlwaysSection extends TagFragment {
def tag = data.AlwaysTag
def isSection = true
def isTaggingNext = true
}
object AlwaysAsSection extends TagFragment {
def tag = data.AlwaysTag
def isSection = true
def isTaggingNext = false
}
/** tags the next fragment */
case class Tag(override val names: String*) extends TagFragment {
def tag = data.Tag(names:_*)
def isSection = false
def isTaggingNext = true
}
/** tags the previous fragment */
case class TaggedAs(override val names: String*) extends TagFragment {
def tag = data.Tag(names:_*)
def isSection = false
def isTaggingNext = false
}
/** the previous fragment starts a section */
case class AsSection(override val names: String*) extends TagFragment {
def tag = data.Tag(names:_*)
def isSection = true
def isTaggingNext = false
}
/** the next fragment starts a section */
case class Section(override val names: String*) extends TagFragment {
def tag = data.Tag(names:_*)
def isSection = true
def isTaggingNext = true
}
/**
* define a very coarse Monoid for TaggingFragments where appending 2 TaggingFragments returns a Tag object
* with both list of tags
*/
implicit val TaggingFragmentsAreMonoid = new Monoid[TagFragment] {
val zero: TagFragment = AlwaysWhenNoIncludeTag
def append(t1: TagFragment, t2: =>TagFragment): TagFragment =
if (t1 == zero) t2
else if (t2 == zero) t1
else t1 overrideWith t2
}
/** @return true if the object is a TaggingFragment */
def isTag(f: Fragment) = f match {
case t: TagFragment => true
case other => false
}
}