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

org.specs2.io.Location.scala Maven / Gradle / Ivy

There is a newer version: 3.7
Show newest version
package org.specs2
package io

/**
 * Location of a Fragment in a file.
 *
 * This class behavior is highly dependent on method calls in the FragmentsBuilder trait.
 *
 * Depending on the method that's called to create a fragment or to "link" it to another one, we have to do ad-hoc stacktrace
 * manipulations to determine the exact location of a Fragment.
 */
class Location {
  private val location = FromSource.location(fragmentFilter)
  def file: String = location.fileName
  def lineNumber: Int = location.lineNumber
  def classLocation: String = location.classLocation
  override def toString = location.fullLocation
  override def equals(a: Any) = a match {
    case l: Location => l.toString == this.toString
    case other       => false
  }

  private def isFragmentDefinition(s: String) =
    Seq("Described",
        "InExample"
  ).map("org.specs2.mutable.FragmentsBuilder$"+_).exists(s.contains)

  private def fragmentFilter = (st: Seq[StackTraceElement]) => {
    // for a mutable specification we drop the stacktrace until there are fragment
    // definitions then we drop the definition calls
    if (isMutableSpecification(st))
      st.dropWhile(s => !isFragmentDefinition(s.toString)).
         dropWhile(s => isFragmentDefinition(s.toString))
    // for an acceptance specification we have to drop some stacktrace element and adjust the line number of the method calls
    // to find out the exact location of a fragment
    else {
      val fragmentsMethods = Seq("org.specs2", ".textStart", ".textFragment", ".fragmentsFragments", ".p(", ".br(", ".t(", ".bt(", ".end(", ".endp(", ".endbr(")
      val (start, end) = st.span(s => fragmentsMethods.exists(s.toString.contains))
      // 145 is the magic line number of the "FragmentsFragment::def ^(t: String) = fragments add Text(t)" method
      if (lastContains(start, "$up(FragmentsFragment.scala:12") && !isTextStart(start)) takeHead(end, st(0), lineOffset = 1)
      else if (lastContains(start, "$up"))                                              takeHead(end, st(0))
      else                                                                              end
    }
  }

  private def isTextStart(st: Seq[StackTraceElement]) = st.exists(_.toString.contains(".textStart"))
  private def lastContains(st: Seq[StackTraceElement], method: String) = st.lastOption.map(_.toString.contains(method)).getOrElse(false)
  private def takeHead(st: Seq[StackTraceElement], defaultValue: StackTraceElement, lineOffset: Int = 0) = {
    val stElement = st.headOption.getOrElse(defaultValue)
    Seq(new StackTraceElement(stElement.getClassName, stElement.getMethodName, stElement.getFileName, stElement.getLineNumber+lineOffset))
  }

  private def isMutableSpecification(st: Seq[StackTraceElement]) = st.exists(_.toString.contains("org.specs2.mutable.FragmentsBuilder"))
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy