scalacheck-shapeless
scalacheck-shapeless copied to clipboard
ClassCastException shrinking value class
I'm using https://github.com/fthomas/refined and I was pretty sure it had been working fine but today I got a ClassCastException
when shrinking a case class that contained a field which was a Refined
type. My guess is it has something to do with it being a value class.
Here is the exception:
Caused by: java.lang.ClassCastException: java.lang.Integer cannot be cast to eu.timepit.refined.api.Refined
at com.geneious.nucleus.service.job.api.generators.JobStatusGenerators$anon$macro$381$1.from(JobStatusGenerators.scala:20)
at com.geneious.nucleus.service.job.api.generators.JobStatusGenerators$anon$macro$381$1.from(JobStatusGenerators.scala:20)
at org.scalacheck.derive.MkShrink$.$anonfun$genericProduct$2(MkShrink.scala:41)
at scala.collection.immutable.Stream.map(Stream.scala:415)
at org.scalacheck.derive.MkShrink$.$anonfun$lazyxmap$1(MkShrink.scala:32)
at org.scalacheck.Shrink$$anon$1.shrink(Shrink.scala:40)
at org.scalacheck.derive.MkHListShrink$.$anonfun$hcons$2(MkShrink.scala:88)
at org.scalacheck.Shrink$$anon$1.shrink(Shrink.scala:40)
at org.scalacheck.derive.MkHListShrink$.$anonfun$hcons$4(MkShrink.scala:89)
at scala.collection.immutable.Stream.append(Stream.scala:252)
at scala.collection.immutable.Stream$ConsWrapper.$hash$colon$colon$colon(Stream.scala:1126)
at org.scalacheck.derive.MkHListShrink$.$anonfun$hcons$2(MkShrink.scala:88)
at org.scalacheck.Shrink$$anon$1.shrink(Shrink.scala:40)
at org.scalacheck.derive.MkHListShrink$.$anonfun$hcons$4(MkShrink.scala:89)
at scala.collection.immutable.Stream.append(Stream.scala:252)
at scala.collection.immutable.Stream$ConsWrapper.$hash$colon$colon$colon(Stream.scala:1126)
at org.scalacheck.derive.MkHListShrink$.$anonfun$hcons$2(MkShrink.scala:88)
at org.scalacheck.Shrink$$anon$1.shrink(Shrink.scala:40)
at org.scalacheck.derive.MkHListShrink$.$anonfun$hcons$4(MkShrink.scala:89)
at scala.collection.immutable.Stream.append(Stream.scala:252)
at scala.collection.immutable.Stream$ConsWrapper.$hash$colon$colon$colon(Stream.scala:1126)
at org.scalacheck.derive.MkHListShrink$.$anonfun$hcons$2(MkShrink.scala:88)
at org.scalacheck.Shrink$$anon$1.shrink(Shrink.scala:40)
at org.scalacheck.derive.MkShrink$.$anonfun$lazyxmap$1(MkShrink.scala:32)
at org.scalacheck.Shrink$$anon$1.shrink(Shrink.scala:40)
at org.scalacheck.Shrink$.shrink(Shrink.scala:44)
at org.scalacheck.Prop$.$anonfun$forAll$16(Prop.scala:913)
at org.scalacheck.Prop$.shrinker$1(Prop.scala:778)
at org.scalacheck.Prop$.$anonfun$forAllShrink$1(Prop.scala:802)
at org.scalacheck.Prop$.$anonfun$apply$1(Prop.scala:307)
at org.scalacheck.PropFromFun.apply(Prop.scala:22)
at org.scalacheck.Prop$.result$1(Prop.scala:762)
at org.scalacheck.Prop$.$anonfun$forAllShrink$1(Prop.scala:800)
at org.scalacheck.Prop$.$anonfun$apply$1(Prop.scala:307)
at org.scalacheck.PropFromFun.apply(Prop.scala:22)
at org.scalacheck.Test$.workerFun$1(Test.scala:326)
at org.scalacheck.Test$.$anonfun$check$1(Test.scala:355)
at org.scalacheck.Test$.$anonfun$check$1$adapted(Test.scala:355)
at org.scalacheck.Platform$.runWorkers(Platform.scala:40)
at org.scalacheck.Test$.check(Test.scala:355)
at org.scalatest.enablers.UnitCheckerAsserting$CheckerAssertingImpl.check(CheckerAsserting.scala:89)
... 53 more
Here is the relevant code:
sealed trait JobStatus {
def kind: String
def dateTime: OffsetDateTime
def messages: immutable.Seq[String]
def progress: Percentage
}
final case class Running(dateTime: OffsetDateTime,
messages: immutable.Seq[String],
progress: Percentage) extends JobStatus {
@transient
override val kind: String = Running.Kind
}
object CommonRefinedTypes {
type Percentage = Int Refined Interval.Closed[W.`0`.T, W.`100`.T]
object Percentage extends RefinedTypeOps.Numeric[Percentage, Int]
}
object CommonRefinedTypeGenerators {
implicit val arbPercentage: Arbitrary[Percentage] = numeric.intervalClosedArbitrary
implicit val shrinkPercentage: Shrink[Percentage] = shrinkFrom(Percentage)
private def shrinkFrom[A <: Refined[B, _], B: Shrink](ops: RefinedTypeOps[A, B]): Shrink[A] = Shrink { a =>
shrink(a.value).flatMap { shrunk =>
ops.from(shrunk).fold(_ => Stream.empty, Stream(_))
}
}
}
import com.geneious.nucleus.service.util.generators.CommonRefinedTypeGenerators._
trait JobStatusGenerators {
implicit val arbRunningJobStatus: Arbitrary[Running] = MkArbitrary[Running].arbitrary
implicit val shrinkRunningJobStatus: Shrink[Running] = MkShrink[Running].shrink
}
If I inline shrinkFrom
then it works. Not too sure what is going on there.