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

weaver.TestErrorFormatter.scala Maven / Gradle / Ivy

The newest version!
package weaver

import cats.data.NonEmptyVector

object TestErrorFormatter {

  def formatStackTrace(
      ex: Throwable,
      traceLimit: Option[Int]): Vector[String] = {
    val (traceElements, truncated) = {
      val tr = ex.getStackTrace.toVector

      traceLimit.fold(tr -> false) { limit =>
        if (tr.length <= limit) tr -> false
        else if (limit == 0) Vector[StackTraceElement]() -> false
        else tr.take(limit)                              -> true
      }
    }

    val newStackTrace = groupStackTraceElements(traceElements)

    newStackTrace match {
      case Nil => Vector()
      case h :: t =>
        renderGroupedStackTrace(NonEmptyVector.of(h, t: _*), truncated)
    }
  }

  private sealed trait TraceOutput
  private case class Element(st: StackTraceElement) extends TraceOutput {
    def location: String = s"${st.getFileName}:${st.getLineNumber}"
  }
  private case class Snip(pack: String) extends TraceOutput

  private def exclusion: StackTraceElement => Option[String] = { el =>
    val exclusions = Set(
      "cats.effect.internals",
      "java.util.concurrent",
      "zio.internal",
      "java.lang.Thread"
    )

    exclusions.find(el.toString.contains)
  }

  private def groupStackTraceElements(
      elements: Vector[StackTraceElement]): List[TraceOutput] = {
    val traces = new scala.collection.mutable.ListBuffer[TraceOutput]
    var latest: Option[TraceOutput] = None

    def append(el: TraceOutput) = {
      traces.append(el); latest = Some(el)
    }

    elements.map(el => el -> exclusion(el)).foreach {
      case (el, exclusion) =>
        (latest, exclusion) match {
          case (Some(Snip(pack)), Some(excl)) =>
            if (pack != excl) append(Snip(excl))
          case (_, None) =>
            append(Element(el))
          case (None, Some(excl)) =>
            append(Snip(excl))
          case (Some(_: Element), Some(excl)) =>
            append(Snip(excl))
        }
    }

    traces.toList
  }

  private def renderGroupedStackTrace(
      stackTrace: NonEmptyVector[TraceOutput],
      truncated: Boolean): Vector[String] = {

    import cats.syntax.all._

    val (snipPrefix, snipSuffix) = ("", ".<...>")

    val sources = stackTrace.map {
      case el: Element => el.location
      case _: Snip     => snipPrefix
    }

    val maxLen = sources.map(_.length).toList.max

    val formatted = stackTrace.map {
      case el: Element =>
        s"${el.location.padTo(maxLen + 4, ' ')}${el.st.getClassName}#${el.st.getMethodName}"
      case Snip(pack) =>
        s"${snipPrefix.padTo(maxLen + 4, ' ')}$pack$snipSuffix"
    }

    val truncatedMaybe = if (truncated) Vector("...") else Vector()

    formatted.toVector ++ truncatedMaybe
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy