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

zio.test.Result.scala Maven / Gradle / Ivy

There is a newer version: 2.1.14
Show newest version
package zio.test

import zio.Chunk
import zio.internal.ansi.AnsiStringOps
import zio.stacktracer.TracingImplicits.disableAutoTrace
import zio.test.TestArrow.Span
import zio.test.ConsoleUtils._
import zio.test.render.LogLine.Message

import scala.annotation.tailrec

sealed trait Result[+A] { self =>
  def isFailOrDie: Boolean = self match {
    case Result.Fail       => true
    case Result.Die(_)     => true
    case Result.Succeed(_) => false
  }

  def isDie: Boolean = self match {
    case Result.Die(_) => true
    case _             => false
  }

  def zipWith[B, C](that: Result[B])(f: (A, B) => C): Result[C] =
    (self, that) match {
      case (Result.Succeed(a), Result.Succeed(b)) => Result.succeed(f(a, b))
      case (Result.Fail, _)                       => Result.fail
      case (_, Result.Fail)                       => Result.fail
      case (Result.Die(err), _)                   => Result.die(err)
      case (_, Result.Die(err))                   => Result.die(err)
    }
}

object Result {
  def succeed[A](value: A): Result[A] = Succeed(value)

  def fail: Result[Nothing] = Fail

  def die(throwable: Throwable): Result[Nothing] = Die(throwable)

  case object Fail                 extends Result[Nothing]
  case class Die(err: Throwable)   extends Result[Nothing]
  case class Succeed[+A](value: A) extends Result[A]
}

case class FailureCase(
  errorMessage: Message,
  codeString: String,
  location: String,
  path: Chunk[(String, String)],
  span: Span,
  nestedFailures: Chunk[FailureCase],
  result: Any,
  customLabel: Option[String]
)

object FailureCase {
  def highlight(
    string: String,
    span: Span,
    parentSpan: Option[Span] = None,
    color: String => String,
    normalColor: String => String = identity
  ): String =
    parentSpan match {
      case Some(Span(pStart, pEnd)) if pStart <= span.start && pEnd >= span.end =>
        val part1 = string.take(pStart)
        val part2 = string.slice(pStart, span.start)
        val part3 = string.slice(span.start, span.end)
        val part4 = string.slice(span.end, pEnd)
        val part5 = string.drop(pEnd)
        part1 + bold(part2) + bold(color(part3)) + bold(part4) + part5
      case _ =>
        bold(normalColor(string.take(span.start))) + bold(color(string.slice(span.start, span.end))) + bold(
          normalColor(string.drop(span.end))
        )
    }

  @tailrec
  def rightmostNode(trace: TestTrace[Boolean]): TestTrace.Node[Boolean] = trace match {
    case node: TestTrace.Node[Boolean] => node
    case TestTrace.AndThen(_, right)   => rightmostNode(right)
    case TestTrace.And(_, right)       => rightmostNode(right)
    case TestTrace.Or(_, right)        => rightmostNode(right)
    case TestTrace.Not(trace)          => rightmostNode(trace)
  }

  def getPath(trace: TestTrace[_]): Chunk[(String, String)] =
    trace match {
      case node: TestTrace.Node[_] =>
        Chunk(node.code -> PrettyPrint(node.renderResult))
      case TestTrace.AndThen(left, right) =>
        getPath(left) ++ getPath(right)
      case _ => Chunk.empty
    }

  def fromTrace(trace: TestTrace[Boolean], path: Chunk[(String, String)]): Chunk[FailureCase] =
    trace match {
      case node: TestTrace.Node[Boolean] =>
        Chunk(fromNode(node, path.reverse))
      case TestTrace.AndThen(left, right) =>
        fromTrace(right, path ++ getPath(left))
      case TestTrace.And(left, right) =>
        fromTrace(left, path) ++ fromTrace(right, path)
      case TestTrace.Or(left, right) =>
        fromTrace(left, path) ++ fromTrace(right, path)
      case TestTrace.Not(trace) =>
        fromTrace(trace, path)
    }

  private def fromNode(node: TestTrace.Node[Boolean], path: Chunk[(String, String)]): FailureCase = {
    val color = node.result match {
      case Result.Die(_) => red _
      case _             => yellow _
    }

    FailureCase(
      errorMessage = node.message.render(node.isSuccess),
      codeString = {
        node.completeCode match {
          case Some(completeCode) =>
            val idx = completeCode.indexOf(node.code)
            if (idx >= 0 && node.code.nonEmpty) {
              highlight(completeCode, Span(idx, idx + node.code.length), None, color, (_: String).cyan)
            } else {
              completeCode
            }
          case None =>
            highlight(node.fullCode.getOrElse(""), node.span.getOrElse(Span(0, 0)), node.parentSpan, color)
        }
      },
      location = node.location.getOrElse(""),
      path = path,
      span = node.span.getOrElse(Span(0, 0)),
      nestedFailures = node.children.map(fromTrace(_, Chunk.empty)).getOrElse(Chunk.empty),
      result = node.result,
      customLabel = node.customLabel
    )
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy