
scalaprops.Shrink.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scalaprops-core_native0.5_3 Show documentation
Show all versions of scalaprops-core_native0.5_3 Show documentation
property based testing library for Scala
The newest version!
package scalaprops
import java.math.BigInteger
import scala.reflect.ClassTag
final class Shrink[A](val f: A => Stream[A]) {
def apply(a: A): Stream[A] = f(a)
def xmap[B](x: A => B, y: B => A): Shrink[B] =
Shrink.shrink(y andThen f andThen (_.map(x)))
}
object Shrink {
def apply[A](implicit A: Shrink[A]): Shrink[A] = A
def shrink[A](f: A => Stream[A]): Shrink[A] =
new Shrink(f)
private[this] val Empty = shrink[Any](_ => Stream.Empty)
def empty[A]: Shrink[A] = Empty.asInstanceOf[Shrink[A]]
private[this] def longMin(min: Long): Shrink[Long] =
shrink {
case 0L => Stream.Empty
case i =>
val is = 0L #:: Stream.iterate(i)(_ / 2L).takeWhile(_ != 0L).map(i - _)
if (min < i && i < 0L) {
is
} else {
is
}
}
implicit val long: Shrink[Long] = longMin(Long.MinValue)
implicit val boolean: Shrink[Boolean] =
shrink {
case true => Stream(false)
case false => Stream.empty
}
implicit val int: Shrink[Int] =
longMin(Int.MinValue).xmap(_.toInt, x => x)
implicit val short: Shrink[Short] =
longMin(Short.MinValue).xmap(_.toShort, x => x)
implicit val byte: Shrink[Byte] =
longMin(Byte.MinValue).xmap(_.toByte, x => x)
implicit def option[A](implicit A: Shrink[A]): Shrink[Option[A]] =
shrink {
case None => Stream.Empty
case Some(a) => None #:: A(a).map(Option(_))
}
implicit def either[A, B](implicit A: Shrink[A], B: Shrink[B]): Shrink[A Either B] =
shrink {
case Left(a) =>
A(a).map(Left(_))
case Right(a) =>
B(a).map(Right(_))
}
implicit def list[A](implicit A: Shrink[A]): Shrink[List[A]] = {
def interleave[B](s1: Stream[B], s2: Stream[B]): Stream[B] =
if (s1.isEmpty) s2
else s1.head #:: interleave(s2, s1.tail)
def removeChunks(n: Int, as: List[A]): Stream[List[A]] =
as match {
case Nil =>
Stream.Empty
case _ :: Nil =>
Stream.cons(Nil, Stream.Empty)
case _ =>
val n1 = n / 2
val n2 = n - n1
val as1 = as.take(n1)
Stream.cons(
as1, {
val as2 = as.drop(n1)
Stream.cons(
as2,
interleave(
removeChunks(n1, as1).withFilter(_.nonEmpty).map(_ ::: as2),
removeChunks(n2, as2).withFilter(_.nonEmpty).map(as1 ::: _)
)
)
}
)
}
def shrinkOne(as: List[A]): Stream[List[A]] =
as match {
case h :: t =>
A(h).map(_ :: t) #::: shrinkOne(t).map(h :: _)
case _ =>
Stream.Empty
}
shrink(as => removeChunks(as.length, as) #::: shrinkOne(as))
}
implicit def stream[A: Shrink]: Shrink[Stream[A]] = {
Shrink[List[A]].xmap(_.toStream, _.toList)
}
implicit def tuple2[A1, A2](implicit A1: Shrink[A1], A2: Shrink[A2]): Shrink[(A1, A2)] =
shrink { case (a1, a2) =>
for {
x1 <- A1(a1)
x2 <- A2(a2)
} yield (x1, x2)
}
implicit def tuple3[A1, A2, A3](implicit A1: Shrink[A1], A2: Shrink[A2], A3: Shrink[A3]): Shrink[(A1, A2, A3)] =
shrink { case (a1, a2, a3) =>
for {
x1 <- A1(a1)
x2 <- A2(a2)
x3 <- A3(a3)
} yield (x1, x2, x3)
}
implicit def tuple4[A1, A2, A3, A4](implicit
A1: Shrink[A1],
A2: Shrink[A2],
A3: Shrink[A3],
A4: Shrink[A4]
): Shrink[(A1, A2, A3, A4)] =
shrink { case (a1, a2, a3, a4) =>
for {
x1 <- A1(a1)
x2 <- A2(a2)
x3 <- A3(a3)
x4 <- A4(a4)
} yield (x1, x2, x3, x4)
}
implicit def map[A, B](implicit A: Shrink[A], B: Shrink[B]): Shrink[Map[A, B]] = {
Shrink[List[(A, B)]].xmap(
_.foldLeft(Map.newBuilder[A, B])(_ += _).result(),
_.foldRight(List.empty[(A, B)])(_ :: _)
)
}
implicit def array[A: Shrink](implicit A: ClassTag[A]): Shrink[Array[A]] = {
def list2array(list: List[A]) = {
val array = new Array[A](list.length)
@annotation.tailrec
def loop(xs: List[A], i: Int): Unit =
xs match {
case h :: t =>
array(i) = h
loop(t, i + 1)
case _ =>
}
loop(list, 0)
array
}
Shrink[List[A]].xmap(list2array, _.toList)
}
implicit val bigInt: Shrink[BigInt] =
Shrink.shrink[BigInt] {
case i if i == 0 => Stream.empty
case i =>
val is = BigInt(0) #:: Stream.iterate(i)(_ / 2).takeWhile(_ != 0).map(i - _)
if (i < 0) {
-i #:: is
} else {
is
}
}
implicit val bigInteger: Shrink[BigInteger] =
bigInt.xmap(_.bigInteger, BigInt(_))
implicit def cogenShrink[A: Gen: Cogen]: Cogen[Shrink[A]] =
Cogen[A => Stream[A]].contramap(_.f)
implicit def shrinkGen[A: Cogen: Gen]: Gen[Shrink[A]] =
Gen[A => Stream[A]].map(new Shrink(_))
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy