Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 13 additions & 17 deletions test-tests/shared/src/test/scala/zio/test/laws/LawsFSpec.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package zio.test.laws

import zio.random.Random
import zio.test._

object LawsFSpec extends ZIOBaseSpec {
Expand Down Expand Up @@ -55,12 +54,6 @@ object LawsFSpec extends ZIOBaseSpec {
}
}

val OptionGenF: GenF[Random, Option] =
new GenF[Random, Option] {
def apply[R1 <: Random, A](gen: Gen[R1, A]): Gen[R1, Option[A]] =
Gen.option(gen)
}

trait Covariant[F[+_]] {
def map[A, B](f: A => B): F[A] => F[B]
}
Expand All @@ -70,17 +63,20 @@ object LawsFSpec extends ZIOBaseSpec {
def apply[F[+_]](implicit covariant: Covariant[F]): Covariant[F] =
covariant

val identityLaw = new LawsF.Covariant.Law1[CovariantEqualF, Equal]("identityLaw") {
def apply[F[+_]: CovariantEqualF, A: Equal](fa: F[A]): TestResult =
fa.map(identity) <-> fa
}
val identityLaw: LawsF.Covariant[CovariantEqualF, Equal] =
new LawsF.Covariant.Law1[CovariantEqualF, Equal]("identityLaw") {
def apply[F[+_]: CovariantEqualF, A: Equal](fa: F[A]): TestResult =
fa.map(identity) <-> fa
}

val compositionLaw = new ZLawsF.Covariant.Law3Function[CovariantEqualF, Equal]("compositionLaw") {
def apply[F[+_]: CovariantEqualF, A: Equal, B: Equal, C: Equal](fa: F[A], f: A => B, g: B => C): TestResult =
fa.map(f).map(g) <-> fa.map(f andThen g)
}
val compositionLaw: LawsF.Covariant[CovariantEqualF, Equal] =
new ZLawsF.Covariant.ComposeLaw[CovariantEqualF, Equal]("compositionLaw") {
def apply[F[+_]: CovariantEqualF, A: Equal, B: Equal, C: Equal](fa: F[A], f: A => B, g: B => C): TestResult =
fa.map(f).map(g) <-> fa.map(f andThen g)
}

val laws = identityLaw + compositionLaw
val laws: LawsF.Covariant[CovariantEqualF, Equal] =
identityLaw + compositionLaw

implicit val OptionCovariant: Covariant[Option] =
new Covariant[Option] {
Expand Down Expand Up @@ -112,7 +108,7 @@ object LawsFSpec extends ZIOBaseSpec {
def spec = suite("LawsFSpec")(
suite("covariantLaws")(
testM("option") {
checkAllLaws(Covariant)(OptionGenF, Gen.anyInt)
checkAllLaws(Covariant)(GenF.option, Gen.anyInt)
}
)
)
Expand Down
82 changes: 81 additions & 1 deletion test/shared/src/main/scala/zio/test/laws/GenF.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,88 @@

package zio.test.laws

import zio.test.Gen
import zio.Chunk
import zio.random.Random
import zio.test.{ Gen, Sized }

/**
* A `GenF` knows how to construct a generator of `F[A]` values given a
* generator of `A` values for any `A`. For example, a `GenF` of `List` values
* knows how to generate lists with elements given a generator of elements of
* that type. You can think of `GenF` as a "recipe" for building generators
* for parameterized types.
* `
*/
trait GenF[-R, F[_]] {

/**
* Construct a generator of `F[A]` values given a generator of `A` values.
*/
def apply[R1 <: R, A](gen: Gen[R1, A]): Gen[R1, F[A]]
}

object GenF {

/**
* A generator of `Chunk` values.
*/
val chunk: GenF[Random with Sized, Chunk] =
new GenF[Random with Sized, Chunk] {
def apply[R1 <: Random with Sized, A](gen: Gen[R1, A]): Gen[R1, Chunk[A]] =
Gen.chunkOf(gen)
}

/**
* A generator of `Either` values.
*/
def either[R <: Random, A](a: Gen[R, A]): GenF[R, ({ type lambda[x] = Either[A, x] })#lambda] =
new GenF[R, ({ type lambda[x] = Either[A, x] })#lambda] {
def apply[R1 <: R, B](b: Gen[R1, B]): Gen[R1, Either[A, B]] =
Gen.either(a, b)
}

/**
* A generator of `List` values.
*/
val list: GenF[Random with Sized, List] =
new GenF[Random with Sized, List] {
def apply[R1 <: Random with Sized, A](gen: Gen[R1, A]): Gen[R1, List[A]] =
Gen.listOf(gen)
}

/**
* A generator of `Map` values.
*/
def map[R <: Random with Sized, A](a: Gen[R, A]): GenF[R, ({ type lambda[x] = Map[A, x] })#lambda] =
new GenF[R, ({ type lambda[x] = Map[A, x] })#lambda] {
def apply[R1 <: R, B](b: Gen[R1, B]): Gen[R1, Map[A, B]] =
Gen.mapOf(a, b)
}

/**
* A generator of `Option` values.
*/
val option: GenF[Random, Option] =
new GenF[Random, Option] {
def apply[R1 <: Random, A](gen: Gen[R1, A]): Gen[R1, Option[A]] =
Gen.option(gen)
}

/**
* A generator of `Set` values.
*/
val set: GenF[Random with Sized, Set] =
new GenF[Random with Sized, Set] {
def apply[R1 <: Random with Sized, A](gen: Gen[R1, A]): Gen[R1, Set[A]] =
Gen.setOf(gen)
}

/**
* A generator of `Vector` values.
*/
val vector: GenF[Random with Sized, Vector] =
new GenF[Random with Sized, Vector] {
def apply[R1 <: Random with Sized, A](gen: Gen[R1, A]): Gen[R1, Vector[A]] =
Gen.vectorOf(gen)
}
}
16 changes: 16 additions & 0 deletions test/shared/src/main/scala/zio/test/laws/ZLawfulF.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,18 @@

package zio.test.laws

/**
* `ZLawful[CapsF, Caps, R]` describes a set of laws that a parameterized type
* `F[A]` with capabilities `CapsF` is expected to satisfy with respect to all
* types `A` that have capabilities `Caps`. Lawful instances can be combined
* using `+` to describe a set of capabilities and all of the laws that those
* capabilities are expected to satisfy.
*/
object ZLawfulF {

/**
* `ZLawful` for covariant type constructors.
*/
trait Covariant[-CapsF[_[+_]], -Caps[_], -R] { self =>
def laws: ZLawsF.Covariant[CapsF, Caps, R]
def +[CapsF1[x[+_]] <: CapsF[x], Caps1[x] <: Caps[x], R1 <: R](
Expand All @@ -28,6 +38,9 @@ object ZLawfulF {
}
}

/**
* `ZLawful` for contravariant type constructors.
*/
trait Contravariant[-CapsF[_[-_]], -Caps[_], -R] { self =>
def laws: ZLawsF.Contravariant[CapsF, Caps, R]
def +[CapsF1[x[-_]] <: CapsF[x], Caps1[x] <: Caps[x], R1 <: R](
Expand All @@ -38,6 +51,9 @@ object ZLawfulF {
}
}

/**
* `ZLawful` for invariant type construtors.
*/
trait Invariant[-CapsF[_[_]], -Caps[_], -R] { self =>
def laws: ZLawsF.Invariant[CapsF, Caps, R]
def +[CapsF1[x[_]] <: CapsF[x], Caps1[x] <: Caps[x], R1 <: R](
Expand Down
2 changes: 1 addition & 1 deletion test/shared/src/main/scala/zio/test/laws/ZLaws.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import zio.test.{ check, checkM, Gen, TestResult }
* Laws can be combined using `+` to produce a set of laws that require both
* sets of laws to be satisfied.
*/
sealed trait ZLaws[-Caps[_], -R] { self =>
trait ZLaws[-Caps[_], -R] { self =>

/**
* Test that values of type `A` satisfy the laws using the specified
Expand Down
2 changes: 1 addition & 1 deletion test/shared/src/main/scala/zio/test/laws/ZLaws2.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package zio.test.laws
import zio.ZIO
import zio.test.{ check, Gen, TestResult }

sealed trait ZLaws2[-CapsBoth[_, _], -CapsLeft[_], -CapsRight[_], -R] { self =>
trait ZLaws2[-CapsBoth[_, _], -CapsLeft[_], -CapsRight[_], -R] { self =>

def run[R1 <: R, A: CapsLeft, B: CapsRight](left: Gen[R1, A], right: Gen[R1, B])(
implicit CapsBoth: CapsBoth[A, B]
Expand Down
64 changes: 52 additions & 12 deletions test/shared/src/main/scala/zio/test/laws/ZLawsF.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,21 @@ package zio.test.laws
import zio.ZIO
import zio.test.{ check, checkM, Gen, TestResult }

/**
* `ZLaws[CapsF, Caps, R]` describes a set of laws that a parameterized type
* `F[A]` with capabilities `CapsF` is expected to satisfy with respect to all
* types `A` that have capabilities `Caps`. Laws can be run by providing a
* `GenF` that is capable of generating `F[A]` values given a generator of `A`
* values and a generatoro of values of some type `A`. Laws can be combined
* using `+` to produce a set of laws that require both sets of laws to be
* satisfied.
*/
object ZLawsF {

sealed trait Covariant[-CapsF[_[+_]], -Caps[_], -R] { self =>
/**
* `ZLawsF` for covariant type constructors.
*/
trait Covariant[-CapsF[_[+_]], -Caps[_], -R] { self =>

/**
* Test that values of type `F[+_]` satisfy the laws using the specified
Expand Down Expand Up @@ -50,6 +62,26 @@ object ZLawsF {
left.run(genF, gen).zipWith(right.run(genF, gen))(_ && _)
}

/**
* Constructs a law from a pure function taking one parameterized value and
* two functions that can be composed.
*/
abstract class ComposeLaw[-CapsF[_[+_]], -Caps[_]](label: String) extends Covariant[CapsF, Caps, Any] { self =>
def apply[F[+_]: CapsF, A: Caps, B: Caps, C: Caps](fa: F[A], f: A => B, g: B => C): TestResult
final def run[R, F[+_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): ZIO[R, Nothing, TestResult] =
check(genF(gen), Gen.function(gen), Gen.function(gen))(apply(_, _, _).map(_.label(label)))
}

/**
* Constructs a law from a parameterized value wrapped in two additional
* layers that can be flattened.
*/
abstract class FlattenLaw[-CapsF[_[+_]], -Caps[_]](label: String) extends Covariant[CapsF, Caps, Any] { self =>
def apply[F[+_]: CapsF, A: Caps](fffa: F[F[F[A]]]): TestResult
final def run[R, F[+_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): ZIO[R, Nothing, TestResult] =
check(genF(genF(genF(gen))))(apply(_).map(_.label(label)))
}

/**
* Constructs a law from a pure function taking a single parameter.
*/
Expand Down Expand Up @@ -103,18 +135,12 @@ object ZLawsF {
final def run[R1 <: R, F[+_]: CapsF, A: Caps](genF: GenF[R1, F], gen: Gen[R1, A]): ZIO[R1, Nothing, TestResult] =
checkM(genF(gen), genF(gen), genF(gen))(apply(_, _, _).map(_.map(_.label(label))))
}

/**
* Constructs a law from a pure function taking three parameters.
*/
abstract class Law3Function[-CapsF[_[+_]], -Caps[_]](label: String) extends Covariant[CapsF, Caps, Any] { self =>
def apply[F[+_]: CapsF, A: Caps, B: Caps, C: Caps](fa: F[A], f: A => B, g: B => C): TestResult
final def run[R, F[+_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): ZIO[R, Nothing, TestResult] =
check(genF(gen), Gen.function(gen), Gen.function(gen))(apply(_, _, _).map(_.label(label)))
}
}

sealed trait Contravariant[-CapsF[_[-_]], -Caps[_], -R] { self =>
/**
* `ZLawsF` for contravariant type constructors.
*/
trait Contravariant[-CapsF[_[-_]], -Caps[_], -R] { self =>

/**
* Test that values of type `F[+_]` satisfy the laws using the specified
Expand Down Expand Up @@ -143,6 +169,17 @@ object ZLawsF {
left.run(genF, gen).zipWith(right.run(genF, gen))(_ && _)
}

/**
* Constructs a law from a pure function taking one parameterized value and
* two functions that can be composed.
*/
abstract class ComposeLaw[-CapsF[_[-_]], -Caps[_]](label: String) extends Contravariant[CapsF, Caps, Any] {
self =>
def apply[F[-_]: CapsF, A: Caps, B: Caps, C: Caps](fa: F[A], f: A => B, g: B => C): TestResult
final def run[R, F[-_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): ZIO[R, Nothing, TestResult] =
check(genF(gen), Gen.function(gen), Gen.function(gen))(apply(_, _, _).map(_.label(label)))
}

/**
* Constructs a law from a pure function taking a single parameter.
*/
Expand Down Expand Up @@ -198,7 +235,10 @@ object ZLawsF {
}
}

sealed trait Invariant[-CapsF[_[_]], -Caps[_], -R] { self =>
/**
* `ZLawsF` for invariant type constructors.
*/
trait Invariant[-CapsF[_[_]], -Caps[_], -R] { self =>

/**
* Test that values of type `F[+_]` satisfy the laws using the specified
Expand Down
16 changes: 16 additions & 0 deletions test/shared/src/main/scala/zio/test/laws/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ package object laws {

object LawsF {

type Covariant[-CapsF[_[+_]], -Caps[_]] = ZLawsF.Covariant[CapsF, Caps, Any]
type Contravariant[-CapsF[_[-_]], -Caps[_]] = ZLawsF.Contravariant[CapsF, Caps, Any]
type Invariant[-CapsF[_[_]], -Caps[_]] = ZLawsF.Invariant[CapsF, Caps, Any]

object Covariant {
type Law1[-CapsF[_[+_]], -Caps[_]] = ZLawsF.Covariant.Law1[CapsF, Caps]
type Law1M[-CapsF[_[+_]], -Caps[_]] = ZLawsF.Covariant.Law1M[CapsF, Caps, Any]
Expand Down Expand Up @@ -131,6 +135,10 @@ package object laws {
)(gen: Gen[R1, A]): ZIO[R1, Nothing, TestResult] =
lawful.laws.run(gen)

/**
* Checks that all values generated by a the specified generator satisfy the
* expected behavior of the lawful instance.
*/
def checkAllLaws[CapsBoth[_, _], CapsLeft[_], CapsRight[_], R, R1 <: R, A: CapsLeft, B: CapsRight](
lawful: ZLawful2[CapsBoth, CapsLeft, CapsRight, R]
)(a: Gen[R1, A], b: Gen[R1, B])(implicit CapsBoth: CapsBoth[A, B]): ZIO[R1, Nothing, TestResult] =
Expand All @@ -141,11 +149,19 @@ package object laws {
)(genF: GenF[R1, F], gen: Gen[R1, A]): ZIO[R1, Nothing, TestResult] =
lawful.laws.run(genF, gen)

/**
* Checks that all values generated by a the specified generator satisfy the
* expected behavior of the lawful instance.
*/
def checkAllLaws[CapsF[_[-_]], Caps[_], R, R1 <: R, F[-_]: CapsF, A: Caps](
lawful: ZLawfulF.Contravariant[CapsF, Caps, R]
)(genF: GenF[R1, F], gen: Gen[R1, A]): ZIO[R1, Nothing, TestResult] =
lawful.laws.run(genF, gen)

/**
* Checks that all values generated by a the specified generator satisfy the
* expected behavior of the lawful instance.
*/
def checkAllLaws[CapsF[_[_]], Caps[_], R, R1 <: R, F[_]: CapsF, A: Caps](
lawful: ZLawfulF.Invariant[CapsF, Caps, R]
)(genF: GenF[R1, F], gen: Gen[R1, A]): ZIO[R1, Nothing, TestResult] =
Expand Down