Skip to content

Instantly share code, notes, and snippets.

@SystemFw
Last active Oct 29, 2020
Embed
What would you like to do?
Using shapeless to check exhaustiveness of Golden tests
// stemming from
// https://gitter.im/typelevel/general?at=5f987f8161007f7d1b9b4b92
// The idea is
// read the JSON files in a directory
// try to decode each of them as Event which could be either One or Two, we don't know this at compile time
// serialize the values we got, again could be either One or Two and compare it with the content of the JSON file
// verify that there was at least a JSON file for One and Two (exhaustivity check)
import shapeless._
trait Check[T] {
type Id
def register(t: T): Id
def check(registered: Set[Id]): Boolean
}
object Check {
def apply[T](implicit ev: Checks[T]): Check[T] = new Check[T] {
type Id = Int
def register(t: T): Id = ev.checks.indexWhere(_.apply(t))
def check(registered: Set[Id]): Boolean =
ev.checks.indices.forall(registered.contains)
}
trait Checks[T] {
def checks: Vector[Any => Boolean]
}
implicit def base: Checks[CNil] =
new Checks[CNil] {
def checks = Vector.empty
}
implicit def inductive[H, T <: Coproduct](
implicit next: Checks[T],
castH: Typeable[H]
): Checks[H :+: T] =
new Checks[H :+: T] {
def checks = (castH.cast(_: Any).isDefined) +: next.checks
}
implicit def generic[T, R <: Coproduct](
implicit ev: Generic.Aux[T, R],
check: Checks[R]
): Checks[T] = new Checks[T] {
def checks = check.checks
}
}
object Ex {
sealed trait A
case class B() extends A
case class C() extends A
def b: A = B()
def c: A = C()
}
object Exhaustive {
val check = Check[Ex.A]
val checks: collection.mutable.Set[check.Id] = collection.mutable.Set()
checks.add(check.register(Ex.b))
checks.add(check.register(Ex.c))
val result = check.check(checks.toSet)
}
object NonExhaustive {
val check = Check[Ex.A]
val checks: collection.mutable.Set[check.Id] = collection.mutable.Set()
checks.add(check.register(Ex.b))
val result = check.check(checks.toSet)
}
// scala> Exhaustive.result
// res0: Boolean = true
// scala> NonExhaustive.result
// res1: Boolean = false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment