Skip to content

Commit

Permalink
Allow unit tests which assure that some code does not type-check.
Browse files Browse the repository at this point in the history
This consists of a ShouldNotTypecheck macro which does the actual work,
and a modification of the build process to delete the output from these
tests after compiling (because they should be recompiled every time).

(cherry picked from commit 1fe9863)
  • Loading branch information
szeiger committed Oct 31, 2013
1 parent ff992bb commit f16915d
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 1 deletion.
9 changes: 8 additions & 1 deletion project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,14 @@ object SlickBuild extends Build {
// Workaround for sbt bug: Without a testGrouping for all test configs,
// the wrong tests are run
testGrouping in DocTest <<= definedTests in DocTest map partitionTests,
parallelExecution in Test := false
parallelExecution in Test := false,
compile in Test ~= { a =>
// Delete classes in "compile" packages after compiling.
// These are used for compile-time tests and should be recompiled every time.
val products = a.relations.allProducts.toSeq ** new SimpleFileFilter(_.getParentFile.getName == "compile")
IO.delete(products.get)
a
}
) ++ ifPublished(Seq(
libraryDependencies <+= scalaVersion("org.scala-lang" % "scala-compiler" % _ % "test")
))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.typesafe.slick.testkit.util

import scala.language.experimental.macros
import scala.reflect.macros.{Context, TypecheckException}
import scala.util.control.NonFatal
import java.util.regex.Pattern

/**
* A macro that ensures that a code snippet does not typecheck.
*/
object ShouldNotTypecheck {
def apply(code: String): Unit = macro applyImplNoExp
def apply(code: String, expected: String): Unit = macro applyImpl

def applyImplNoExp(ctx: Context)(code: ctx.Expr[String]) = applyImpl(ctx)(code, null)

def applyImpl(ctx: Context)(code: ctx.Expr[String], expected: ctx.Expr[String]): ctx.Expr[Unit] = {
import ctx.universe._

val Expr(Literal(Constant(codeStr: String))) = code
val (expPat, expMsg) = expected match {
case null => (null, "Expected some error.")
case Expr(Literal(Constant(s: String))) =>
(Pattern.compile(s, Pattern.CASE_INSENSITIVE), "Expected error matching: "+s)
}

try ctx.typeCheck(ctx.parse("{ "+codeStr+" }")) catch { case e: TypecheckException =>
val msg = e.getMessage
if((expected ne null) && !(expPat.matcher(msg)).matches)
ctx.abort(ctx.enclosingPosition, "Type-checking failed in an unexpected way.\n"+
expMsg+"\nActual error: "+msg)
else return reify(())
}

ctx.abort(ctx.enclosingPosition, "Type-checking succeeded unexpectedly.\n"+expMsg)
}
}

0 comments on commit f16915d

Please sign in to comment.