Last active
October 29, 2020 21:47
-
-
Save SystemFw/1038a0ba297760efca946bbe5c1650bd to your computer and use it in GitHub Desktop.
Using shapeless to check exhaustiveness of Golden tests
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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