scalaprops.Shrink.scala Maven / Gradle / Ivy
package scalaprops
import java.math.BigInteger
import scala.reflect.ClassTag
import scalaz._
import scalaz.std.stream._
import scalaz.Isomorphism._
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)))
def xmapi[B](f: A <=> B): Shrink[B] =
xmap(f.to, f.from)
}
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]]
implicit val shrinkInstance: InvariantFunctor[Shrink] =
new InvariantFunctor[Shrink] {
def xmap[A, B](ma: Shrink[A], f: A => B, g: B => A) =
ma.xmap(f, g)
}
implicit val long: Shrink[Long] =
shrink{
case 0L => Stream.Empty
case i =>
val is = 0L #:: Stream.iterate(i)(_ / 2L).takeWhile(_ != 0L).map(i - _)
if(i < 0L){
-i #:: is
} else {
is
}
}
implicit val boolean: Shrink[Boolean] =
shrink(_ => Stream.cons(false, Stream.Empty))
implicit val int: Shrink[Int] =
long.xmap(_.toInt, x => x)
implicit val short: Shrink[Short] =
long.xmap(_.toShort, x => x)
implicit val byte: Shrink[Byte] =
long.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 disjunction[A, B](implicit A: Shrink[A], B: Shrink[B]): Shrink[A \/ B] =
shrink{
case -\/(a) => A(a).map(\/.left)
case \/-(a) => B(a).map(\/.right)
}
implicit def either[A: Shrink, B: Shrink]: Shrink[A Either B] =
Shrink[A \/ B].xmap(_.toEither, \/.fromEither)
implicit def ilist[A](implicit A: Shrink[A]): Shrink[IList[A]] = {
def removeChunks(n: Int, as: IList[A]): Stream[IList[A]] =
as match {
case INil() =>
Stream.Empty
case ICons(a, INil()) =>
Stream.cons(INil(), 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,
std.stream.interleave(
removeChunks(n1, as1).withFilter(_.nonEmpty).map(_ ::: as2),
removeChunks(n2, as2).withFilter(_.nonEmpty).map(as1 ::: _)
)
)
}
)
}
def shrinkOne(as: IList[A]): Stream[IList[A]] = as match {
case INil() => Stream.Empty
case ICons(h, t) =>
(A(h).map(_ :: t) ++ shrinkOne(t)).map(h :: _)
}
shrink(as => removeChunks(as.length, as) #::: shrinkOne(as))
}
implicit def list[A: Shrink]: Shrink[List[A]] =
Shrink[IList[A]].xmap(_.toList, Gen.IListFromList)
implicit def stream[A: Shrink]: Shrink[Stream[A]] = {
Shrink[IList[A]].xmap(_.toStream, IList.fromFoldable(_))
}
implicit def tuple2[A1, A2](implicit A1: Shrink[A1], A2: Shrink[A2]): Shrink[(A1, A2)] =
shrink{ case (a1, a2) =>
Apply[Stream].tuple2(A1(a1), A2(a2))
}
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) =>
Apply[Stream].tuple3(A1(a1), A2(a2), A3(a3))
}
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) =>
Apply[Stream].tuple4(A1(a1), A2(a2), A3(a3), A4(a4))
}
implicit def map[A, B](implicit A: Shrink[A], B: Shrink[B]): Shrink[Map[A, B]] = {
import syntax.foldable._
Shrink[IList[(A, B)]].xmap(
_.to[({type l[_] = Map[A, B]})#l],
_.foldRight(IList.empty[(A, B)])(_ :: _)
)
}
implicit def array[A: Shrink](implicit A: ClassTag[A]): Shrink[Array[A]] = {
def ilist2array(list: IList[A]) = {
val array = new Array[A](list.length)
@annotation.tailrec
def loop(xs: IList[A], i: Int): Unit = xs match {
case ICons(h, t) =>
array(i) = h
loop(t, i + 1)
case INil() =>
}
loop(list, 0)
array
}
Shrink[IList[A]].xmap(ilist2array, array => IList(array: _*))
}
implicit val bigInteger: Shrink[BigInteger] =
Shrink[(Byte, Array[Byte])].xmap(
bs => {
val x = new Array[Byte](bs._2.length - 1)
var i = 0
while(i < bs._2.length){
x(i) = bs._2.apply(i)
i += 1
}
x(bs._2.length) = bs._1
new BigInteger(x)
},
i => {
val b = i.toByteArray
val x = new Array[Byte](b.length - 1)
System.arraycopy(b, 0, x, 0, b.length - 1)
(b(0), x)
}
)
implicit val bigInt: Shrink[BigInt] =
Shrink[BigInteger].xmap(BigInt(_), _.bigInteger)
val shrinkFunctionIso: Shrink <~> ({type l[a] = a => Stream[a]})#l =
new IsoFunctorTemplate[Shrink, ({type l[a] = a => Stream[a]})#l] {
def to[A](fa: Shrink[A]) = fa.f
def from[A](ga: A => Stream[A]) = new Shrink(ga)
}
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