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

cgta.otest.runner.LoggerHelp.scala Maven / Gradle / Ivy

package cgta.otest
package runner

import sbt.testing.Logger
import cgta.otest.runner.TestResults.{FailedWithEitherTrace, FailedFatalException, FailedUnexpectedException, FailedAssertion, FailedBad}
import scala.collection.mutable.ListBuffer
import scala.annotation.tailrec


//////////////////////////////////////////////////////////////
// Copyright (c) 2014 Ben Jackman, Jeff Gomberg
// All Rights Reserved
// please contact [email protected] or [email protected]
// for licensing inquiries
// Created by bjackman @ 5/28/14 10:49 AM
//////////////////////////////////////////////////////////////

object LoggerHelp {
  lazy val isScalaJS = {
    try {
      1/0
      true
    } catch {
      case e : ArithmeticException => false
    }
  }

  def trace(e: Throwable): List[String] = {
    val lb = new ListBuffer[Either[String, StackTraceElement]]
    @tailrec
    def loop(e: Throwable) {
      lb += Left(e.getClass + ": " + e.getMessage)
      lb ++= e.getStackTrace.map(Right(_))
      val cause = e.getCause
      if (cause != null) loop(cause)
    }
    loop(e)
    trace(lb.toList)
  }

  def trace(t: Seq[Either[String, StackTraceElement]]): List[String] = {
    val lb = new ListBuffer[String]
    var first = true
    var passedAsserts = false
    @tailrec
    def loop(itr: Iterator[Either[String, StackTraceElement]]) {
      if (itr.hasNext) {
        val cur = itr.next()
        cur match {
          case Left(msg) =>
            lb += (if (first) "Exception " else "Caused by: ") + msg
            first = false
            loop(itr)
          case Right(ste) =>
            val steStr = ""+ste
            if (isScalaJS) {
              lb += "  at " + steStr
              loop(itr)
            } else {
              val isCgtaOtestCode = steStr.startsWith("cgta.otest.")
              if (passedAsserts) {
                if (isCgtaOtestCode) {
                  //Back into the guts of the runner, therefore stop printing the trace
                } else {
                  lb += "  at " + steStr
                  loop(itr)
                }
              } else {
                if (isCgtaOtestCode) {
                  //Not yet out of the assertion exception areas
                  loop(itr)
                } else {
                  passedAsserts = true
                  lb += "  at " + steStr
                  loop(itr)
                }
              }
            }
        }
      }
    }
    loop(t.iterator)
    lb.toList
  }


  def logResults(name: String, loggers: Array[Logger], results: Seq[TestResult]) {
    loggers.map(ColorLogger).foreach { logger =>
      val trimmedName = if (name.endsWith("$")) name.dropRight(1) else name
      val hasIgnoreOnly = results.exists(_.isIgnoredOnly)
      val ignoreOnlyMsg = if (hasIgnoreOnly) "(Ignored some test b/c testOnly was used)" else ""
      val msg = s"$trimmedName:$ignoreOnlyMsg [${results.map(_.duration()).sum}ms]"
      if (results.exists(_.isFailed)) {
        logger.redError(msg)
      } else if(hasIgnoreOnly)  {
        logger.yellowInfo(msg)
      } else  {
        logger.logger.info(msg)
      }
      results.foreach {
        case r: TestResults.Passed => logger.green(s"[${r.duration}ms] - ${r.name}")
        case r: TestResults.Ignored if r.becauseOnly =>
          //No logging here
        case r: TestResults.Ignored =>
          logger.yellowInfo(s"- ${r.name} !!! IGNORED !!!")
        case r: TestResults.Failed =>
          logger.redError(s"- ${r.name} *** FAILED ***")

          def logException(e: Throwable) {
            logTraceStr(trace(e))
          }
          def logEitherTrace(t: Seq[Either[String, StackTraceElement]]) {
            logTraceStr(trace(t))
          }
          def logTraceStr(t: Seq[String]) {
            t.foreach { line =>
              logger.redError("  " + line)
            }
          }

          r match {
            case f: FailedBad =>
              //Failed bad tests are actually expected to fail
            case f: FailedAssertion =>
              f.e match {
                case e : SimpleAssertionException =>
                  val acStr = ""+e.actual
                  val exStr = ""+e.expected
                  logTraceStr(trace(e.getStackTrace.map(Right(_))))
                  logger.redError(s"Expected `A` ${e.op} `B`")
                  logger.redError(s"A: $exStr")
                  logger.redError(s"B: $acStr")
                  if (e.clues.nonEmpty) {
                    logger.redError("Clues:")
                    e.clues.foreach(c => logger.redError(""+c))
                  }
                case e =>
                  logException(e)
              }
            case f: FailedUnexpectedException => logException(f.e)
            case f: FailedFatalException => logException(f.e)
            case f: FailedWithEitherTrace => logEitherTrace(f.trace)
          }

      }
    }
  }

}

case class ColorLogger(logger: Logger) {
  def format(color: String, msg: String): String = {
    if (color.nonEmpty) {
      var m = ""
      if (logger.ansiCodesSupported()) {
        m += color
      }
      m += msg
      if (logger.ansiCodesSupported()) {
        m += Console.RESET
      }
      m
    } else {
      msg
    }
  }
  def redError(msg: String) { logger.error(format(color = Console.RED, msg = msg)) }
  def green(msg: String) { logger.info(format(color = Console.GREEN, msg = msg)) }
  def yellowInfo(msg: String) { logger.info(format(color = Console.YELLOW, msg = msg)) }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy