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

sntb.TestProvider.scala Maven / Gradle / Ivy

There is a newer version: 0.3
Show newest version
package sntb

import language.experimental.macros
import language.implicitConversions
import reflect.ClassTag
import reflect.macros.blackbox.Context
import reflect.macros.{ParseException, TypecheckException}
import util.{Failure, Success, Try}

trait TestProvider {

  private var runList: List[Block] = Nil

  private val className = this.getClass.getSimpleName()

  def assertCompiles(str: String): Unit = macro TestProvider.assertCompilesImpl

  def assertTypeError(str: String): Unit = macro TestProvider.assertTypeErrorImpl

  object it {
    def should(name: String): BlockBuilder = BlockBuilder(name)
  }

  def intercept[T <: RuntimeException](arg: => Any)(implicit ct: ClassTag[T]): T =
    try {
      arg
      throw new RuntimeException("Value was not an error")
    } catch {
      case x: T => x
      case _: Throwable => throw new RuntimeException(s"Error was not an instance of the expected type")
    }

  def run: Unit = {
    println(s"${Console.GREEN}$className: ${Console.RESET}")
    runList.reverse foreach {
      case Block(name, exec) =>
        print(s"- $name...")
        val initTime = System.currentTimeMillis
        exec()
        val endTime = System.currentTimeMillis
        println(s"done (${TestProvider.formatTime(endTime - initTime)}) ")
    }
  }

  case class BlockBuilder(name: String) {
    def in(exec: => Unit): Unit = runList = new Block(name, () => exec) :: runList
  }

  private case class Block(name: String, exec: Function0[Unit])

  final class TSEQ[T](lhs: T) {
    def ===(rhs: T): Boolean = lhs == rhs
  }

  implicit def tseqBuilder[T](lhs: T): TSEQ[T] = new TSEQ(lhs)

}

object TestProvider {

  private def formatTime(millis: Long): String = {
    var result = ""
    var opr = millis
    val min = opr / 60000l
    if (min > 0) {
      if (min == 1) {
        result = "1 minute"
      } else {
        result = s"$min minutes"
      }
    }
    opr %= 60000
    val sec = opr / 1000l
    if (sec > 0) {
      val spacer = if (result.isEmpty) "" else " "
      if (sec == 1) {
        result += s"${spacer}1 second"
      } else {
        result += s"$spacer$sec seconds"
      }
    }
    opr %= 1000
    val spacer = if (result.isEmpty) "" else " "
    if (opr == 1) {
      result + s"${spacer}1 millisecond"
    } else {
      result + s"${spacer}$opr milliseconds"
    }
  }

  def assertCompilesImpl(c: Context)(str: c.Tree): c.Tree = {
    import c.universe._
    str match {
      case q"${y: String}" =>
        util.Try {
          val parsed = c.parse(y)
          val typechecked = c.typecheck(parsed)
          true
        } match {
          case util.Success(_) => q"assert(true)"
          case util.Failure(TypecheckException(_, msg)) => q"""assert(false, "typecheck failure: " + $msg)"""
          case util.Failure(ParseException(_, msg)) => q"""assert(false, "parse failure: " + $msg)"""
          case util.Failure(err) => q"assert(false, error: ${err.toString})"
        }
      case _ => c.abort(c.enclosingPosition, "Supplied Literal must be a string")
    }
  }

  def assertTypeErrorImpl(c: Context)(str: c.Tree): c.Tree = {
    import c.universe._
    str match {
      case q"${y: String}" =>
        util.Try {
          val parsed = c.parse(y)
          val typechecked = c.typecheck(parsed)
        } match {
          case util.Failure(TypecheckException(_, _)) => q"assert(true)"
          case util.Failure(ParseException(_, msg)) => q"""assert(false, "parse failure: " + $msg)"""
          case util.Success(_) => q"""assert(false, "code compiles")"""
          case util.Failure(err) => q"assert(false, error: ${err.toString})"
        }
      case _ => c.abort(c.enclosingPosition, "Supplied Literal must be a string")
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy