diff --git a/core/src/main/scala-3/cats/derived/DerivedShowPretty.scala b/core/src/main/scala-3/cats/derived/DerivedShowPretty.scala index e93ba93a..195e9b2b 100644 --- a/core/src/main/scala-3/cats/derived/DerivedShowPretty.scala +++ b/core/src/main/scala-3/cats/derived/DerivedShowPretty.scala @@ -21,14 +21,11 @@ Make sure that A satisfies one of the following conditions: type DerivedShowPretty[A] = Derived[ShowPretty[A]] object DerivedShowPretty: opaque type Or[A] = A => List[String] - object Or extends OrInstances: - def apply[A](instance: A => List[String]): Or[A] = instance + object Or: extension [A](or: Or[A]) def apply(a: A): List[String] = or(a) - - sealed abstract class OrInstances: inline given [A]: Or[A] = summonFrom { - case instance: Show[A] => Or((a: A) => instance.show(a).split(System.lineSeparator).toList) - case derived: DerivedShowPretty[A] => Or(derived.instance.showLines(_)) + case instance: Show[A] => instance.show(_).split(System.lineSeparator).toList + case derived: DerivedShowPretty[A] => derived.instance.showLines(_) } inline def apply[A]: ShowPretty[A] = @@ -48,23 +45,18 @@ object DerivedShowPretty: val n = labels.size if n <= 0 then List(s"$prefix()") else - var lines: List[String] = List(")") - val inner = inst.project(a)(n - 1)([t] => (show: Or[t], x: t) => show.apply(x)) - inner match - case Nil => lines = s" ${labels(n - 1)} = \"\"," :: lines - case h :: t => lines = s" ${labels(n - 1)} = $h" :: t.map(s => " " + s) ::: lines + var lines = List(")") + inst.project(a)(n - 1)([t] => (show: Or[t], x: t) => show.apply(x)) match + case Nil => lines ::= s" ${labels(n - 1)} = \"\"," + case h :: t => lines :::= s" ${labels(n - 1)} = $h" :: t.map(s => " " + s) var i = n - 2 while i >= 0 do - val inner = inst.project(a)(i)([t] => (show: Or[t], x: t) => show.apply(x)) - inner match - case Nil => lines = s" ${labels(i)} = \"\"," :: lines - case v :: Nil => lines = s" ${labels(i)} = $v," :: lines + inst.project(a)(i)([t] => (show: Or[t], x: t) => show.apply(x)) match + case Nil => lines ::= s" ${labels(i)} = \"\"," + case v :: Nil => lines ::= s" ${labels(i)} = $v," case h :: t => lines = s" ${labels(i)} = $h" :: t.init.map(s => " " + s) ::: s" ${t.last}," :: lines i -= 1 - - lines = s"$prefix(" :: lines - - lines + s"$prefix(" :: lines trait Coproduct[A](using inst: K0.CoproductInstances[Or, A]) extends ShowPretty[A]: def showLines(a: A): List[String] = diff --git a/core/src/test/scala-3/cats/derived/ADTs.scala b/core/src/test/scala-3/cats/derived/ADTs.scala new file mode 100644 index 00000000..aac23d15 --- /dev/null +++ b/core/src/test/scala-3/cats/derived/ADTs.scala @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2015 Miles Sabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cats.derived + +import cats.{Eq, Eval} +import cats.syntax.all.given +import org.scalacheck.rng.Seed +import org.scalacheck.{Arbitrary, Cogen, Gen} +import scala.annotation.tailrec + +object ADTs: + enum EnumK0: + case LeafS(value: String) + case LeafI(value: Int) + + object EnumK0: + given Arbitrary[EnumK0] = Arbitrary( + Gen.oneOf( + Arbitrary.arbitrary[String].map(LeafS.apply), + Arbitrary.arbitrary[Int].map(LeafI.apply) + ) + ) + + given Cogen[EnumK0] = Cogen[Either[String, Int]].contramap { + case LeafS(s) => Left(s) + case LeafI(i) => Right(i) + } + + enum EnumK1[A]: + case Leaf(value: A) + case Rec(l: EnumK1[A], r: EnumK1[A]) + + object EnumK1: + given [A: Arbitrary]: Arbitrary[EnumK1[A]] = + Arbitrary(Gen.recursive { rec => + Gen.sized { size => + val leaf = Arbitrary.arbitrary[A].map(Leaf.apply) + if size == 0 then leaf + else Gen.resize(size / 2, Gen.oneOf(leaf, Gen.zip(rec, rec).map(Rec.apply))) + } + }) + + enum EnumK1Contra[-A]: + case Leaf(value: A => Unit) + case Rec(l: EnumK1Contra[A], r: EnumK1Contra[A]) + + object EnumK1Contra: + given [A](using Arbitrary[A => Unit]): Arbitrary[EnumK1Contra[A]] = + Arbitrary(Gen.recursive { rec => + Gen.sized { size => + val leaf = Arbitrary.arbitrary[A => Unit].map(Leaf.apply) + if size == 0 then leaf + else Gen.resize(size / 2, Gen.oneOf(leaf, Gen.zip(rec, rec).map(Rec.apply))) + } + }) + + enum EnumK1Inv[A]: + case Leaf(cov: A, contra: A => Unit) + case Rec(l: EnumK1Inv[A], r: EnumK1Inv[A]) + + object EnumK1Inv: + given [A: Arbitrary](using Arbitrary[A => Unit]): Arbitrary[EnumK1Inv[A]] = + Arbitrary(Gen.recursive { rec => + Gen.sized { size => + val leaf = for + cov <- Arbitrary.arbitrary[A] + contra <- Arbitrary.arbitrary[A => Unit] + yield Leaf(cov, contra) + if size == 0 then leaf + else Gen.resize(size / 2, Gen.oneOf(leaf, Gen.zip(rec, rec).map(Rec.apply))) + } + }) + + sealed trait Rgb + object Rgb: + case object Red extends Rgb + case object Green extends Rgb + case object Blue extends Rgb + + final case class ComplexProduct[T](lbl: String, set: Set[T], fns: Vector[() => T], opt: Eval[Option[T]]) + object ComplexProduct: + given [T: Arbitrary]: Arbitrary[ComplexProduct[T]] = Arbitrary(for + lbl <- Arbitrary.arbitrary[String] + set <- Arbitrary.arbitrary[Set[T]] + vec <- Arbitrary.arbitrary[Vector[T]] + fns = vec.map(x => () => x) + opt <- Arbitrary.arbitrary[Option[T]] + yield ComplexProduct(lbl, set, fns, Eval.now(opt))) + + final case class Box[+A](content: A) + object Box: + given [A: Arbitrary]: Arbitrary[Box[A]] = Arbitrary(Arbitrary.arbitrary[A].map(apply)) + given [A: Cogen]: Cogen[Box[A]] = Cogen[A].contramap(_.content) + + final case class Recursive(i: Int, is: Option[Recursive]) + object Recursive extends ((Int, Option[Recursive]) => Recursive): + given Arbitrary[Recursive] = + def recursive(size: Int): Gen[Recursive] = for + i <- Arbitrary.arbitrary[Int] + is <- if (size <= 0) Gen.const(None) else Gen.option(recursive(size / 2)) + yield Recursive(i, is) + Arbitrary(Gen.sized(recursive)) + + given Cogen[Recursive] = + @tailrec def perturb(seed: Seed, rec: Recursive): Seed = + val i = Cogen[Int].perturb(seed, rec.i) + rec.is match + case Some(is) => perturb(i, is) + case None => i + Cogen(perturb _) + + final case class Interleaved[T](i: Int, t: T, l: Long, tt: Vector[T], s: String) + object Interleaved: + def empty[T](t: T): Interleaved[T] = + Interleaved(0, t, 0, Vector.empty, "") + given [T: Arbitrary]: Arbitrary[Interleaved[T]] = + Arbitrary(Arbitrary.arbitrary[(Int, T, Long, Vector[T], String)].map(apply)) + given [T: Cogen]: Cogen[Interleaved[T]] = + Cogen[(Int, T, Long, Vector[T], String)].contramap(x => (x.i, x.t, x.l, x.tt, x.s)) + + case class Bivariant[A](run: A => Boolean, store: A) + object Bivariant: + given [A: Arbitrary]: Arbitrary[Bivariant[A]] = Arbitrary(for + a <- Arbitrary.arbitrary[A] + f <- Arbitrary.arbitrary[Boolean].map(Function.const[Boolean, A]) + yield Bivariant[A](f, a)) + + case class Pred[A](run: A => Boolean) + + sealed trait IList[A] + final case class ICons[A](head: A, tail: IList[A]) extends IList[A] + final case class INil[A]() extends IList[A] + + object ICons: + given [A: Arbitrary]: Arbitrary[ICons[A]] = Arbitrary(for + head <- Arbitrary.arbitrary[A] + tail <- Arbitrary.arbitrary[IList[A]] + yield ICons(head, tail)) + + object IList: + given [A: Arbitrary]: Arbitrary[IList[A]] = + Arbitrary(Arbitrary.arbitrary[Seq[A]].map(fromSeq)) + given [A: Cogen]: Cogen[IList[A]] = + Cogen[Seq[A]].contramap(toList) + def fromSeq[T](ts: Seq[T]): IList[T] = + ts.foldRight[IList[T]](INil())(ICons.apply) + def toList[T](list: IList[T]): List[T] = + @tailrec def loop(list: IList[T], acc: List[T]): List[T] = list match + case INil() => acc.reverse + case ICons(head, tail) => loop(tail, head :: acc) + loop(list, Nil) + + sealed trait Snoc[A] + final case class SCons[A](init: Snoc[A], last: A) extends Snoc[A] + final case class SNil[A]() extends Snoc[A] + + object Snoc: + given [A: Arbitrary]: Arbitrary[Snoc[A]] = + Arbitrary(Arbitrary.arbitrary[Seq[A]].map(fromSeq)) + def fromSeq[T](ts: Seq[T]): Snoc[T] = + ts.foldLeft[Snoc[T]](SNil())(SCons.apply) + def toList[T](snoc: Snoc[T]): List[T] = + @tailrec def loop(snoc: Snoc[T], acc: List[T]): List[T] = snoc match + case SNil() => acc + case SCons(init, last) => loop(init, last :: acc) + loop(snoc, Nil) + + object SCons: + given [A: Arbitrary]: Arbitrary[SCons[A]] = Arbitrary(for + init <- Arbitrary.arbitrary[Snoc[A]] + last <- Arbitrary.arbitrary[A] + yield SCons(init, last)) + + sealed trait Tree[A] + final case class Leaf[A](value: A) extends Tree[A] + final case class Node[A](left: Tree[A], right: Tree[A]) extends Tree[A] + + object Tree: + given [A: Arbitrary]: Arbitrary[Tree[A]] = + val leaf = Arbitrary.arbitrary[A].map(Leaf.apply) + def tree(maxDepth: Int): Gen[Tree[A]] = + if (maxDepth <= 0) leaf + else Gen.oneOf(leaf, node(maxDepth)) + def node(maxDepth: Int): Gen[Tree[A]] = for + depthL <- Gen.choose(0, maxDepth - 1) + depthR <- Gen.choose(0, maxDepth - 1) + left <- tree(depthL) + right <- tree(depthR) + yield Node(left, right) + Arbitrary(Gen.sized(tree)) + + given [A: Cogen]: Cogen[Tree[A]] = + lazy val cogen: Cogen[Tree[A]] = Cogen { (seed, tree) => + tree match + case Leaf(value) => Cogen[A].perturb(seed, value) + case Node(left, right) => cogen.perturb(cogen.perturb(seed, left), right) + } + + cogen + + final case class Foo(i: Int, b: Option[String]) + object Foo: + given Cogen[Foo] = Cogen[Int].contramap(_.i) + given Arbitrary[Foo] = Arbitrary(for + i <- Arbitrary.arbitrary[Int] + b <- Arbitrary.arbitrary[Option[String]] + yield Foo(i, b)) + + final case class CommutativeFoo(i: Int, b: Option[Long]) + object CommutativeFoo: + given Cogen[CommutativeFoo] = + Cogen[(Int, Option[Long])].contramap(x => (x.i, x.b)) + given Arbitrary[CommutativeFoo] = Arbitrary(for + i <- Arbitrary.arbitrary[Int] + b <- Arbitrary.arbitrary[Option[Long]] + yield CommutativeFoo(i, b)) + + case class Inner(i: Int) + case class Outer(in: Inner) + + object Inner: + given Arbitrary[Inner] = Arbitrary(Arbitrary.arbitrary[Int].map(apply)) + given Cogen[Inner] = Cogen[Int].contramap(_.i) + + object Outer: + given Arbitrary[Outer] = Arbitrary(Arbitrary.arbitrary[Inner].map(apply)) + given Cogen[Outer] = Cogen[Inner].contramap(_.in) + + sealed trait IntTree + final case class IntLeaf(t: Int) extends IntTree + final case class IntNode(l: IntTree, r: IntTree) extends IntTree + + object IntTree + + sealed trait GenericAdt[A] + final case class GenericAdtCase[A](value: Option[A]) extends GenericAdt[A] + + object GenericAdt: + given [A: Arbitrary]: Arbitrary[GenericAdt[A]] = + Arbitrary(Arbitrary.arbitrary[Option[A]].map(GenericAdtCase.apply)) + given [A: Cogen]: Cogen[GenericAdt[A]] = + Cogen[Option[A]].contramap { case GenericAdtCase(value) => value } + + final case class CaseClassWOption[A](value: Option[A]) + object CaseClassWOption: + given [A: Arbitrary]: Arbitrary[CaseClassWOption[A]] = + Arbitrary(Arbitrary.arbitrary[Option[A]].map(apply)) + + final case class First(value: String) + final case class Second(value: String) + final case class Middle(first: First, second: Option[Second]) + final case class Top(middle: Middle) + + final case class Address(street: String, city: String, state: String) + final case class ContactInfo(phoneNumber: String, address: Address) + final case class People(name: String, contactInfo: ContactInfo) + + final case class ListField(a: String, b: List[ListFieldChild]) + final case class ListFieldChild(c: Int) + + enum Many[+A] derives Eq: + case Naught + case More(value: A, rest: Many[A]) + + enum AtMostOne[+A] derives Eq: + case Naught + case Single(value: A) + + enum AtLeastOne[+A] derives Eq: + case Single(value: A) + case More(value: A, rest: Option[AtLeastOne[A]]) + + object Many: + given [A: Arbitrary]: Arbitrary[Many[A]] = Arbitrary( + Gen.oneOf( + Gen.const(Many.Naught), + Gen.lzy(Arbitrary.arbitrary[(A, Many[A])].map(Many.More.apply)) + ) + ) + + object AtMostOne: + given [A: Arbitrary]: Arbitrary[AtMostOne[A]] = Arbitrary( + Gen.oneOf( + Gen.const(AtMostOne.Naught), + Arbitrary.arbitrary[A].map(AtMostOne.Single.apply) + ) + ) + + object AtLeastOne: + given [A: Arbitrary]: Arbitrary[AtLeastOne[A]] = Arbitrary( + Gen.oneOf( + Arbitrary.arbitrary[A].map(AtLeastOne.Single.apply), + Gen.lzy(Arbitrary.arbitrary[(A, Option[AtLeastOne[A]])].map(AtLeastOne.More.apply)) + ) + ) + + trait EqInstances: + import ADTs.* + + given [T: Eq]: Eq[ComplexProduct[T]] = (x, y) => + x.lbl == y.lbl && x.set === y.set && x.fns.map(_()) === y.fns.map(_()) && x.opt === y.opt + + given [A: Eq]: Eq[Box[A]] = Eq.by(_.content) + given Eq[Recursive] = Eq.fromUniversalEquals + given [T: Eq]: Eq[Interleaved[T]] = Eq.by(i => (i.i, i.t, i.l, i.tt, i.s)) + + given [A: Eq]: Eq[IList[A]] with + @tailrec final def eqv(x: IList[A], y: IList[A]): Boolean = (x, y) match + case (ICons(hx, tx), ICons(hy, ty)) => hx === hy && eqv(tx, ty) + case (INil(), INil()) => true + case _ => false + + given [A: Eq]: Eq[Snoc[A]] with + @tailrec final def eqv(x: Snoc[A], y: Snoc[A]): Boolean = (x, y) match + case (SCons(ix, lx), SCons(iy, ly)) => lx === ly && eqv(ix, iy) + case (SNil(), SNil()) => true + case _ => false + + given [A: Eq]: Eq[ICons[A]] = Eq.by(identity[IList[A]]) + given [A: Eq]: Eq[SCons[A]] = Eq.by(identity[Snoc[A]]) + + given [A: Eq]: Eq[Tree[A]] with + def eqv(x: Tree[A], y: Tree[A]): Boolean = (x, y) match + case (Leaf(vx), Leaf(vy)) => vx === vy + case (Node(lx, rx), Node(ly, ry)) => eqv(lx, ly) && eqv(rx, ry) + case _ => false + + given [A: Eq]: Eq[Bivariant[A]] = { case (Bivariant(runX, storeX), Bivariant(runY, storeY)) => + storeX === storeY && runX(storeX) == runY(storeY) + } + + given Eq[Foo] = Eq.fromUniversalEquals + given Eq[CommutativeFoo] = Eq.fromUniversalEquals + given [A: Eq]: Eq[GenericAdt[A]] = Eq.by { case GenericAdtCase(v) => v } + given [A: Eq]: Eq[CaseClassWOption[A]] = Eq.by(_.value) + given Eq[Inner] = Eq.fromUniversalEquals + given Eq[Outer] = Eq.fromUniversalEquals + + given Eq[EnumK0] = { + case (EnumK0.LeafS(s1), EnumK0.LeafS(s2)) => s1 === s2 + case (EnumK0.LeafI(i1), EnumK0.LeafI(i2)) => i1 === i2 + case _ => false + } + + given [A: Eq]: Eq[EnumK1[A]] = { + case (EnumK1.Leaf(v1), EnumK1.Leaf(v2)) => v1 === v2 + case (EnumK1.Rec(l1, r1), EnumK1.Rec(l2, r2)) => l1 === l2 && r1 === r2 + case _ => false + } + + given [A](using Eq[A => Unit]): Eq[EnumK1Contra[A]] = { + case (EnumK1Contra.Leaf(v1), EnumK1Contra.Leaf(v2)) => v1 === v2 + case (EnumK1Contra.Rec(l1, r1), EnumK1Contra.Rec(l2, r2)) => l1 === l2 && r1 === r2 + case _ => false + } + + given [A: Eq](using Eq[A => Unit]): Eq[EnumK1Inv[A]] = { + case (EnumK1Inv.Leaf(cov1, contra1), EnumK1Inv.Leaf(cov2, contra2)) => cov1 === cov2 && contra1 === contra2 + case (EnumK1Inv.Rec(l1, r1), EnumK1Inv.Rec(l2, r2)) => l1 === l2 && r1 === r2 + case _ => false + } + + end EqInstances +end ADTs diff --git a/core/src/test/scala-3/cats/derived/ApplicativeSuite.scala b/core/src/test/scala-3/cats/derived/ApplicativeSuite.scala index 562d6ff1..a326bd86 100644 --- a/core/src/test/scala-3/cats/derived/ApplicativeSuite.scala +++ b/core/src/test/scala-3/cats/derived/ApplicativeSuite.scala @@ -19,44 +19,50 @@ package cats.derived import cats.Applicative import cats.laws.discipline.* import cats.laws.discipline.SemigroupalTests.Isomorphisms - +import org.scalacheck.Arbitrary import scala.compiletime.* class ApplicativeSuite extends KittensSuite: import ApplicativeSuite.* - import TestDefns.* + import ADTs.* + + inline given [F[_]]: Isomorphisms[F] = + Isomorphisms.invariant(summonInline[Applicative[F]]) - inline def applicativeTests[F[_]](f: Isomorphisms[F] ?=> (at: ApplicativeTests[F]) => at.RuleSet) = - f(using summonInline)(ApplicativeTests[F](summonInline)) + inline def tests[F[_]]: ApplicativeTests[F] = + ApplicativeTests[F](summonInline) - inline def testApplicative(inline context: String): Unit = - checkAll( - s"$context.Applicative[CaseClassWOption]", - applicativeTests[CaseClassWOption](_.applicative[Int, String, Long]) - ) - checkAll(s"$context.Applicative[OptList]", applicativeTests[OptList](_.applicative[Int, String, Long])) - checkAll(s"$context.Applicative[AndInt]", applicativeTests[AndInt](_.applicative[Int, String, Long])) - checkAll(s"$context.Applicative[Interleaved]", applicativeTests[Interleaved](_.applicative[Int, String, Long])) - checkAll(s"$context.Applicative[ListBox]", applicativeTests[ListBox](_.applicative[Int, String, Long])) - checkAll( - s"$context.Applicative is Serializable", - SerializableTests.serializable(summonInline[Applicative[Interleaved]]) - ) + inline def validate(inline instance: String): Unit = + checkAll(s"$instance[CaseClassWOption]", tests[CaseClassWOption].applicative[Int, String, Long]) + checkAll(s"$instance[OptList]", tests[OptList].applicative[Int, String, Long]) + checkAll(s"$instance[AndInt]", tests[AndInt].applicative[Int, String, Long]) + checkAll(s"$instance[Interleaved]", tests[Interleaved].applicative[Int, String, Long]) + checkAll(s"$instance[ListBox]", tests[ListBox].applicative[Int, String, Long]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[Applicative[Interleaved]])) locally { import auto.applicative.given - testApplicative("auto") + validate("auto.applicative") } locally { import semiInstances.given - testApplicative("semiauto") + validate("semiauto.applicative") + } + + locally { + import derivedInstances.* + val instance = "derived.applicative" + checkAll(s"$instance[CaseClassWOption]", tests[CaseClassWOption].applicative[Int, String, Long]) + checkAll(s"$instance[AndInt]", tests[AndInt].applicative[Int, String, Long]) + checkAll(s"$instance[Interleaved]", tests[Interleaved].applicative[Int, String, Long]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(Applicative[Interleaved])) } end ApplicativeSuite object ApplicativeSuite: - import TestDefns.* + import ADTs.* type OptList[A] = Option[List[A]] type AndInt[A] = (A, Int) @@ -70,4 +76,9 @@ object ApplicativeSuite: given Applicative[Interleaved] = semiauto.applicative given Applicative[ListBox] = semiauto.applicative + object derivedInstances: + case class CaseClassWOption[A](x: ADTs.CaseClassWOption[A]) derives Applicative + case class Interleaved[A](x: ADTs.Interleaved[A]) derives Applicative + case class AndInt[A](x: ApplicativeSuite.AndInt[A]) derives Applicative + end ApplicativeSuite diff --git a/core/src/test/scala-3/cats/derived/ApplySuite.scala b/core/src/test/scala-3/cats/derived/ApplySuite.scala index f75f2a42..bb066b65 100644 --- a/core/src/test/scala-3/cats/derived/ApplySuite.scala +++ b/core/src/test/scala-3/cats/derived/ApplySuite.scala @@ -17,42 +17,51 @@ package cats.derived import cats.Apply -import cats.laws.discipline.{ApplyTests, SerializableTests} +import cats.laws.discipline.* import cats.laws.discipline.SemigroupalTests.Isomorphisms import scala.compiletime.* class ApplySuite extends KittensSuite: import ApplySuite.* - import TestDefns.* + import ADTs.* inline given [F[_]]: Isomorphisms[F] = Isomorphisms.invariant(summonInline[Apply[F]]) - inline def applyTests[F[_]]: ApplyTests[F] = + inline def tests[F[_]]: ApplyTests[F] = ApplyTests[F](summonInline) - inline def testApply(inline context: String): Unit = - checkAll(s"$context.Apply[CaseClassWOption]", applyTests[CaseClassWOption].apply[Int, String, Long]) - checkAll(s"$context.Apply[OptList]", applyTests[OptList].apply[Int, String, Long]) - checkAll(s"$context.Apply[AndInt]", applyTests[AndInt].apply[Int, String, Long]) - checkAll(s"$context.Apply[Interleaved]", applyTests[Interleaved].apply[Int, String, Long]) - checkAll(s"$context.Apply[ListBox]", applyTests[ListBox].apply[Int, String, Long]) - checkAll(s"$context.Apply is Serializable", SerializableTests.serializable(summonInline[Apply[Interleaved]])) + inline def validate(inline instance: String): Unit = + checkAll(s"$instance[CaseClassWOption]", tests[CaseClassWOption].apply[Int, String, Long]) + checkAll(s"$instance[OptList]", tests[OptList].apply[Int, String, Long]) + checkAll(s"$instance[AndInt]", tests[AndInt].apply[Int, String, Long]) + checkAll(s"$instance[Interleaved]", tests[Interleaved].apply[Int, String, Long]) + checkAll(s"$instance[ListBox]", tests[ListBox].apply[Int, String, Long]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[Apply[Interleaved]])) locally { import auto.apply.given - testApply("auto") + validate("auto.apply") } locally { import semiInstances.given - testApply("semiauto") + validate("semiauto.apply") + } + + locally { + import derivedInstances.* + val instance = "derived.apply" + checkAll(s"$instance[CaseClassWOption]", tests[CaseClassWOption].apply[Int, String, Long]) + checkAll(s"$instance[AndInt]", tests[AndInt].apply[Int, String, Long]) + checkAll(s"$instance[Interleaved]", tests[Interleaved].apply[Int, String, Long]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(Apply[Interleaved])) } end ApplySuite object ApplySuite: - import TestDefns.* + import ADTs.* type OptList[A] = Option[List[A]] type AndInt[A] = (A, Int) @@ -66,4 +75,9 @@ object ApplySuite: given Apply[Interleaved] = semiauto.apply given Apply[ListBox] = semiauto.apply + object derivedInstances: + case class CaseClassWOption[A](x: ADTs.CaseClassWOption[A]) derives Apply + case class Interleaved[A](x: ADTs.Interleaved[A]) derives Apply + case class AndInt[A](x: ApplySuite.AndInt[A]) derives Apply + end ApplySuite diff --git a/core/src/test/scala-3/cats/derived/CommutativeMonoidSuite.scala b/core/src/test/scala-3/cats/derived/CommutativeMonoidSuite.scala index c4e14cb5..7418b3ba 100644 --- a/core/src/test/scala-3/cats/derived/CommutativeMonoidSuite.scala +++ b/core/src/test/scala-3/cats/derived/CommutativeMonoidSuite.scala @@ -16,58 +16,67 @@ package cats.derived -import cats.Eq import cats.kernel.{CommutativeMonoid, CommutativeSemigroup} -import cats.kernel.laws.discipline.{CommutativeMonoidTests, SerializableTests} -import org.scalacheck.Arbitrary - +import cats.kernel.laws.discipline.* import scala.compiletime.* class CommutativeMonoidSuite extends KittensSuite: import CommutativeMonoidSuite.* - import TestDefns.* + import ADTs.* - inline def commutativeMonoidTests[A]: CommutativeMonoidTests[A] = + inline def tests[A]: CommutativeMonoidTests[A] = CommutativeMonoidTests[A](summonInline) - inline def testCommutativeMonoid(inline context: String): Unit = - checkAll(s"$context.CommutativeMonoid[Foo]", commutativeMonoidTests[CommutativeFoo].commutativeMonoid) - checkAll(s"$context.CommutativeMonoid[Recursive]", commutativeMonoidTests[Recursive].commutativeMonoid) - checkAll(s"$context.CommutativeMonoid[Box[Mul]]", commutativeMonoidTests[Box[Mul]].commutativeMonoid) + inline def validate(inline instance: String): Unit = + checkAll(s"$instance[CommutativeFoo]", tests[CommutativeFoo].commutativeMonoid) + checkAll(s"$instance[Recursive]", tests[Recursive].commutativeMonoid) + checkAll(s"$instance[BoxMul]", tests[BoxMul].commutativeMonoid) checkAll( - s"$context.CommutativeMonoid is Serializable", + s"$instance is Serializable", SerializableTests.serializable(summonInline[CommutativeMonoid[CommutativeFoo]]) ) - test(s"$context.CommutativeMonoid respects existing instances") { - val box = summonInline[CommutativeMonoid[Box[Mul]]] + test(s"$instance respects existing instances") { + val box = summonInline[CommutativeMonoid[BoxMul]] assert(box.empty == Box(Mul(1))) assert(box.combine(Box(Mul(5)), Box(Mul(5))) == Box(Mul(25))) } locally { import auto.commutativeMonoid.given - testCommutativeMonoid("auto") + validate("auto.commutativeMonoid") } locally { import semiInstances.given - testCommutativeMonoid("semiauto") + validate("semiauto.commutativeMonoid") + } + + locally { + import derivedInstances.* + val instance = "derived.commutativeMonoid" + checkAll(s"$instance[CommutativeFoo]", tests[CommutativeFoo].commutativeMonoid) + checkAll(s"$instance[BoxMul]", tests[BoxMul].commutativeMonoid) + checkAll(s"$instance is Serializable", SerializableTests.serializable(CommutativeMonoid[CommutativeFoo])) } end CommutativeMonoidSuite object CommutativeMonoidSuite: - import TestDefns.* + import ADTs.* + + type BoxMul = Box[Mul] object semiInstances: given CommutativeMonoid[CommutativeFoo] = semiauto.commutativeMonoid given CommutativeMonoid[Recursive] = semiauto.commutativeMonoid given CommutativeMonoid[Box[Mul]] = semiauto.commutativeMonoid + object derivedInstances: + case class CommutativeFoo(x: ADTs.CommutativeFoo) derives CommutativeMonoid + case class BoxMul(x: CommutativeMonoidSuite.BoxMul) derives CommutativeMonoid + final case class Mul(value: Int) object Mul: - given Eq[Mul] = Eq.fromUniversalEquals - given Arbitrary[Mul] = Arbitrary(Arbitrary.arbitrary[Int].map(apply)) given CommutativeMonoid[Mul] with val empty = Mul(1) def combine(x: Mul, y: Mul) = Mul(x.value * y.value) diff --git a/core/src/test/scala-3/cats/derived/CommutativeMonoidTests.scala b/core/src/test/scala-3/cats/derived/CommutativeMonoidTests.scala deleted file mode 100644 index 96c2914f..00000000 --- a/core/src/test/scala-3/cats/derived/CommutativeMonoidTests.scala +++ /dev/null @@ -1,8 +0,0 @@ -package cats.derived - -import cats.kernel.CommutativeMonoid -import cats.derived.* - -class CommutativeMonoidTests { // - case class Foo(i: Int, b: Option[Int]) derives CommutativeMonoid -} diff --git a/core/src/test/scala-3/cats/derived/CommutativeSemigroupSuite.scala b/core/src/test/scala-3/cats/derived/CommutativeSemigroupSuite.scala index 1ce03dc2..e4b4d86c 100644 --- a/core/src/test/scala-3/cats/derived/CommutativeSemigroupSuite.scala +++ b/core/src/test/scala-3/cats/derived/CommutativeSemigroupSuite.scala @@ -16,60 +16,66 @@ package cats.derived -import cats.Eq import cats.kernel.CommutativeSemigroup -import cats.kernel.laws.discipline.{CommutativeSemigroupTests, SerializableTests} -import org.scalacheck.Arbitrary - +import cats.kernel.laws.discipline.* import scala.compiletime.* class CommutativeSemigroupSuite extends KittensSuite: import CommutativeSemigroupSuite.* - import TestDefns.* + import ADTs.* - inline def commutativeSemigroupTests[A]: CommutativeSemigroupTests[A] = + inline def tests[A]: CommutativeSemigroupTests[A] = CommutativeSemigroupTests[A](summonInline) - inline def testCommutativeSemigroup(inline context: String): Unit = - checkAll( - s"$context.CommutativeSemigroup[CommutativeFoo]", - commutativeSemigroupTests[CommutativeFoo].commutativeSemigroup - ) - checkAll(s"$context.CommutativeSemigroup[Recursive]", commutativeSemigroupTests[Recursive].commutativeSemigroup) - checkAll(s"$context.CommutativeSemigroup[Box[Mul]]", commutativeSemigroupTests[Box[Mul]].commutativeSemigroup) + inline def validate(inline instance: String): Unit = + checkAll(s"$instance[CommutativeFoo]", tests[CommutativeFoo].commutativeSemigroup) + checkAll(s"$instance[Recursive]", tests[Recursive].commutativeSemigroup) + checkAll(s"$instance[BoxMul]", tests[BoxMul].commutativeSemigroup) checkAll( - s"$context.CommutativeSemigroup is Serializable", + s"$instance is Serializable", SerializableTests.serializable(summonInline[CommutativeSemigroup[CommutativeFoo]]) ) - test(s"$context.CommutativeSemigroup respects existing instances") { - val box = summonInline[CommutativeSemigroup[Box[Mul]]] + test(s"$instance respects existing instances") { + val box = summonInline[CommutativeSemigroup[BoxMul]] assert(box.combine(Box(Mul(5)), Box(Mul(5))).content.value == 25) } locally { import auto.commutativeSemigroup.given - testCommutativeSemigroup("auto") + validate("auto.commutativeSemigroup") } locally { import semiInstances.given - testCommutativeSemigroup("semiauto") + validate("semiauto.commutativeSemigroup") + } + + locally { + import derivedInstances.* + val instance = "derived.commutativeSemigroup" + checkAll(s"$instance[CommutativeFoo]", tests[CommutativeFoo].commutativeSemigroup) + checkAll(s"$instance[BoxMul]", tests[BoxMul].commutativeSemigroup) + checkAll(s"$instance is Serializable", SerializableTests.serializable(CommutativeSemigroup[CommutativeFoo])) } end CommutativeSemigroupSuite object CommutativeSemigroupSuite: - import TestDefns.* + import ADTs.* + + type BoxMul = Box[Mul] object semiInstances: given CommutativeSemigroup[CommutativeFoo] = semiauto.commutativeSemigroup given CommutativeSemigroup[Recursive] = semiauto.commutativeSemigroup - given CommutativeSemigroup[Box[Mul]] = semiauto.commutativeSemigroup + given CommutativeSemigroup[BoxMul] = semiauto.commutativeSemigroup + + object derivedInstances: + case class CommutativeFoo(x: ADTs.CommutativeFoo) derives CommutativeSemigroup + case class BoxMul(x: CommutativeSemigroupSuite.BoxMul) derives CommutativeSemigroup final case class Mul(value: Int) object Mul: - given Eq[Mul] = Eq.fromUniversalEquals - given Arbitrary[Mul] = Arbitrary(Arbitrary.arbitrary[Int].map(apply)) given CommutativeSemigroup[Mul] = (x, y) => Mul(x.value * y.value) end CommutativeSemigroupSuite diff --git a/core/src/test/scala-3/cats/derived/CommutativeSemigroupTests.scala b/core/src/test/scala-3/cats/derived/CommutativeSemigroupTests.scala deleted file mode 100644 index e790e0cf..00000000 --- a/core/src/test/scala-3/cats/derived/CommutativeSemigroupTests.scala +++ /dev/null @@ -1,8 +0,0 @@ -package cats.derived - -import cats.kernel.CommutativeSemigroup -import cats.derived.* - -class CommutativeSemigroupTests { // - case class Foo(i: Int, b: Option[Int]) derives CommutativeSemigroup -} diff --git a/core/src/test/scala-3/cats/derived/ContravariantSuite.scala b/core/src/test/scala-3/cats/derived/ContravariantSuite.scala index bcf5084c..c9c8f700 100644 --- a/core/src/test/scala-3/cats/derived/ContravariantSuite.scala +++ b/core/src/test/scala-3/cats/derived/ContravariantSuite.scala @@ -14,9 +14,9 @@ * limitations under the License. */ -package cats -package derived +package cats.derived +import cats.Contravariant import cats.laws.discipline.* import cats.laws.discipline.arbitrary.* import cats.laws.discipline.eq.* @@ -24,65 +24,45 @@ import scala.compiletime.* class ContravariantSuite extends KittensSuite: import ContravariantSuite.* - import TestDefns.* + import ADTs.* - inline def contravariantTests[F[_]]: ContravariantTests[F] = + inline def tests[F[_]]: ContravariantTests[F] = ContravariantTests[F](summonInline) - inline def testContravariant(context: String): Unit = - checkAll(s"$context.Contravariant[OptPred]", contravariantTests[OptPred].contravariant[MiniInt, String, Boolean]) - checkAll(s"$context.Contravariant[TreePred]", contravariantTests[TreePred].contravariant[MiniInt, String, Boolean]) - checkAll(s"$context.Contravariant[ListPred]", contravariantTests[ListPred].contravariant[MiniInt, String, Boolean]) - checkAll( - s"$context.Contravariant[GenericAdtPred]", - contravariantTests[GenericAdtPred].contravariant[MiniInt, String, Boolean] - ) - // TODO https://github.com/typelevel/kittens/issues/473 - // checkAll( - // s"$context.Contravariant[InterleavedPred]", - // contravariantTests[InterleavedPred].contravariant[MiniInt, String, Boolean] - // ) - checkAll( - s"$context.Contravariant[AndCharPred]", - contravariantTests[AndCharPred].contravariant[MiniInt, String, Boolean] - ) - checkAll( - s"$context.Contravariant[ListSnocF]", - contravariantTests[ListSnocF].contravariant[MiniInt, String, Boolean] - ) - checkAll( - s"$context.Contravariant[EnumK1Contra]", - contravariantTests[EnumK1Contra].contravariant[MiniInt, String, Boolean] - ) - checkAll( - s"$context.Contravariant is Serializable", - SerializableTests.serializable(summonInline[Contravariant[TreePred]]) - ) - - // TODO https://github.com/typelevel/kittens/issues/476 - // test(s"$context.Contravariant.contramap is stack safe") { - // val C = summonInline[Contravariant[ListSnocF]] - // val n = 10000 - // val largeBoxed = Snoc.fromSeq((1 until n).map((j: Int) => (i: Int) => i + j)) :: Nil - // val actualBoxed = C.contramap[Int, Int](largeBoxed)((j: Int) => j + 1).flatMap(Snoc.toList) - // val expected = (3 until n + 2).toList - // assert(actualBoxed.map(_.apply(1)) == expected) - // } + inline def validate(instance: String): Unit = + checkAll(s"$instance[OptPred]", tests[OptPred].contravariant[MiniInt, String, Boolean]) + checkAll(s"$instance[TreePred]", tests[TreePred].contravariant[MiniInt, String, Boolean]) + checkAll(s"$instance[ListPred]", tests[ListPred].contravariant[MiniInt, String, Boolean]) + checkAll(s"$instance[GenericAdtPred]", tests[GenericAdtPred].contravariant[MiniInt, String, Boolean]) + // TODO: https://github.com/typelevel/kittens/issues/473 + // checkAll(s"$instance[InterleavedPred]", tests[InterleavedPred].contravariant[MiniInt, String, Boolean]) + checkAll(s"$instance[AndCharPred]", tests[AndCharPred].contravariant[MiniInt, String, Boolean]) + checkAll(s"$instance[ListSnocF]", tests[ListSnocF].contravariant[MiniInt, String, Boolean]) + checkAll(s"$instance[EnumK1Contra]", tests[EnumK1Contra].contravariant[MiniInt, String, Boolean]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[Contravariant[TreePred]])) locally { import auto.contravariant.given - testContravariant("auto") + validate("auto.contravariant") } locally { import semiInstances.given - testContravariant("semiauto") + validate("semiauto.contravariant") + } + + locally { + import derivedInstances.* + val instance = "derived.contravariant" + checkAll(s"$instance[EnumK1Contra]", tests[EnumK1Contra].contravariant[MiniInt, String, Boolean]) + checkAll(s"$instance[Single]", tests[Single].contravariant[MiniInt, String, Boolean]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(Contravariant[EnumK1Contra])) } end ContravariantSuite object ContravariantSuite: - import TestDefns.* + import ADTs.* type OptPred[A] = Option[A => Boolean] type ListPred[A] = List[A => Boolean] @@ -97,23 +77,26 @@ object ContravariantSuite: given Contravariant[TreePred] = semiauto.contravariant given Contravariant[ListPred] = semiauto.contravariant given Contravariant[GenericAdtPred] = semiauto.contravariant + // TODO: https://github.com/typelevel/kittens/issues/473 // given Contravariant[InterleavedPred] = semiauto.contravariant given Contravariant[AndCharPred] = semiauto.contravariant given Contravariant[ListSnocF] = semiauto.contravariant given Contravariant[EnumK1Contra] = semiauto.contravariant - case class Single[A](value: A => Unit) derives Contravariant + object derivedInstances: + case class EnumK1Contra[-A](x: ADTs.EnumK1Contra[A]) derives Contravariant + case class Single[-A](value: A => Unit) derives Contravariant - enum Many[-A] derives Contravariant: - case Naught - case More(value: A => Unit, rest: Many[A]) + enum Many[-A] derives Contravariant: + case Naught + case More(value: A => Unit, rest: Many[A]) - enum AtMostOne[-A] derives Contravariant: - case Naught - case Single(value: A => Unit) + enum AtMostOne[-A] derives Contravariant: + case Naught + case Single(value: A => Unit) - enum AtLeastOne[-A] derives Contravariant: - case Single(value: A => Unit) - case More(value: A => Unit, rest: Option[AtLeastOne[A]]) + enum AtLeastOne[-A] derives Contravariant: + case Single(value: A => Unit) + case More(value: A => Unit, rest: Option[AtLeastOne[A]]) end ContravariantSuite diff --git a/core/src/test/scala-3/cats/derived/EmptyKSuite.scala b/core/src/test/scala-3/cats/derived/EmptyKSuite.scala index f14a3221..f01fc1eb 100644 --- a/core/src/test/scala-3/cats/derived/EmptyKSuite.scala +++ b/core/src/test/scala-3/cats/derived/EmptyKSuite.scala @@ -20,12 +20,11 @@ import alleycats.{EmptyK, Pure} import alleycats.std.all.* import cats.data.NonEmptyList import cats.laws.discipline.SerializableTests - import scala.compiletime.summonInline class EmptyKSuite extends KittensSuite: import EmptyKSuite.* - import TestDefns.* + import ADTs.* given Pure[Box] with def pure[A](a: A) = Box(a) @@ -33,30 +32,39 @@ class EmptyKSuite extends KittensSuite: inline def emptyK[F[_]] = summonInline[EmptyK[F]].empty - inline def testEmptyK(context: String): Unit = - test(s"$context.EmptyK[LOption]")(assert(emptyK[LOption] == Nil)) - test(s"$context.EmptyK[PList]")(assert(emptyK[PList] == (Nil, Nil))) - test(s"$context.EmptyK[CaseClassWOption]")(assert(emptyK[CaseClassWOption] == CaseClassWOption(None))) - test(s"$context.EmptyK[NelOption]")(assert(emptyK[NelOption] == NonEmptyList.one(None))) - test(s"$context.EmptyK[IList]")(assert(emptyK[IList] == INil())) - test(s"$context.EmptyK[Snoc]")(assert(emptyK[Snoc] == SNil())) - test(s"$context.EmptyK respects existing instances")(assert(emptyK[BoxColor] == Box(Color(255, 255, 255)))) - checkAll(s"$context.EmptyK is Serializable", SerializableTests.serializable(summonInline[EmptyK[LOption]])) + inline def validate(instance: String): Unit = + test(s"$instance[LOption]")(assert(emptyK[LOption] == Nil)) + test(s"$instance[PList]")(assert(emptyK[PList] == (Nil, Nil))) + test(s"$instance[CaseClassWOption]")(assert(emptyK[CaseClassWOption] == CaseClassWOption(None))) + test(s"$instance[NelOption]")(assert(emptyK[NelOption] == NonEmptyList.one(None))) + test(s"$instance[IList]")(assert(emptyK[IList] == INil())) + test(s"$instance[Snoc]")(assert(emptyK[Snoc] == SNil())) + test(s"$instance respects existing instances")(assert(emptyK[BoxColor] == Box(Color(255, 255, 255)))) + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[EmptyK[LOption]])) locally { import auto.emptyK.given - testEmptyK("auto") + validate("auto.emptyK") } locally { import semiInstances.given - testEmptyK("semiauto") + validate("semiauto.emptyK") + } + + locally { + import derivedInstances.* + val instance = "derived.emptyK" + test(s"$instance[CaseClassWOption]")(assert(emptyK[CaseClassWOption].x.value.isEmpty)) + test(s"$instance[IList]")(assert(emptyK[IList].x == INil())) + test(s"$instance[Snoc]")(assert(emptyK[Snoc].x == SNil())) + checkAll(s"$instance is Serializable", SerializableTests.serializable(EmptyK[Snoc])) } end EmptyKSuite object EmptyKSuite: - import TestDefns.* + import ADTs.* type LOption[A] = List[Option[A]] type PList[A] = (List[A], List[A]) @@ -72,6 +80,11 @@ object EmptyKSuite: given EmptyK[Snoc] = semiauto.emptyK given EmptyK[BoxColor] = semiauto.emptyK + object derivedInstances: + case class CaseClassWOption[A](x: ADTs.CaseClassWOption[A]) derives EmptyK + case class IList[A](x: ADTs.IList[A]) derives EmptyK + case class Snoc[A](x: ADTs.Snoc[A]) derives EmptyK + final case class Color[A](r: Int, g: Int, b: Int) object Color: given EmptyK[Color] with diff --git a/core/src/test/scala-3/cats/derived/EmptyKTests.scala b/core/src/test/scala-3/cats/derived/EmptyKTests.scala deleted file mode 100644 index 3234d74a..00000000 --- a/core/src/test/scala-3/cats/derived/EmptyKTests.scala +++ /dev/null @@ -1,10 +0,0 @@ -package cats.derived - -import alleycats.* -import alleycats.std.all.* -import cats.* -import cats.derived.* - -class EmptyKTests { // - case class Foo[A](i: String, l: List[A]) derives EmptyK -} diff --git a/core/src/test/scala-3/cats/derived/EmptySuite.scala b/core/src/test/scala-3/cats/derived/EmptySuite.scala index 2886b13f..46040a34 100644 --- a/core/src/test/scala-3/cats/derived/EmptySuite.scala +++ b/core/src/test/scala-3/cats/derived/EmptySuite.scala @@ -18,30 +18,29 @@ package cats.derived import alleycats.Empty import cats.laws.discipline.SerializableTests - import scala.compiletime.* class EmptySuite extends KittensSuite: import EmptySuite.given import EmptySuite.* - import TestDefns.* + import ADTs.* inline def empty[A]: A = summonInline[Empty[A]].empty - inline def testEmpty(inline context: String): Unit = - test(s"$context.Empty[Foo]")(assert(empty[Foo] == Foo(0, None))) - test(s"$context.Empty[Outer]")(assert(empty[Outer] == Outer(Inner(0)))) - test(s"$context.Empty[Interleaved[String]]")(assert(empty[Interleaved[String]] == Interleaved.empty(""))) - test(s"$context.Empty[Recursive]")(assert(empty[Recursive] == Recursive(0, None))) - test(s"$context.Empty[IList[Dummy]]")(assert(empty[IList[Dummy]] == INil())) - test(s"$context.Empty[Snoc[Dummy]]")(assert(empty[Snoc[Dummy]] == SNil())) - test(s"$context.Empty respects existing instances")(assert(empty[Box[Mask]] == Box(Mask(0xffffffff)))) - checkAll(s"$context.Empty is Serializable", SerializableTests.serializable(summonInline[Empty[Foo]])) + inline def validate(inline instance: String): Unit = + test(s"$instance[Foo]")(assert(empty[Foo] == Foo(0, None))) + test(s"$instance[Outer]")(assert(empty[Outer] == Outer(Inner(0)))) + test(s"$instance[Interleaved[String]]")(assert(empty[Interleaved[String]] == Interleaved.empty(""))) + test(s"$instance[Recursive]")(assert(empty[Recursive] == Recursive(0, None))) + test(s"$instance[IList[Dummy]]")(assert(empty[IList[Dummy]] == INil())) + test(s"$instance[Snoc[Dummy]]")(assert(empty[Snoc[Dummy]] == SNil())) + test(s"$instance respects existing instances")(assert(empty[Box[Mask]] == Box(Mask(0xffffffff)))) + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[Empty[Foo]])) locally { import auto.empty.given - testEmpty("auto") + validate("auto.empty") testNoAuto("Empty", "IList[Int]") testNoAuto("Empty", "Snoc[Int]") testNoAuto("Empty", "Rgb") @@ -49,16 +48,29 @@ class EmptySuite extends KittensSuite: locally { import semiInstances.given - testEmpty("semiauto") + validate("semiauto.empty") testNoSemi("Empty", "IList[Int]") testNoSemi("Empty", "Snoc[Int]") testNoSemi("Empty", "Rgb") } + locally { + import derivedInstances.* + val instance = "derived.empty" + test(s"$instance[Foo]")(assert(empty[Foo].x == ADTs.Foo(0, None))) + test(s"$instance[Outer]")(assert(empty[Outer].x == ADTs.Outer(Inner(0)))) + test(s"$instance[Interleaved[String]]")(assert(empty[Interleaved[String]].x == ADTs.Interleaved.empty(""))) + test(s"$instance[Recursive]")(assert(empty[Recursive].x == ADTs.Recursive(0, None))) + test(s"$instance[IList[Dummy]]")(assert(empty[IList[Int]].x == INil())) + test(s"$instance[Snoc[Dummy]]")(assert(empty[Snoc[Int]].x == SNil())) + test(s"$instance respects existing instances")(assert(empty[BoxMask].x == Box(Mask(0xffffffff)))) + checkAll(s"$instance is Serializable", SerializableTests.serializable(Empty[Foo])) + } + end EmptySuite object EmptySuite: - import TestDefns.* + import ADTs.* // `Monoid[Option[A]]` gives us `Empty[Option[A]]` but it requires a `Semigroup[A]`. given [A]: Empty[Option[A]] = Empty(None) @@ -73,6 +85,15 @@ object EmptySuite: given Empty[Box[Mask]] = semiauto.empty given Empty[Chain] = semiauto.empty + object derivedInstances: + case class Foo(x: ADTs.Foo) derives Empty + case class Outer(x: ADTs.Outer) derives Empty + case class Interleaved[A](x: ADTs.Interleaved[A]) derives Empty + case class Recursive(x: ADTs.Recursive) derives Empty + case class IList[A](x: ADTs.IList[A]) derives Empty + case class Snoc[A](x: ADTs.Snoc[A]) derives Empty + case class BoxMask(x: Box[Mask]) derives Empty + trait Dummy final case class Chain(head: Int, tail: Chain) final case class Mask(bits: Int) diff --git a/core/src/test/scala-3/cats/derived/EmptyTests.scala b/core/src/test/scala-3/cats/derived/EmptyTests.scala deleted file mode 100644 index cc1e6c83..00000000 --- a/core/src/test/scala-3/cats/derived/EmptyTests.scala +++ /dev/null @@ -1,11 +0,0 @@ -package cats.derived - -import alleycats.* -import cats.* -import cats.derived.* - -object EmptyTests: - case class Foo(i: Int, b: IntTree) derives Empty - enum IntTree: - case Leaf - case Node(left: IntTree, value: Int, right: IntTree) diff --git a/core/src/test/scala-3/cats/derived/EqSuite.scala b/core/src/test/scala-3/cats/derived/EqSuite.scala index adf99d51..bce4a3ee 100644 --- a/core/src/test/scala-3/cats/derived/EqSuite.scala +++ b/core/src/test/scala-3/cats/derived/EqSuite.scala @@ -17,41 +17,53 @@ package cats.derived import cats.Eq -import cats.kernel.laws.discipline.{EqTests, SerializableTests} - +import cats.kernel.laws.discipline.* import scala.compiletime.* class EqSuite extends KittensSuite.WithoutEq: import EqSuite.* - import TestDefns.* + import ADTs.* - inline def eqTests[A]: EqTests[A] = + inline def tests[A]: EqTests[A] = EqTests[A](summonInline) - inline def testEq(inline context: String): Unit = - checkAll(s"$context.Eq[Foo]]", eqTests[Foo].eqv) - checkAll(s"$context.Eq[IList[Int]]", eqTests[IList[Int]].eqv) - checkAll(s"$context.Eq[Inner]", eqTests[Inner].eqv) - checkAll(s"$context.Eq[Outer]", eqTests[Outer].eqv) - checkAll(s"$context.Eq[Interleaved[Int]]", eqTests[Interleaved[Int]].eqv) - checkAll(s"$context.Eq[Tree[Int]]", eqTests[Tree[Int]].eqv) - checkAll(s"$context.Eq[Recursive]", eqTests[Recursive].eqv) - checkAll(s"$context.Eq is Serializable", SerializableTests.serializable(summonInline[Eq[Foo]])) + inline def validate(inline instance: String): Unit = + checkAll(s"$instance[Foo]]", tests[Foo].eqv) + checkAll(s"$instance[IList[Int]]", tests[IList[Int]].eqv) + checkAll(s"$instance[Inner]", tests[Inner].eqv) + checkAll(s"$instance[Outer]", tests[Outer].eqv) + checkAll(s"$instance[Interleaved[Int]]", tests[Interleaved[Int]].eqv) + checkAll(s"$instance[Tree[Int]]", tests[Tree[Int]].eqv) + checkAll(s"$instance[Recursive]", tests[Recursive].eqv) + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[Eq[Foo]])) locally { import auto.eq.given - testEq("auto") + validate("auto.eq") } locally { import semiInstances.given - testEq("semiauto") + validate("semiauto.eq") + } + + locally { + import derivedInstances.* + val instance = "derived.eq" + checkAll(s"$instance[Foo]]", tests[Foo].eqv) + checkAll(s"$instance[IList[Int]]", tests[IList[Int]].eqv) + checkAll(s"$instance[Inner]", tests[Inner].eqv) + checkAll(s"$instance[Outer]", tests[Outer].eqv) + checkAll(s"$instance[Interleaved[Int]]", tests[Interleaved[Int]].eqv) + checkAll(s"$instance[Tree[Int]]", tests[Tree[Int]].eqv) + checkAll(s"$instance[Recursive]", tests[Recursive].eqv) + checkAll(s"$instance is Serializable", SerializableTests.serializable(Eq[Foo])) } end EqSuite object EqSuite: - import TestDefns.* + import ADTs.* object semiInstances: given Eq[Foo] = semiauto.eq @@ -62,4 +74,13 @@ object EqSuite: given Eq[Tree[Int]] = semiauto.eq given Eq[Recursive] = semiauto.eq + object derivedInstances: + case class Foo(x: ADTs.Foo) derives Eq + case class IList[A](x: ADTs.IList[A]) derives Eq + case class Inner(x: ADTs.Inner) derives Eq + case class Outer(x: ADTs.Outer) derives Eq + case class Interleaved[A](x: ADTs.Interleaved[A]) derives Eq + case class Tree[A](x: ADTs.Tree[A]) derives Eq + case class Recursive(x: ADTs.Recursive) derives Eq + end EqSuite diff --git a/core/src/test/scala-3/cats/derived/EqTests.scala b/core/src/test/scala-3/cats/derived/EqTests.scala deleted file mode 100644 index b9a128ce..00000000 --- a/core/src/test/scala-3/cats/derived/EqTests.scala +++ /dev/null @@ -1,9 +0,0 @@ -package cats.derived - -import alleycats.* -import cats.* -import cats.derived.* - -class EqTests { // - case class Foo(i: Int, b: Option[String]) derives Eq -} diff --git a/core/src/test/scala-3/cats/derived/FoldableSuite.scala b/core/src/test/scala-3/cats/derived/FoldableSuite.scala index 0b748f3a..7bef2b80 100644 --- a/core/src/test/scala-3/cats/derived/FoldableSuite.scala +++ b/core/src/test/scala-3/cats/derived/FoldableSuite.scala @@ -14,47 +14,64 @@ * limitations under the License. */ -package cats -package derived +package cats.derived -import cats.laws.discipline.{FoldableTests, SerializableTests} -import cats.syntax.all.* -import org.scalacheck.Arbitrary +import cats.{Eval, Foldable} +import cats.laws.discipline.* +import cats.syntax.all.given import scala.compiletime.* class FoldableSuite extends KittensSuite: import FoldableSuite.* - import TestDefns.* + import ADTs.* - inline def foldableTests[F[_]]: FoldableTests[F] = + inline def tests[F[_]]: FoldableTests[F] = FoldableTests[F](summonInline) - inline def testFoldable(inline context: String): Unit = - checkAll(s"$context.Foldable[IList]", foldableTests[IList].foldable[Int, Long]) - checkAll(s"$context.Foldable[Tree]", foldableTests[Tree].foldable[Int, Long]) - checkAll(s"$context.Foldable[GenericAdt]", foldableTests[GenericAdt].foldable[Int, Long]) - checkAll(s"$context.Foldable[OptList]", foldableTests[OptList].foldable[Int, Long]) - checkAll(s"$context.Foldable[ListSnoc]", foldableTests[ListSnoc].foldable[Int, Long]) - checkAll(s"$context.Foldable[AndChar]", foldableTests[AndChar].foldable[Int, Long]) - checkAll(s"$context.Foldable[Interleaved]", foldableTests[Interleaved].foldable[Int, Long]) - checkAll(s"$context.Foldable[BoxNel]", foldableTests[BoxNel].foldable[Int, Long]) - checkAll(s"$context.Foldable[EnumK1]", foldableTests[EnumK1].foldable[Int, Long]) - checkAll(s"$context.Foldable is Serializable", SerializableTests.serializable(summonInline[Foldable[Tree]])) + inline def validate(inline instance: String): Unit = + checkAll(s"$instance[IList]", tests[IList].foldable[Int, Long]) + checkAll(s"$instance[Tree]", tests[Tree].foldable[Int, Long]) + checkAll(s"$instance[GenericAdt]", tests[GenericAdt].foldable[Int, Long]) + checkAll(s"$instance[OptList]", tests[OptList].foldable[Int, Long]) + checkAll(s"$instance[ListSnoc]", tests[ListSnoc].foldable[Int, Long]) + checkAll(s"$instance[AndChar]", tests[AndChar].foldable[Int, Long]) + checkAll(s"$instance[Interleaved]", tests[Interleaved].foldable[Int, Long]) + checkAll(s"$instance[BoxNel]", tests[BoxNel].foldable[Int, Long]) + checkAll(s"$instance[EnumK1]", tests[EnumK1].foldable[Int, Long]) + checkAll(s"$instance[Many]", tests[Many].foldable[Int, Long]) + checkAll(s"$instance[AtMostOne]", tests[AtMostOne].foldable[Int, Long]) + checkAll(s"$instance[AtLeastOne]", tests[AtLeastOne].foldable[Int, Long]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[Foldable[Tree]])) locally { import auto.foldable.given - testFoldable("auto") + validate("auto.foldable") } locally { import semiInstances.given - testFoldable("semiauto") + validate("semiauto.foldable") + } + + locally { + import derivedInstances.* + val instance = "derived.foldable" + checkAll(s"$instance[IList]", tests[IList].foldable[Int, Long]) + checkAll(s"$instance[Tree]", tests[Tree].foldable[Int, Long]) + checkAll(s"$instance[GenericAdt]", tests[GenericAdt].foldable[Int, Long]) + checkAll(s"$instance[AndChar]", tests[AndChar].foldable[Int, Long]) + checkAll(s"$instance[Interleaved]", tests[Interleaved].foldable[Int, Long]) + checkAll(s"$instance[EnumK1]", tests[EnumK1].foldable[Int, Long]) + checkAll(s"$instance[Many]", tests[Many].foldable[Int, Long]) + checkAll(s"$instance[AtMostOne]", tests[AtMostOne].foldable[Int, Long]) + checkAll(s"$instance[AtLeastOne]", tests[AtLeastOne].foldable[Int, Long]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(Foldable[Tree])) } end FoldableSuite object FoldableSuite: - import TestDefns.* + import ADTs.* type OptList[A] = Option[List[A]] type ListSnoc[A] = List[Snoc[A]] @@ -71,34 +88,25 @@ object FoldableSuite: given Foldable[Interleaved] = semiauto.foldable given Foldable[BoxNel] = semiauto.foldable given Foldable[EnumK1] = semiauto.foldable + given Foldable[Many] = semiauto.foldable + given Foldable[AtMostOne] = semiauto.foldable + given Foldable[AtLeastOne] = semiauto.foldable + + object derivedInstances: + case class IList[A](x: ADTs.IList[A]) derives Foldable + case class Tree[A](x: ADTs.Tree[A]) derives Foldable + case class GenericAdt[A](x: ADTs.GenericAdt[A]) derives Foldable + case class Interleaved[A](x: ADTs.Interleaved[A]) derives Foldable + case class EnumK1[A](x: ADTs.EnumK1[A]) derives Foldable + case class AndChar[A](x: FoldableSuite.AndChar[A]) derives Foldable + case class Many[+A](x: ADTs.Many[A]) derives Foldable + case class AtMostOne[+A](x: ADTs.AtMostOne[A]) derives Foldable + case class AtLeastOne[+A](x: ADTs.AtLeastOne[A]) derives Foldable final case class Nel[+A](head: A, tail: List[A]) object Nel: - given [A: Eq]: Eq[Nel[A]] = - (x, y) => x.head === y.head && x.tail === y.tail - - given [A: Arbitrary]: Arbitrary[Nel[A]] = - Arbitrary(for - head <- Arbitrary.arbitrary[A] - tail <- Arbitrary.arbitrary[List[A]] - yield Nel(head, tail)) - given Foldable[Nel] with def foldLeft[A, B](fa: Nel[A], b: B)(f: (B, A) => B) = fa.tail.foldl(b)(f) def foldRight[A, B](fa: Nel[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]) = fa.tail.foldr(lb)(f) - case class Single[A](value: A) derives Foldable - - enum Many[+A] derives Foldable: - case Naught - case More(value: A, rest: Many[A]) - - enum AtMostOne[+A] derives Foldable: - case Naught - case Single(value: A) - - enum AtLeastOne[+A] derives Foldable: - case Single(value: A) - case More(value: A, rest: Option[AtLeastOne[A]]) - end FoldableSuite diff --git a/core/src/test/scala-3/cats/derived/FunctorSuite.scala b/core/src/test/scala-3/cats/derived/FunctorSuite.scala index c4d64abe..b13f7159 100644 --- a/core/src/test/scala-3/cats/derived/FunctorSuite.scala +++ b/core/src/test/scala-3/cats/derived/FunctorSuite.scala @@ -14,49 +14,67 @@ * limitations under the License. */ -package cats -package derived +package cats.derived +import cats.Functor import cats.laws.discipline.* import cats.laws.discipline.eq.* import scala.compiletime.* class FunctorSuite extends KittensSuite: import FunctorSuite.* - import TestDefns.* + import ADTs.* given ExhaustiveCheck[Predicate[Boolean]] = ExhaustiveCheck.instance(List(_ => true, _ => false, identity, !_)) - inline def functorTests[F[_]]: FunctorTests[F] = + inline def tests[F[_]]: FunctorTests[F] = FunctorTests[F](summonInline) - inline def testFunctor(inline context: String): Unit = - checkAll(s"$context.Functor[IList]", functorTests[IList].functor[Int, String, Long]) - checkAll(s"$context.Functor[Tree]", functorTests[Tree].functor[Int, String, Long]) - checkAll(s"$context.Functor[GenericAdt]", functorTests[GenericAdt].functor[Int, String, Long]) - checkAll(s"$context.Functor[OptList]", functorTests[OptList].functor[Int, String, Long]) - checkAll(s"$context.Functor[ListSnoc]", functorTests[ListSnoc].functor[Int, String, Long]) - checkAll(s"$context.Functor[AndChar]", functorTests[AndChar].functor[Int, String, Long]) - checkAll(s"$context.Functor[Interleaved]", functorTests[Interleaved].functor[Int, String, Long]) - checkAll(s"$context.Functor[NestedPred]", functorTests[NestedPred].functor[Boolean, Int, Boolean]) - checkAll(s"$context.Functor[EnumK1]", functorTests[EnumK1].functor[Boolean, Int, Boolean]) - checkAll(s"$context.Functor is Serializable", SerializableTests.serializable(summonInline[Functor[Tree]])) + inline def validate(inline instance: String): Unit = + checkAll(s"$instance[IList]", tests[IList].functor[Int, String, Long]) + checkAll(s"$instance[Tree]", tests[Tree].functor[Int, String, Long]) + checkAll(s"$instance[GenericAdt]", tests[GenericAdt].functor[Int, String, Long]) + checkAll(s"$instance[OptList]", tests[OptList].functor[Int, String, Long]) + checkAll(s"$instance[ListSnoc]", tests[ListSnoc].functor[Int, String, Long]) + checkAll(s"$instance[AndChar]", tests[AndChar].functor[Int, String, Long]) + checkAll(s"$instance[Interleaved]", tests[Interleaved].functor[Int, String, Long]) + checkAll(s"$instance[NestedPred]", tests[NestedPred].functor[Boolean, Int, Boolean]) + checkAll(s"$instance[EnumK1]", tests[EnumK1].functor[Boolean, Int, Boolean]) + checkAll(s"$instance[Many]", tests[Many].functor[Boolean, Int, Boolean]) + checkAll(s"$instance[AtMostOne]", tests[AtMostOne].functor[Boolean, Int, Boolean]) + checkAll(s"$instance[AtLeastOne]", tests[AtLeastOne].functor[Boolean, Int, Boolean]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[Functor[Tree]])) locally { import auto.functor.given - testFunctor("auto") + validate("auto.functor") } locally { import semiInstances.given - testFunctor("semiauto") + validate("semiauto.functor") + } + + locally { + import derivedInstances.* + val instance = "derived.functor" + checkAll(s"$instance[IList]", tests[IList].functor[Int, String, Long]) + checkAll(s"$instance[Tree]", tests[Tree].functor[Int, String, Long]) + checkAll(s"$instance[GenericAdt]", tests[GenericAdt].functor[Int, String, Long]) + checkAll(s"$instance[AndChar]", tests[AndChar].functor[Int, String, Long]) + checkAll(s"$instance[Interleaved]", tests[Interleaved].functor[Int, String, Long]) + checkAll(s"$instance[EnumK1]", tests[EnumK1].functor[Boolean, Int, Boolean]) + checkAll(s"$instance[Many]", tests[Many].functor[Boolean, Int, Boolean]) + checkAll(s"$instance[AtMostOne]", tests[AtMostOne].functor[Boolean, Int, Boolean]) + checkAll(s"$instance[AtLeastOne]", tests[AtLeastOne].functor[Boolean, Int, Boolean]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(Functor[Tree])) } end FunctorSuite object FunctorSuite: - import TestDefns.* + import ADTs.* type OptList[A] = Option[List[A]] type ListSnoc[A] = List[Snoc[A]] @@ -74,19 +92,19 @@ object FunctorSuite: given Functor[Interleaved] = semiauto.functor given Functor[NestedPred] = semiauto.functor given Functor[EnumK1] = semiauto.functor - - case class Single[A](value: A) derives Functor - - enum Many[+A] derives Functor: - case Naught - case More(value: A, rest: Many[A]) - - enum AtMostOne[+A] derives Functor: - case Naught - case Single(value: A) - - enum AtLeastOne[+A] derives Functor: - case Single(value: A) - case More(value: A, rest: Option[AtLeastOne[A]]) + given Functor[Many] = semiauto.functor + given Functor[AtMostOne] = semiauto.functor + given Functor[AtLeastOne] = semiauto.functor + + object derivedInstances: + case class IList[A](x: ADTs.IList[A]) derives Functor + case class Tree[A](x: ADTs.Tree[A]) derives Functor + case class GenericAdt[A](x: ADTs.GenericAdt[A]) derives Functor + case class Interleaved[A](x: ADTs.Interleaved[A]) derives Functor + case class EnumK1[A](x: ADTs.EnumK1[A]) derives Functor + case class AndChar[A](x: FoldableSuite.AndChar[A]) derives Functor + case class Many[+A](x: ADTs.Many[A]) derives Functor + case class AtMostOne[+A](x: ADTs.AtMostOne[A]) derives Functor + case class AtLeastOne[+A](x: ADTs.AtLeastOne[A]) derives Functor end FunctorSuite diff --git a/core/src/test/scala-3/cats/derived/HashSuite.scala b/core/src/test/scala-3/cats/derived/HashSuite.scala index d222fbb7..87a88f76 100644 --- a/core/src/test/scala-3/cats/derived/HashSuite.scala +++ b/core/src/test/scala-3/cats/derived/HashSuite.scala @@ -1,43 +1,56 @@ package cats.derived import cats.Hash -import cats.kernel.laws.discipline.{HashTests, SerializableTests} - +import cats.kernel.laws.discipline.* import scala.compiletime.* import scala.util.hashing.MurmurHash3 class HashSuite extends KittensSuite: import HashSuite.* - import TestDefns.* + import ADTs.* - inline def hashTests[A]: HashTests[A] = + inline def tests[A]: HashTests[A] = HashTests[A](summonInline) - inline def testHash(inline context: String): Unit = - checkAll(s"$context.Hash[IList[Int]]", hashTests[IList[Int]].hash) - checkAll(s"$context.Hash[Inner]", hashTests[Inner].hash) - checkAll(s"$context.Hash[Outer]", hashTests[Outer].hash) + inline def validate(inline instance: String): Unit = + checkAll(s"$instance[IList[Int]]", tests[IList[Int]].hash) + checkAll(s"$instance[Inner]", tests[Inner].hash) + checkAll(s"$instance[Outer]", tests[Outer].hash) // FIXME: typelevel/cats#2878 - // checkAll(s"$context.Hash[Interleaved[Int]]", hashTests[Interleaved[Int]].hash) - checkAll(s"$context.Hash[Tree[Int]]", hashTests[Tree[Int]].hash) - checkAll(s"$context.Hash[Recursive]", hashTests[Recursive].hash) - checkAll(s"$context.Hash[EnumK0]", hashTests[EnumK0].hash) - checkAll(s"$context.Hash is Serializable", SerializableTests.serializable(summonInline[Hash[Inner]])) + // checkAll(s"$instance[Interleaved[Int]]", tests[Interleaved[Int]].hash) + checkAll(s"$instance[Tree[Int]]", tests[Tree[Int]].hash) + checkAll(s"$instance[Recursive]", tests[Recursive].hash) + checkAll(s"$instance[EnumK0]", tests[EnumK0].hash) + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[Hash[Inner]])) locally { import auto.hash.given - testHash("auto") + validate("auto.hash") } locally { import semiInstances.given - testHash("semiauto") + validate("semiauto.hash") + } + + locally { + import derivedInstances.* + val instance = "derived.hash" + checkAll(s"$instance[IList[Int]]", tests[IList[Int]].hash) + checkAll(s"$instance[Inner]", tests[Inner].hash) + checkAll(s"$instance[Outer]", tests[Outer].hash) + // FIXME: typelevel/cats#2878 + // checkAll(s"$context[Interleaved[Int]]", tests[Interleaved[Int]].hash) + checkAll(s"$instance[Tree[Int]]", tests[Tree[Int]].hash) + checkAll(s"$instance[Recursive]", tests[Recursive].hash) + checkAll(s"$instance[EnumK0]", tests[EnumK0].hash) + checkAll(s"$instance is Serializable", SerializableTests.serializable(Hash[Inner])) } end HashSuite object HashSuite: - import TestDefns.* + import ADTs.* object semiInstances: given Hash[IList[Int]] = semiauto.hash @@ -48,4 +61,13 @@ object HashSuite: given Hash[Recursive] = semiauto.hash given Hash[EnumK0] = semiauto.hash + object derivedInstances: + case class IList[A](x: ADTs.IList[A]) derives Hash + case class Inner(x: ADTs.Inner) derives Hash + case class Outer(x: ADTs.Outer) derives Hash + case class Interleaved[A](x: ADTs.Interleaved[A]) derives Hash + case class Tree[A](x: ADTs.Tree[A]) derives Hash + case class Recursive(x: ADTs.Recursive) derives Hash + case class EnumK0(x: ADTs.EnumK0) derives Hash + end HashSuite diff --git a/core/src/test/scala-3/cats/derived/HashTests.scala b/core/src/test/scala-3/cats/derived/HashTests.scala deleted file mode 100644 index 49de408d..00000000 --- a/core/src/test/scala-3/cats/derived/HashTests.scala +++ /dev/null @@ -1,9 +0,0 @@ -package cats.derived - -import alleycats.* -import cats.* -import cats.derived.* - -class HashTests { // - case class Foo(i: Int, b: Option[String]) derives Hash -} diff --git a/core/src/test/scala-3/cats/derived/InvariantSuite.scala b/core/src/test/scala-3/cats/derived/InvariantSuite.scala index 5a99b794..c89d202d 100644 --- a/core/src/test/scala-3/cats/derived/InvariantSuite.scala +++ b/core/src/test/scala-3/cats/derived/InvariantSuite.scala @@ -14,63 +14,62 @@ * limitations under the License. */ -package cats -package derived +package cats.derived +import cats.Invariant import cats.laws.discipline.* import cats.laws.discipline.arbitrary.* import cats.laws.discipline.eq.* -import cats.laws.discipline.* - import scala.compiletime.* class InvariantSuite extends KittensSuite: import InvariantSuite.* - import TestDefns.* + import ADTs.* - inline def invariantTests[F[_]]: InvariantTests[F] = + inline def tests[F[_]]: InvariantTests[F] = InvariantTests[F](summonInline) - inline def testInvariant(context: String): Unit = { - checkAll(s"$context.Invariant[TreeF]", invariantTests[TreeF].invariant[MiniInt, String, Boolean]) - checkAll(s"$context.Invariant[GenAdtF]", invariantTests[GenericAdtF].invariant[MiniInt, String, Boolean]) - // TODO https://github.com/typelevel/kittens/issues/473 - // checkAll(s"$context.Invariant[InterleavedF]", invariantTests[InterleavedF].invariant[MiniInt, String, Boolean]) - checkAll(s"$context.Invariant[AndCharF]", invariantTests[AndCharF].invariant[MiniInt, String, Boolean]) - checkAll(s"$context.Invariant[ListSnoc", invariantTests[ListSnoc].invariant[MiniInt, String, Boolean]) - checkAll(s"$context.Invariant[Bivariant]", invariantTests[Bivariant].invariant[MiniInt, String, Boolean]) - checkAll(s"$context.Invariant[EnumK1Inv]", invariantTests[EnumK1Inv].invariant[MiniInt, String, Boolean]) - checkAll(s"$context.Invariant is Serializable", SerializableTests.serializable(summonInline[Invariant[TreeF]])) - - // TODO https://github.com/typelevel/kittens/issues/476 - // test(s"$context.Invariant.imap is stack safe") { - // val I = summonInline[Invariant[ListSnoc]] - // val J = summonInline[Invariant[IList]] - // val n = 10000 - // val largeIList = IList.fromSeq(1 until n) - // val largeSnoc = Snoc.fromSeq(1 until n) :: Nil - // val actualIList = IList.toList(J.imap(largeIList)(_ + 1)(_ - 1)) - // val actualSnoc = I.imap(largeSnoc)(_ + 1)(_ - 1).flatMap(Snoc.toList) - // val expected = (2 until n + 1).toList - // assert(actualIList == expected) - // assert(actualSnoc == expected) - // } - } + inline def validate(instance: String): Unit = + checkAll(s"$instance[TreeF]", tests[TreeF].invariant[MiniInt, String, Boolean]) + checkAll(s"$instance[GenAdtF]", tests[GenericAdtF].invariant[MiniInt, String, Boolean]) + // TODO: https://github.com/typelevel/kittens/issues/473 + // checkAll(s"$instance[InterleavedF]", tests[InterleavedF].invariant[MiniInt, String, Boolean]) + checkAll(s"$instance[AndCharF]", tests[AndCharF].invariant[MiniInt, String, Boolean]) + checkAll(s"$instance[ListSnoc]", tests[ListSnoc].invariant[MiniInt, String, Boolean]) + checkAll(s"$instance[IList]", tests[IList].invariant[MiniInt, String, Boolean]) + checkAll(s"$instance[Bivariant]", tests[Bivariant].invariant[MiniInt, String, Boolean]) + checkAll(s"$instance[EnumK1Inv]", tests[EnumK1Inv].invariant[MiniInt, String, Boolean]) + checkAll(s"$instance[Many]", tests[Many].invariant[MiniInt, String, Boolean]) + checkAll(s"$instance[AtLeastOne]", tests[AtLeastOne].invariant[MiniInt, String, Boolean]) + checkAll(s"$instance[AtMostOne]", tests[AtMostOne].invariant[MiniInt, String, Boolean]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[Invariant[TreeF]])) locally { import auto.invariant.given - testInvariant("auto") + validate("auto.invariant") } locally { import semiInstances.given - testInvariant("semiauto") + validate("semiauto.invariant") + } + + locally { + import derivedInstances.* + val instance = "derived.invariant" + checkAll(s"$instance[IList]", tests[IList].invariant[MiniInt, String, Boolean]) + checkAll(s"$instance[Bivariant]", tests[Bivariant].invariant[MiniInt, String, Boolean]) + checkAll(s"$instance[EnumK1Inv]", tests[EnumK1Inv].invariant[MiniInt, String, Boolean]) + checkAll(s"$instance[Many]", tests[Many].invariant[MiniInt, String, Boolean]) + checkAll(s"$instance[AtLeastOne]", tests[AtLeastOne].invariant[MiniInt, String, Boolean]) + checkAll(s"$instance[AtMostOne]", tests[AtMostOne].invariant[MiniInt, String, Boolean]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(Invariant[AtMostOne])) } end InvariantSuite object InvariantSuite: - import TestDefns.* + import ADTs.* type ListSnoc[A] = List[Snoc[A]] type GenericAdtF[A] = GenericAdt[A => Boolean] @@ -82,6 +81,7 @@ object InvariantSuite: object semiInstances: given Invariant[GenericAdtF] = semiauto.invariant given Invariant[ListFToInt] = semiauto.invariant + // TODO: https://github.com/typelevel/kittens/issues/473 // given Invariant[InterleavedF] = semiauto.invariant given Invariant[AndCharF] = semiauto.invariant given Invariant[TreeF] = semiauto.invariant @@ -90,19 +90,16 @@ object InvariantSuite: given Invariant[Bivariant] = semiauto.invariant given Invariant[IList] = semiauto.invariant given Invariant[EnumK1Inv] = semiauto.invariant - - case class Single[A](value: A) derives Invariant - - enum Many[A] derives Invariant: - case Naught() - case More(value: A, rest: Many[A]) - - enum AtMostOne[A] derives Invariant: - case Naught() - case Single(value: A) - - enum AtLeastOne[A] derives Invariant: - case Single(value: A) - case More(value: A, rest: Option[AtLeastOne[A]]) + given Invariant[Many] = semiauto.invariant + given Invariant[AtMostOne] = semiauto.invariant + given Invariant[AtLeastOne] = semiauto.invariant + + object derivedInstances: + case class Bivariant[A](x: ADTs.Bivariant[A]) derives Invariant + case class IList[A](x: ADTs.IList[A]) derives Invariant + case class EnumK1Inv[A](x: ADTs.EnumK1Inv[A]) derives Invariant + case class Many[A](x: ADTs.Many[A]) derives Invariant + case class AtMostOne[A](x: ADTs.AtMostOne[A]) derives Invariant + case class AtLeastOne[A](x: ADTs.AtLeastOne[A]) derives Invariant end InvariantSuite diff --git a/core/src/test/scala-3/cats/derived/KittensSuite.scala b/core/src/test/scala-3/cats/derived/KittensSuite.scala index 672656be..526b7139 100644 --- a/core/src/test/scala-3/cats/derived/KittensSuite.scala +++ b/core/src/test/scala-3/cats/derived/KittensSuite.scala @@ -16,18 +16,23 @@ package cats.derived +import cats.Eq import cats.platform.Platform import cats.syntax.AllSyntax import munit.DisciplineSuite -import org.scalacheck.Arbitrary +import org.scalacheck.{Arbitrary, Cogen} import org.scalacheck.Test.Parameters +import scala.deriving.Mirror import scala.quoted.* /** An opinionated stack of traits to improve consistency and reduce boilerplate in Kittens tests. Note that unlike the * corresponding CatsSuite in the Cat project, this trait does not mix in any instances. */ -abstract class KittensSuite extends KittensSuite.WithoutEq, TestEqInstances +abstract class KittensSuite extends KittensSuite.WithoutEq, ADTs.EqInstances: + given [A <: Product](using mirror: Mirror.ProductOf[A], via: Eq[mirror.MirroredElemTypes]): Eq[A] = + Eq.by(Tuple.fromProductTyped) + object KittensSuite: def deCapitalizeMacro(str: Expr[String])(using Quotes) = val value = str.valueOrAbort @@ -48,6 +53,12 @@ object KittensSuite: given [A: Arbitrary]: Arbitrary[List[A]] = Arbitrary.arbContainer + given [A <: Product](using mirror: Mirror.ProductOf[A], via: Arbitrary[mirror.MirroredElemTypes]): Arbitrary[A] = + Arbitrary(via.arbitrary.map(mirror.fromTuple)) + + given [A <: Product](using mirror: Mirror.ProductOf[A], via: Cogen[mirror.MirroredElemTypes]): Cogen[A] = + via.contramap(Tuple.fromProductTyped) + inline def testNoInstance(inline tc: String, target: String, message: String): Unit = val errors = compileErrors(tc + "[" + target + "]") test(s"No $tc for $target")(assert(errors.contains(message), s"$errors did not contain $message")) diff --git a/core/src/test/scala-3/cats/derived/MonoidKSuite.scala b/core/src/test/scala-3/cats/derived/MonoidKSuite.scala index 84890097..d85b884b 100644 --- a/core/src/test/scala-3/cats/derived/MonoidKSuite.scala +++ b/core/src/test/scala-3/cats/derived/MonoidKSuite.scala @@ -1,24 +1,22 @@ package cats.derived -import alleycats.* -import cats.* +import cats.MonoidK import cats.laws.discipline.{MonoidKTests, SerializableTests} -import org.scalacheck.Arbitrary import scala.compiletime.* class MonoidKSuite extends KittensSuite: import MonoidKSuite.* - import TestDefns.* + import ADTs.* - inline def monoidKTests[F[_]]: MonoidKTests[F] = + inline def tests[F[_]]: MonoidKTests[F] = MonoidKTests[F](summonInline) - inline def testMonoidK(context: String): Unit = - checkAll(s"$context.MonoidK[ComplexProduct]", monoidKTests[ComplexProduct].monoidK[Char]) - checkAll(s"$context.MonoidK[CaseClassWOption]", monoidKTests[CaseClassWOption].monoidK[Char]) - checkAll(s"$context.MonoidK[BoxMul]", monoidKTests[BoxMul].monoidK[Char]) - checkAll(s"$context.MonoidK is Serializable", SerializableTests.serializable(summonInline[MonoidK[ComplexProduct]])) - test(s"$context.MonoidK respects existing instances") { + inline def validate(instance: String): Unit = + checkAll(s"$instance[ComplexProduct]", tests[ComplexProduct].monoidK[Char]) + checkAll(s"$instance[CaseClassWOption]", tests[CaseClassWOption].monoidK[Char]) + checkAll(s"$instance[BoxMul]", tests[BoxMul].monoidK[Char]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[MonoidK[ComplexProduct]])) + test(s"$instance respects existing instances") { val M = summonInline[MonoidK[BoxMul]] assert(M.empty[Char] == Box(Mul[Char](1))) assert(M.combineK(Box(Mul[Char](5)), Box(Mul[Char](5))) == Box(Mul[Char](25))) @@ -26,37 +24,45 @@ class MonoidKSuite extends KittensSuite: locally { import auto.monoidK.given - testMonoidK("auto") + validate("auto.monoidK") } locally { - import monInstances.given - testMonoidK("semi") + import semiInstances.given + validate("semiauto.monoidK") + } + + locally { + import derivedInstances.* + val instance = "derived.monoidK" + checkAll(s"$instance[ComplexProduct]", tests[ComplexProduct].monoidK[Char]) + checkAll(s"$instance[CaseClassWOption]", tests[CaseClassWOption].monoidK[Char]) + checkAll(s"$instance[Simple]", tests[Simple].monoidK[Char]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(MonoidK[ComplexProduct])) } end MonoidKSuite object MonoidKSuite: - import TestDefns.* + import ADTs.* type BoxMul[A] = Box[Mul[A]] - object monInstances: + object semiInstances: given MonoidK[ComplexProduct] = semiauto.monoidK given MonoidK[CaseClassWOption] = semiauto.monoidK given MonoidK[BoxMul] = semiauto.monoidK + object derivedInstances: + case class ComplexProduct[A](x: ADTs.ComplexProduct[A]) derives MonoidK + case class CaseClassWOption[A](x: ADTs.CaseClassWOption[A]) derives MonoidK + case class Simple[A](value1: List[A], value2: Set[A]) derives MonoidK + case class Recursive[A](first: List[A], rest: Recursive[A]) derives MonoidK + final case class Mul[T](value: Int) object Mul: - given [T]: Eq[Mul[T]] = Eq.by(_.value) - - given [T]: Arbitrary[Mul[T]] = Arbitrary(Arbitrary.arbitrary[Int].map(apply)) - given MonoidK[Mul] with def empty[A] = Mul(1) def combineK[A](x: Mul[A], y: Mul[A]) = Mul(x.value * y.value) - case class Simple[A](value1: List[A], value2: Set[A]) derives MonoidK - case class Recursive[A](first: List[A], rest: Recursive[A]) derives MonoidK - end MonoidKSuite diff --git a/core/src/test/scala-3/cats/derived/MonoidSuite.scala b/core/src/test/scala-3/cats/derived/MonoidSuite.scala index f19417f8..3651cb82 100644 --- a/core/src/test/scala-3/cats/derived/MonoidSuite.scala +++ b/core/src/test/scala-3/cats/derived/MonoidSuite.scala @@ -16,26 +16,24 @@ package cats.derived -import cats.{Eq, Monoid} +import cats.Monoid import cats.kernel.laws.discipline.{MonoidTests, SerializableTests} -import org.scalacheck.Arbitrary - import scala.compiletime.* class MonoidSuite extends KittensSuite: import MonoidSuite.* - import TestDefns.* + import ADTs.* - inline def monoidTests[A]: MonoidTests[A] = + inline def tests[A]: MonoidTests[A] = MonoidTests[A](summonInline) - inline def testMonoid(inline context: String): Unit = - checkAll(s"$context.Monoid[Foo]", monoidTests[Foo].monoid) - checkAll(s"$context.Monoid[Interleaved[Int]]", monoidTests[Interleaved[Int]].monoid) - checkAll(s"$context.Monoid[Box[Mul]]", monoidTests[Box[Mul]].monoid) - checkAll(s"$context.Monoid[Recursive]", monoidTests[Recursive].monoid) - checkAll(s"$context.Monoid is Serializable", SerializableTests.serializable(summonInline[Monoid[Foo]])) - test(s"$context.Monoid respects existing instances") { + inline def validate(inline instance: String): Unit = + checkAll(s"$instance[Foo]", tests[Foo].monoid) + checkAll(s"$instance[Interleaved[Int]]", tests[Interleaved[Int]].monoid) + checkAll(s"$instance[Box[Mul]]", tests[Box[Mul]].monoid) + checkAll(s"$instance[Recursive]", tests[Recursive].monoid) + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[Monoid[Foo]])) + test(s"$instance respects existing instances") { val box = summonInline[Monoid[Box[Mul]]] assert(box.empty == Box(Mul(1))) assert(box.combine(Box(Mul(5)), Box(Mul(5))) == Box(Mul(25))) @@ -43,18 +41,27 @@ class MonoidSuite extends KittensSuite: locally { import auto.monoid.given - testMonoid("auto") + validate("auto.monoid") } locally { import semiInstances.given - testMonoid("semiauto") + validate("semiauto.monoid") + } + + locally { + import derivedInstances.* + val instance = "derived.monoid" + checkAll(s"$instance[Foo]", tests[Foo].monoid) + checkAll(s"$instance[Interleaved[Int]]", tests[Interleaved[Int]].monoid) + checkAll(s"$instance[BoxMul]", tests[BoxMul].monoid) + checkAll(s"$instance is Serializable", SerializableTests.serializable(Monoid[Foo])) } end MonoidSuite object MonoidSuite: - import TestDefns.* + import ADTs.* object semiInstances: given Monoid[Foo] = semiauto.monoid @@ -62,10 +69,13 @@ object MonoidSuite: given Monoid[Interleaved[Int]] = semiauto.monoid given Monoid[Box[Mul]] = semiauto.monoid + object derivedInstances: + case class Foo(x: ADTs.Foo) derives Monoid + case class Interleaved[A](x: ADTs.Interleaved[A]) derives Monoid + case class BoxMul(x: Box[Mul]) derives Monoid + final case class Mul(value: Int) object Mul: - given Eq[Mul] = Eq.fromUniversalEquals - given Arbitrary[Mul] = Arbitrary(Arbitrary.arbitrary[Int].map(apply)) given Monoid[Mul] with val empty = Mul(1) def combine(x: Mul, y: Mul) = Mul(x.value * y.value) diff --git a/core/src/test/scala-3/cats/derived/MonoidTests.scala b/core/src/test/scala-3/cats/derived/MonoidTests.scala deleted file mode 100644 index 412a36d1..00000000 --- a/core/src/test/scala-3/cats/derived/MonoidTests.scala +++ /dev/null @@ -1,9 +0,0 @@ -package cats.derived - -import alleycats.* -import cats.* -import cats.derived.* - -class MonoidTests { // - case class Foo(i: Int, b: Option[String]) derives Monoid, Empty, Semigroup -} diff --git a/core/src/test/scala-3/cats/derived/NonEmptyTraverseSuite.scala b/core/src/test/scala-3/cats/derived/NonEmptyTraverseSuite.scala index b8a37cf4..8c8097b2 100644 --- a/core/src/test/scala-3/cats/derived/NonEmptyTraverseSuite.scala +++ b/core/src/test/scala-3/cats/derived/NonEmptyTraverseSuite.scala @@ -18,68 +18,96 @@ package cats.derived import cats.{Eq, NonEmptyTraverse} import cats.data.{NonEmptyList, NonEmptyVector, OneAnd} +import cats.laws.discipline.* import cats.laws.discipline.arbitrary.* -import cats.laws.discipline.{NonEmptyTraverseTests, SerializableTests} -import cats.syntax.all.* -import org.scalacheck.Arbitrary - +import cats.syntax.all.given import scala.compiletime.* class NonEmptyTraverseSuite extends KittensSuite: import NonEmptyTraverseSuite.* - import TestDefns.* + import ADTs.* - inline def nonEmptyTraverseTests[F[_]]: NonEmptyTraverseTests[F] = + inline def tests[F[_]]: NonEmptyTraverseTests[F] = NonEmptyTraverseTests[F](summonInline) - inline def testReducible(inline context: String): Unit = + inline def validate(inline instance: String): Unit = + checkAll( + s"$instance[ICons]", + tests[ICons].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option] + ) checkAll( - s"$context.NonEmptyTraverse[ICons]", - nonEmptyTraverseTests[ICons].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option] + s"$instance[Tree]", + tests[Tree].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option] ) checkAll( - s"$context.NonEmptyTraverse[Tree]", - nonEmptyTraverseTests[Tree].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option] + s"$instance[NelSCons]", + tests[NelSCons].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option] ) checkAll( - s"$context.NonEmptyTraverse[NelSCons]", - nonEmptyTraverseTests[NelSCons].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option] + s"$instance[NelAndOne]", + tests[NelAndOne].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option] ) checkAll( - s"$context.NonEmptyTraverse[NelAndOne]", - nonEmptyTraverseTests[NelAndOne].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option] + s"$instance[VecAndNel]", + tests[VecAndNel].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option] ) checkAll( - s"$context.NonEmptyTraverse[VecAndNel]", - nonEmptyTraverseTests[VecAndNel].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option] + s"$instance[Interleaved]", + tests[Interleaved].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option] ) checkAll( - s"$context.NonEmptyTraverse[Interleaved]", - nonEmptyTraverseTests[Interleaved].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option] + s"$instance[EnumK1]", + tests[EnumK1].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option] ) checkAll( - s"$context.NonEmptyTraverse[EnumK1]", - nonEmptyTraverseTests[EnumK1].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option] + s"$instance[AtLeastOne]", + tests[AtLeastOne].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option] ) checkAll( - s"$context.NonEmptyTraverse is Serializable", + s"$instance is Serializable", SerializableTests.serializable(summonInline[NonEmptyTraverse[Tree]]) ) locally { import auto.nonEmptyTraverse.given - testReducible("auto") + validate("auto.nonEmptyTraverse") } locally { import semiInstances.given - testReducible("semiauto") + validate("semiauto.nonEmptyTraverse") + } + + locally { + import derivedInstances.* + val instance = "derived.nonEmptyTraverse" + checkAll( + s"$instance[ICons]", + tests[ICons].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option] + ) + checkAll( + s"$instance[Tree]", + tests[Tree].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option] + ) + checkAll( + s"$instance[Interleaved]", + tests[Interleaved].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option] + ) + checkAll( + s"$instance[EnumK1]", + tests[EnumK1].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option] + ) + checkAll( + s"$instance[AtLeastOne]", + tests[AtLeastOne].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option] + ) + checkAll(s"$instance is Serializable", SerializableTests.serializable(NonEmptyTraverse[Tree])) } end NonEmptyTraverseSuite object NonEmptyTraverseSuite: - import TestDefns.* + import ADTs.* type NelSCons[A] = NonEmptyList[SCons[A]] type NelAndOne[A] = NonEmptyList[OneAnd[Vector, A]] @@ -93,5 +121,13 @@ object NonEmptyTraverseSuite: given NonEmptyTraverse[VecAndNel] = semiauto.nonEmptyTraverse given NonEmptyTraverse[Interleaved] = semiauto.nonEmptyTraverse given NonEmptyTraverse[EnumK1] = semiauto.nonEmptyTraverse + given NonEmptyTraverse[AtLeastOne] = semiauto.nonEmptyTraverse + + object derivedInstances: + case class ICons[A](x: ADTs.ICons[A]) derives NonEmptyTraverse + case class Tree[A](x: ADTs.Tree[A]) derives NonEmptyTraverse + case class Interleaved[A](x: ADTs.Interleaved[A]) derives NonEmptyTraverse + case class EnumK1[A](x: ADTs.EnumK1[A]) derives NonEmptyTraverse + case class AtLeastOne[A](x: ADTs.AtLeastOne[A]) derives NonEmptyTraverse end NonEmptyTraverseSuite diff --git a/core/src/test/scala-3/cats/derived/OrderSuite.scala b/core/src/test/scala-3/cats/derived/OrderSuite.scala index e0b7c031..657bdeb8 100644 --- a/core/src/test/scala-3/cats/derived/OrderSuite.scala +++ b/core/src/test/scala-3/cats/derived/OrderSuite.scala @@ -17,47 +17,66 @@ package cats.derived import cats.Order -import cats.kernel.laws.discipline.{OrderTests, SerializableTests} -import org.scalacheck.Arbitrary -import scala.compiletime.summonInline +import cats.kernel.laws.discipline.* +import scala.compiletime.* -class OrderSuite extends KittensSuite { - import OrderSuite._ - import TestDefns._ +class OrderSuite extends KittensSuite: + import OrderSuite.* + import ADTs.* - inline def orderTests[A]: OrderTests[A] = + inline def tests[A]: OrderTests[A] = OrderTests[A](summonInline) - inline def testOrder(context: String): Unit = { - checkAll(s"$context.Order[Inner]", orderTests[Inner].order) - checkAll(s"$context.Order[Outer]", orderTests[Outer].order) - checkAll(s"$context.Order[Interleaved[Int]]", orderTests[Interleaved[Int]].order) - checkAll(s"$context.Order[Recursive]", orderTests[Recursive].order) - checkAll(s"$context.Order[GenericAdt[Int]]", orderTests[GenericAdt[Int]].order) - checkAll(s"$context.Order[EnumK0]", orderTests[EnumK0].order) - checkAll(s"$context.Order is Serializable", SerializableTests.serializable(summonInline[Order[Interleaved[Int]]])) - } + inline def validate(instance: String): Unit = + checkAll(s"$instance[Inner]", tests[Inner].order) + checkAll(s"$instance[Outer]", tests[Outer].order) + checkAll(s"$instance[Interleaved[Int]]", tests[Interleaved[Int]].order) + checkAll(s"$instance[Recursive]", tests[Recursive].order) + checkAll(s"$instance[GenericAdt[Int]]", tests[GenericAdt[Int]].order) + checkAll(s"$instance[EnumK0]", tests[EnumK0].order) + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[Order[Interleaved[Int]]])) - { + locally { import auto.order.given - testOrder("auto") + validate("auto.order") } - { + locally { import semiInstances.given - testOrder("semiauto") + validate("semiauto.order") + } + + locally { + import derivedInstances.* + val instance = "derived.order" + checkAll(s"$instance[Inner]", tests[Inner].order) + checkAll(s"$instance[Outer]", tests[Outer].order) + checkAll(s"$instance[Interleaved[Int]]", tests[Interleaved[Int]].order) + checkAll(s"$instance[Recursive]", tests[Recursive].order) + checkAll(s"$instance[GenericAdt[Int]]", tests[GenericAdt[Int]].order) + checkAll(s"$instance[EnumK0]", tests[EnumK0].order) + checkAll(s"$instance is Serializable", SerializableTests.serializable(Order[Interleaved[Int]])) } -} -object OrderSuite { - import TestDefns._ +end OrderSuite + +object OrderSuite: + import ADTs.* - object semiInstances { + object semiInstances: given Order[Inner] = semiauto.order given Order[Outer] = semiauto.order given Order[Interleaved[Int]] = semiauto.order given Order[Recursive] = semiauto.order given Order[GenericAdt[Int]] = semiauto.order given Order[EnumK0] = semiauto.order - } -} + + object derivedInstances: + case class Inner(x: ADTs.Inner) derives Order + case class Outer(x: ADTs.Outer) derives Order + case class Interleaved[A](x: ADTs.Interleaved[A]) derives Order + case class Recursive(x: ADTs.Recursive) derives Order + case class GenericAdt[A](x: ADTs.GenericAdt[A]) derives Order + case class EnumK0(x: ADTs.EnumK0) derives Order + +end OrderSuite diff --git a/core/src/test/scala-3/cats/derived/OrderTests.scala b/core/src/test/scala-3/cats/derived/OrderTests.scala deleted file mode 100644 index 01e7d855..00000000 --- a/core/src/test/scala-3/cats/derived/OrderTests.scala +++ /dev/null @@ -1,9 +0,0 @@ -package cats.derived - -import alleycats.* -import cats.* -import cats.derived.* - -class OrderTests { // - case class Foo(i: Int, b: Option[String]) derives Order -} diff --git a/core/src/test/scala-3/cats/derived/PartialOrderSuite.scala b/core/src/test/scala-3/cats/derived/PartialOrderSuite.scala index af35587b..335367f6 100644 --- a/core/src/test/scala-3/cats/derived/PartialOrderSuite.scala +++ b/core/src/test/scala-3/cats/derived/PartialOrderSuite.scala @@ -14,35 +14,30 @@ * limitations under the License. */ -package cats -package derived +package cats.derived -import cats.kernel.laws.discipline.{PartialOrderTests, SerializableTests} -import org.scalacheck.{Arbitrary, Cogen} +import cats.PartialOrder +import cats.kernel.laws.discipline.* import scala.compiletime.* class PartialOrderSuite extends KittensSuite: import PartialOrderSuite.* - import TestDefns.* + import ADTs.* - inline def partialOrderTests[A]: PartialOrderTests[A] = + inline def tests[A]: PartialOrderTests[A] = PartialOrderTests[A](summonInline) - inline def testPartialOrder(context: String): Unit = - checkAll(s"$context.PartialOrder[IList[Int]]", partialOrderTests[IList[Int]].partialOrder) - checkAll(s"$context.PartialOrder[Inner]", partialOrderTests[Inner].partialOrder) - checkAll(s"$context.PartialOrder[Outer]", partialOrderTests[Outer].partialOrder) - checkAll(s"$context.PartialOrder[Interleaved[Int]]", partialOrderTests[Interleaved[Int]].partialOrder) - checkAll(s"$context.PartialOrder[Tree[Int]]", partialOrderTests[Tree[Int]].partialOrder) - checkAll(s"$context.PartialOrder[Recursive]", partialOrderTests[Recursive].partialOrder) - checkAll(s"$context.PartialOrder[Box[KeyValue]]", partialOrderTests[Box[KeyValue]].partialOrder) - checkAll(s"$context.PartialOrder[EnumK0]", partialOrderTests[EnumK0].partialOrder) - checkAll( - s"$context.PartialOrder is Serialiable", - SerializableTests.serializable(summonInline[PartialOrder[Tree[Int]]]) - ) - - test(s"$context.PartialOrder respects existing instances") { + inline def validate(instance: String): Unit = + checkAll(s"$instance[IList[Int]]", tests[IList[Int]].partialOrder) + checkAll(s"$instance[Inner]", tests[Inner].partialOrder) + checkAll(s"$instance[Outer]", tests[Outer].partialOrder) + checkAll(s"$instance[Interleaved[Int]]", tests[Interleaved[Int]].partialOrder) + checkAll(s"$instance[Tree[Int]]", tests[Tree[Int]].partialOrder) + checkAll(s"$instance[Recursive]", tests[Recursive].partialOrder) + checkAll(s"$instance[Box[KeyValue]]", tests[Box[KeyValue]].partialOrder) + checkAll(s"$instance[EnumK0]", tests[EnumK0].partialOrder) + checkAll(s"$instance is Serialiable", SerializableTests.serializable(summonInline[PartialOrder[Tree[Int]]])) + test(s"$instance respects existing instances") { val boxKeyValue = summonInline[PartialOrder[Box[KeyValue]]] val x = Box(KeyValue("red", 1)) val y = Box(KeyValue("red", 2)) @@ -53,18 +48,32 @@ class PartialOrderSuite extends KittensSuite: locally { import auto.partialOrder.given - testPartialOrder("auto") + validate("auto.partialOrder") } locally { import semiInstances.given - testPartialOrder("semiauto") + validate("semiauto.partialOrder") + } + + locally { + import derivedInstances.* + val instance = "derived.partialOrder" + checkAll(s"$instance[IList[Int]]", tests[IList[Int]].partialOrder) + checkAll(s"$instance[Inner]", tests[Inner].partialOrder) + checkAll(s"$instance[Outer]", tests[Outer].partialOrder) + checkAll(s"$instance[Interleaved[Int]]", tests[Interleaved[Int]].partialOrder) + checkAll(s"$instance[Tree[Int]]", tests[Tree[Int]].partialOrder) + checkAll(s"$instance[Recursive]", tests[Recursive].partialOrder) + checkAll(s"$instance[BoxKV]", tests[BoxKV].partialOrder) + checkAll(s"$instance[EnumK0]", tests[EnumK0].partialOrder) + checkAll(s"$instance is Serialiable", SerializableTests.serializable(PartialOrder[Tree[Int]])) } end PartialOrderSuite object PartialOrderSuite: - import TestDefns.* + import ADTs.* object semiInstances: given PartialOrder[IList[Int]] = semiauto.partialOrder @@ -76,12 +85,18 @@ object PartialOrderSuite: given PartialOrder[Box[KeyValue]] = semiauto.partialOrder given PartialOrder[EnumK0] = semiauto.partialOrder - final case class KeyValue(key: String, value: Int) - object KeyValue extends ((String, Int) => KeyValue): - given Arbitrary[KeyValue] = Arbitrary(Arbitrary.arbitrary[(String, Int)].map(tupled)) - - given Cogen[KeyValue] = Cogen[(String, Int)].contramap(kv => kv.key -> kv.value) + object derivedInstances: + case class IList[A](x: ADTs.IList[A]) derives PartialOrder + case class Interleaved[A](x: ADTs.Interleaved[A]) derives PartialOrder + case class Tree[A](x: ADTs.Tree[A]) derives PartialOrder + case class Inner(x: ADTs.Inner) derives PartialOrder + case class Outer(x: ADTs.Outer) derives PartialOrder + case class Recursive(x: ADTs.Recursive) derives PartialOrder + case class EnumK0(x: ADTs.EnumK0) derives PartialOrder + case class BoxKV(x: Box[KeyValue]) derives PartialOrder + final case class KeyValue(key: String, value: Int) + object KeyValue: given PartialOrder[KeyValue] = PartialOrder.from((x, y) => if (x.key == y.key) x.value.toDouble - y.value.toDouble else Double.NaN) diff --git a/core/src/test/scala-3/cats/derived/PureSuite.scala b/core/src/test/scala-3/cats/derived/PureSuite.scala index 8c2c9145..ea45a8e0 100644 --- a/core/src/test/scala-3/cats/derived/PureSuite.scala +++ b/core/src/test/scala-3/cats/derived/PureSuite.scala @@ -19,44 +19,55 @@ package cats.derived import alleycats.Pure import cats.data.NonEmptyList import cats.laws.discipline.SerializableTests - -import scala.compiletime.summonInline +import scala.compiletime.* class PureSuite extends KittensSuite: import PureSuite.* - import TestDefns.* + import ADTs.* extension [A](a: A) inline def pure[F[_]] = summonInline[Pure[F]].pure(a) - inline def testPure(inline context: String): Unit = - test(s"$context.Pure[LOption]")(assert(42.pure[LOption] == Some(42) :: Nil)) - test(s"$context.Pure[PList]")(assert("Scala".pure[PList] == ("Scala" :: Nil, "Scala" :: Nil))) - test(s"$context.Pure[CaseClassWOption]")(assert(3.14.pure[CaseClassWOption] == CaseClassWOption(Some(3.14)))) - test(s"$context.Pure[NelOption]")(assert(42.pure[NelOption] == NonEmptyList.of(Some(42)))) - test(s"$context.Pure[Interleaved]")(assert('x'.pure[Interleaved] == Interleaved(0, 'x', 0, Vector('x'), ""))) - test(s"$context.Pure respects existing instances")(assert(().pure[BoxColor] == Box(Color(255, 255, 255)))) - checkAll(s"$context.Pure is Serializable", SerializableTests.serializable(summonInline[Pure[Interleaved]])) + inline def validate(inline instance: String): Unit = + test(s"$instance[LOption]")(assert(42.pure[LOption] == Some(42) :: Nil)) + test(s"$instance[PList]")(assert("Scala".pure[PList] == ("Scala" :: Nil, "Scala" :: Nil))) + test(s"$instance[CaseClassWOption]")(assert(3.14.pure[CaseClassWOption] == CaseClassWOption(Some(3.14)))) + test(s"$instance[NelOption]")(assert(42.pure[NelOption] == NonEmptyList.of(Some(42)))) + test(s"$instance[Interleaved]")(assert('x'.pure[Interleaved] == Interleaved(0, 'x', 0, Vector('x'), ""))) + test(s"$instance respects existing instances")(assert(().pure[BoxColor] == Box(Color(255, 255, 255)))) + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[Pure[Interleaved]])) locally { import auto.pure.given - testPure("auto") + validate("auto.pure") testNoAuto("Pure", "IList") testNoAuto("Pure", "Snoc") } locally { import semiInstances.given - testPure("semiauto") + validate("semiauto.pure") testNoSemi("Pure", "IList") testNoSemi("Pure", "Snoc") } + locally { + import derivedInstances.* + val instance = "derived.pure" + checkAll(s"$instance is Serializable", SerializableTests.serializable(Pure[Interleaved])) + test(s"$instance[CaseClassWOption]")( + assert(3.14.pure[CaseClassWOption].x == ADTs.CaseClassWOption(Some(3.14))) + ) + test(s"$instance[Interleaved]")( + assert('x'.pure[Interleaved].x == ADTs.Interleaved(0, 'x', 0, Vector('x'), "")) + ) + } + end PureSuite object PureSuite: - import TestDefns.* + import ADTs.* type LOption[A] = List[Option[A]] type PList[A] = (List[A], List[A]) @@ -71,6 +82,10 @@ object PureSuite: given Pure[Interleaved] = semiauto.pure given Pure[BoxColor] = semiauto.pure + object derivedInstances: + case class CaseClassWOption[A](x: ADTs.CaseClassWOption[A]) derives Pure + case class Interleaved[A](x: ADTs.Interleaved[A]) derives Pure + final case class Color[A](r: Int, g: Int, b: Int) object Color: given Pure[Color] with diff --git a/core/src/test/scala-3/cats/derived/ReducibleSuite.scala b/core/src/test/scala-3/cats/derived/ReducibleSuite.scala index b3b6f8cf..c30ab3f0 100644 --- a/core/src/test/scala-3/cats/derived/ReducibleSuite.scala +++ b/core/src/test/scala-3/cats/derived/ReducibleSuite.scala @@ -14,48 +14,59 @@ * limitations under the License. */ -package cats -package derived +package cats.derived +import cats.{Eval, Reducible} import cats.data.{NonEmptyList, OneAnd} +import cats.laws.discipline.* import cats.laws.discipline.arbitrary.* -import cats.laws.discipline.{ReducibleTests, SerializableTests} -import cats.syntax.all.* -import org.scalacheck.Arbitrary +import cats.syntax.all.given import scala.compiletime.* class ReducibleSuite extends KittensSuite: import ReducibleSuite.* - import TestDefns.* + import ADTs.* - inline def reducibleTests[F[_]]: ReducibleTests[F] = + inline def tests[F[_]]: ReducibleTests[F] = ReducibleTests[F](summonInline) - inline def testReducible(context: String): Unit = - checkAll(s"$context.Reducible[ICons]", reducibleTests[ICons].reducible[Option, Int, Long]) - checkAll(s"$context.Reducible[Tree]", reducibleTests[Tree].reducible[Option, Int, Long]) - checkAll(s"$context.Reducible[NelSCons]", reducibleTests[NelSCons].reducible[Option, Int, Long]) - checkAll(s"$context.Reducible[NelAndOne]", reducibleTests[NelAndOne].reducible[Option, Int, Long]) - checkAll(s"$context.Reducible[VecAndNel]", reducibleTests[VecAndNel].reducible[Option, Int, Long]) - checkAll(s"$context.Reducible[Interleaved]", reducibleTests[Interleaved].reducible[Option, Int, Long]) - checkAll(s"$context.Reducible[BoxZipper]", reducibleTests[BoxZipper].reducible[Option, Int, Long]) - checkAll(s"$context.Reducible[EnumK1]", reducibleTests[EnumK1].reducible[Option, Int, Long]) - checkAll(s"$context.Reducible is Serializable", SerializableTests.serializable(summonInline[Reducible[Tree]])) + inline def validate(instance: String): Unit = + checkAll(s"$instance[ICons]", tests[ICons].reducible[Option, Int, Long]) + checkAll(s"$instance[Tree]", tests[Tree].reducible[Option, Int, Long]) + checkAll(s"$instance[NelSCons]", tests[NelSCons].reducible[Option, Int, Long]) + checkAll(s"$instance[NelAndOne]", tests[NelAndOne].reducible[Option, Int, Long]) + checkAll(s"$instance[VecAndNel]", tests[VecAndNel].reducible[Option, Int, Long]) + checkAll(s"$instance[Interleaved]", tests[Interleaved].reducible[Option, Int, Long]) + checkAll(s"$instance[BoxZipper]", tests[BoxZipper].reducible[Option, Int, Long]) + checkAll(s"$instance[EnumK1]", tests[EnumK1].reducible[Option, Int, Long]) + checkAll(s"$instance[AtLeastOne]", tests[AtLeastOne].reducible[Option, Int, Long]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[Reducible[Tree]])) locally { import auto.reducible.given - testReducible("auto") + validate("auto.reducible") } locally { import semiInstances.given - testReducible("semiauto") + validate("semiauto.reducible") + } + + locally { + import derivedInstances.* + val instance = "derived.reducible" + checkAll(s"$instance[ICons]", tests[ICons].reducible[Option, Int, Long]) + checkAll(s"$instance[Tree]", tests[Tree].reducible[Option, Int, Long]) + checkAll(s"$instance[Interleaved]", tests[Interleaved].reducible[Option, Int, Long]) + checkAll(s"$instance[EnumK1]", tests[EnumK1].reducible[Option, Int, Long]) + checkAll(s"$instance[AtLeastOne]", tests[AtLeastOne].reducible[Option, Int, Long]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(Reducible[Tree])) } end ReducibleSuite object ReducibleSuite: - import TestDefns.* + import ADTs.* type NelSCons[A] = NonEmptyList[SCons[A]] type NelAndOne[A] = NonEmptyList[OneAnd[Vector, A]] @@ -71,19 +82,17 @@ object ReducibleSuite: given Reducible[Interleaved] = semiauto.reducible given Reducible[BoxZipper] = semiauto.reducible given Reducible[EnumK1] = semiauto.reducible + given Reducible[AtLeastOne] = semiauto.reducible + + object derivedInstances: + case class ICons[A](x: ADTs.ICons[A]) derives Reducible + case class Tree[A](x: ADTs.Tree[A]) derives Reducible + case class Interleaved[A](x: ADTs.Interleaved[A]) derives Reducible + case class EnumK1[A](x: ADTs.EnumK1[A]) derives Reducible + case class AtLeastOne[A](x: ADTs.AtLeastOne[A]) derives Reducible final case class Zipper[+A](left: List[A], focus: A, right: List[A]) object Zipper: - given [A: Eq]: Eq[Zipper[A]] = - (x, y) => x.focus === y.focus && x.left === y.left && x.right === y.right - - given [A: Arbitrary]: Arbitrary[Zipper[A]] = - Arbitrary(for - left <- Arbitrary.arbitrary[List[A]] - focus <- Arbitrary.arbitrary[A] - right <- Arbitrary.arbitrary[List[A]] - yield Zipper(left, focus, right)) - given Reducible[Zipper] with def reduceLeftTo[A, B](fa: Zipper[A])(f: A => B)(g: (B, A) => B) = NonEmptyList(fa.focus, fa.right).reduceLeftTo(f)(g) diff --git a/core/src/test/scala-3/cats/derived/ReducibleTests.scala b/core/src/test/scala-3/cats/derived/ReducibleTests.scala deleted file mode 100644 index b0595b3e..00000000 --- a/core/src/test/scala-3/cats/derived/ReducibleTests.scala +++ /dev/null @@ -1,38 +0,0 @@ -package cats.derived - -import cats.Reducible -import cats.data.NonEmptyList -import cats.instances.all.* -import cats.derived.* -import cats.derived.semiauto.given - -object ReducibleTests: - case class Box[A](value: A) derives Reducible - - sealed trait OneOrMany[+A] derives Reducible - case class One[+A](value: A) extends OneOrMany[A] - case class Many[+A](values: NonEmptyList[A]) extends OneOrMany[A] - - sealed trait CList[A] derives Reducible - case class COne[A](value: A) extends CList[A] - case class CCons[A](head: A, tail: CList[A]) extends CList[A] - - case class NonEmptyTree[A](size: Int, value: A, tree: Tree[A]) - enum Tree[+A]: - case Leaf - case Node(left: Tree[A], value: A, right: Tree[A]) - - case class Foo[A](value: A, xs: List[A]) - enum MyList[+A]: - case Non - case Con(v: A, r: MyList[A]) - - import cats._ - - DerivedFunctor[MyList] - DerivedReducible[NonEmptyTree] - -@main def run() = - println( - DerivedFunctor[ReducibleTests.MyList].map(ReducibleTests.MyList.Con(42, ReducibleTests.MyList.Non))(_.toString) - ) diff --git a/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala b/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala index 2dd7db73..edf3c13a 100644 --- a/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala +++ b/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala @@ -1,44 +1,48 @@ package cats.derived -import cats.* +import cats.SemigroupK import cats.laws.discipline.{SemigroupKTests, SerializableTests} -import org.scalacheck.Arbitrary import scala.compiletime.* class SemigroupKSuite extends KittensSuite: import SemigroupKSuite.* - import TestDefns.* + import ADTs.* - inline def semigroupKTests[F[_]]: SemigroupKTests[F] = + inline def tests[F[_]]: SemigroupKTests[F] = SemigroupKTests[F](summonInline) - inline def testSemigroupK(context: String): Unit = - checkAll(s"$context.SemigroupK[ComplexProduct]", semigroupKTests[ComplexProduct].semigroupK[Char]) - checkAll(s"$context.SemigroupK[CaseClassWOption]", semigroupKTests[CaseClassWOption].semigroupK[Char]) - checkAll(s"$context.SemigroupK[BoxMul]", semigroupKTests[BoxMul].semigroupK[Char]) - checkAll( - s"$context.SemigroupK is Serializable", - SerializableTests.serializable(summonInline[SemigroupK[ComplexProduct]]) - ) - - test(s"$context.SemigroupK respects existing instances") { + inline def validate(instance: String): Unit = + checkAll(s"$instance[ComplexProduct]", tests[ComplexProduct].semigroupK[Char]) + checkAll(s"$instance[CaseClassWOption]", tests[CaseClassWOption].semigroupK[Char]) + checkAll(s"$instance[BoxMul]", tests[BoxMul].semigroupK[Char]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[SemigroupK[ComplexProduct]])) + test(s"$instance respects existing instances") { assert(summonInline[SemigroupK[BoxMul]].combineK(Box(Mul[Char](5)), Box(Mul[Char](5))) == Box(Mul[Char](25))) } locally { import auto.semigroupK.given - testSemigroupK("auto") + validate("auto.semigroupK") } locally { import semiInstances.given - testSemigroupK("semiauto") + validate("semiauto.semigroupK") + } + + locally { + import derivedInstances.* + val instance = "derived.semigroupK" + checkAll(s"$instance[ComplexProduct]", tests[ComplexProduct].semigroupK[Char]) + checkAll(s"$instance[CaseClassWOption]", tests[CaseClassWOption].semigroupK[Char]) + checkAll(s"$instance[Simple]", tests[Simple].semigroupK[Char]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(SemigroupK[ComplexProduct])) } end SemigroupKSuite object SemigroupKSuite: - import TestDefns.* + import ADTs.* type BoxMul[A] = Box[Mul[A]] @@ -47,17 +51,15 @@ object SemigroupKSuite: given SemigroupK[CaseClassWOption] = semiauto.semigroupK given SemigroupK[BoxMul] = semiauto.semigroupK + object derivedInstances: + case class ComplexProduct[A](x: ADTs.ComplexProduct[A]) derives SemigroupK + case class CaseClassWOption[A](x: ADTs.CaseClassWOption[A]) derives SemigroupK + case class Simple[A](value1: List[A], value2: Set[A]) derives SemigroupK + case class Recursive[A](first: List[A], rest: Recursive[A]) derives SemigroupK + final case class Mul[T](value: Int) object Mul: - given [T]: Eq[Mul[T]] = Eq.by(_.value) - - given [T]: Arbitrary[Mul[T]] = - Arbitrary(Arbitrary.arbitrary[Int].map(apply)) - given SemigroupK[Mul] with def combineK[A](x: Mul[A], y: Mul[A]) = Mul(x.value * y.value) - case class Simple[A](value1: List[A], value2: Set[A]) derives SemigroupK - case class Recursive[A](first: List[A], rest: Recursive[A]) derives SemigroupK - end SemigroupKSuite diff --git a/core/src/test/scala-3/cats/derived/SemigroupSuite.scala b/core/src/test/scala-3/cats/derived/SemigroupSuite.scala index 5190dba8..c11b75b1 100644 --- a/core/src/test/scala-3/cats/derived/SemigroupSuite.scala +++ b/core/src/test/scala-3/cats/derived/SemigroupSuite.scala @@ -17,43 +17,52 @@ package cats.derived import cats.{Eq, Semigroup} -import cats.kernel.laws.discipline.{SemigroupTests, SerializableTests} +import cats.kernel.laws.discipline.* import org.scalacheck.Arbitrary import scala.compiletime.* class SemigroupSuite extends KittensSuite: import SemigroupSuite.* - import TestDefns.* + import ADTs.* - inline def semigroupTests[A]: SemigroupTests[A] = + inline def tests[A]: SemigroupTests[A] = SemigroupTests[A](summonInline) - inline def testSemigroup(inline context: String): Unit = - checkAll(s"$context.Semigroup[Foo]", semigroupTests[Foo].semigroup) - checkAll(s"$context.Semigroup[Interleaved[Int]]", semigroupTests[Interleaved[Int]].semigroup) - checkAll(s"$context.Semigroup[Box[Mul]]", semigroupTests[Box[Mul]].semigroup) - checkAll(s"$context.Semigroup[Recursive]", semigroupTests[Recursive].semigroup) - checkAll(s"$context.Semigroup is Serializable", SerializableTests.serializable(summonInline[Semigroup[Foo]])) - test(s"$context.Semigroup respects existing instances") { + inline def validate(inline instances: String): Unit = + checkAll(s"$instances[Foo]", tests[Foo].semigroup) + checkAll(s"$instances[Interleaved[Int]]", tests[Interleaved[Int]].semigroup) + checkAll(s"$instances[Box[Mul]]", tests[Box[Mul]].semigroup) + checkAll(s"$instances[Recursive]", tests[Recursive].semigroup) + checkAll(s"$instances is Serializable", SerializableTests.serializable(summonInline[Semigroup[Foo]])) + test(s"$instances respects existing instances") { val box = summonInline[Semigroup[Box[Mul]]] assert(box.combine(Box(Mul(5)), Box(Mul(5))).content.value == 25) } locally { import auto.semigroup.given - testSemigroup("auto") + validate("auto.semigroup") } locally { import semiInstances.given - testSemigroup("semiauto") + validate("semiauto.semigroup") + } + + locally { + import derivedInstances.* + val instances = "derived.semigroup" + checkAll(s"$instances[Foo]", tests[Foo].semigroup) + checkAll(s"$instances[Interleaved[Int]]", tests[Interleaved[Int]].semigroup) + checkAll(s"$instances[BoxMul]", tests[BoxMul].semigroup) + checkAll(s"$instances is Serializable", SerializableTests.serializable(Semigroup[Foo])) } end SemigroupSuite object SemigroupSuite: - import TestDefns.* + import ADTs.* object semiInstances: given Semigroup[Foo] = semiauto.semigroup @@ -61,10 +70,13 @@ object SemigroupSuite: given Semigroup[Interleaved[Int]] = semiauto.semigroup given Semigroup[Box[Mul]] = semiauto.semigroup + object derivedInstances: + case class Foo(x: ADTs.Foo) derives Semigroup + case class Interleaved[A](x: ADTs.Interleaved[A]) derives Semigroup + case class BoxMul(x: Box[Mul]) derives Semigroup + final case class Mul(value: Int) object Mul: - given Eq[Mul] = Eq.fromUniversalEquals - given Arbitrary[Mul] = Arbitrary(Arbitrary.arbitrary[Int].map(apply)) given Semigroup[Mul] = (x, y) => Mul(x.value * y.value) end SemigroupSuite diff --git a/core/src/test/scala-3/cats/derived/ShowPrettySuite.scala b/core/src/test/scala-3/cats/derived/ShowPrettySuite.scala index 8d51de3a..69c319e7 100644 --- a/core/src/test/scala-3/cats/derived/ShowPrettySuite.scala +++ b/core/src/test/scala-3/cats/derived/ShowPrettySuite.scala @@ -1,21 +1,21 @@ -package cats -package derived +package cats.derived +import cats.Show import cats.laws.discipline.SerializableTests import scala.compiletime.* class ShowPrettySuite extends KittensSuite: - import ShowPrettySuite.* import ShowPrettySuite.given - import TestDefns.* + import ShowPrettySuite.* + import ADTs.* - inline def showPretty[A](value: A): String = + inline def show[A](value: A): String = summonInline[ShowPretty[A]].show(value) - inline def testShowPretty(context: String): Unit = { - checkAll(s"$context.ShowPretty is Serializable", SerializableTests.serializable(summonInline[ShowPretty[IntTree]])) + inline def validate(instance: String): Unit = + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[ShowPretty[IntTree]])) - test(s"$context.ShowPretty[Foo]") { + test(s"$instance[Foo]") { val value = Foo(42, Option("Hello")) val pretty = """ |Foo( @@ -24,10 +24,10 @@ class ShowPrettySuite extends KittensSuite: |) """.stripMargin.trim - assertEquals(showPretty(value), pretty) + assertEquals(show(value), pretty) } - test(s"$context.ShowPretty[Outer]") { + test(s"$instance[Outer]") { val value = Outer(Inner(3)) val pretty = """ |Outer( @@ -37,10 +37,10 @@ class ShowPrettySuite extends KittensSuite: |) """.stripMargin.trim - assertEquals(showPretty(value), pretty) + assertEquals(show(value), pretty) } - test(s"$context.ShowPretty[IntTree]") { + test(s"$instance[IntTree]") { val value: IntTree = IntNode(IntLeaf(1), IntNode(IntNode(IntLeaf(2), IntLeaf(3)), IntLeaf(4))) val pretty = """ |IntNode( @@ -63,10 +63,10 @@ class ShowPrettySuite extends KittensSuite: |) """.stripMargin.trim - assertEquals(showPretty(value), pretty) + assertEquals(show(value), pretty) } - test(s"$context.ShowPretty[GenericAdt[Int]]") { + test(s"$instance[GenericAdt[Int]]") { val value: GenericAdt[Int] = GenericAdtCase(Some(1)) val pretty = """ |GenericAdtCase( @@ -74,10 +74,10 @@ class ShowPrettySuite extends KittensSuite: |) """.stripMargin.trim - assertEquals(showPretty(value), pretty) + assertEquals(show(value), pretty) } - test(s"$context.ShowPretty[People]") { + test(s"$instance[People]") { val value = People("Kai", ContactInfo("303-123-4567", Address("123 1st St", "New York", "NY"))) val pretty = """ |People( @@ -89,10 +89,10 @@ class ShowPrettySuite extends KittensSuite: |) """.stripMargin.trim - assertEquals(showPretty(value), pretty) + assertEquals(show(value), pretty) } - test(s"$context.ShowPretty[ListField]") { + test(s"$instance[ListField]") { val value = ListField("a", List(ListFieldChild(1))) val pretty = """ |ListField( @@ -103,10 +103,10 @@ class ShowPrettySuite extends KittensSuite: |) """.stripMargin.trim - assertEquals(showPretty(value), pretty) + assertEquals(show(value), pretty) } - test(s"$context.ShowPretty[Interleaved[Int]]") { + test(s"$instance[Interleaved[Int]]") { val value = Interleaved(1, 2, 3, Vector(4, 5, 6), "789") val pretty = """ |Interleaved( @@ -118,10 +118,10 @@ class ShowPrettySuite extends KittensSuite: |) """.stripMargin.trim - assertEquals(showPretty(value), pretty) + assertEquals(show(value), pretty) } - test(s"$context.ShowPretty[Tree[Int]]") { + test(s"$instance[Tree[Int]]") { val value: Tree[Int] = Node(Leaf(1), Node(Node(Leaf(2), Leaf(3)), Leaf(4))) val pretty = """ |Node( @@ -144,10 +144,10 @@ class ShowPrettySuite extends KittensSuite: |) """.stripMargin.trim - assertEquals(showPretty(value), pretty) + assertEquals(show(value), pretty) } - test(s"$context.ShowPretty respects existing instances") { + test(s"$instance respects existing instances") { val value = Box(Bogus(42)) val pretty = """ |Box( @@ -155,26 +155,28 @@ class ShowPrettySuite extends KittensSuite: |) """.stripMargin.trim - assertEquals(showPretty(value), pretty) + assertEquals(show(value), pretty) } - } + + end validate locally { import auto.showPretty.given - testShowPretty("auto") + validate("auto.showPretty") } locally { import semiInstances.given - testShowPretty("semiauto") + validate("semiauto.showPretty") } +end ShowPrettySuite + object ShowPrettySuite: - import TestDefns.* + import ADTs.* - given Show[Address] = Show.show { a => - List(a.street, a.city, a.state).mkString(" ") - } + given Show[Address] = + Show.show(a => List(a.street, a.city, a.state).mkString(" ")) final case class Bogus(value: Int) object Bogus: @@ -191,3 +193,17 @@ object ShowPrettySuite: given ShowPretty[Interleaved[Int]] = semiauto.showPretty given ShowPretty[Tree[Int]] = semiauto.showPretty given ShowPretty[Box[Bogus]] = semiauto.showPretty + + object derivedInstances: + case class Foo(x: ADTs.Foo) derives ShowPretty + case class Outer(x: ADTs.Outer) derives ShowPretty + case class IntTree(x: ADTs.IntTree) derives ShowPretty + case class People(x: ADTs.People) derives ShowPretty + case class ListFieldChild(x: ADTs.ListFieldChild) derives ShowPretty + case class ListField(x: ADTs.ListField) derives ShowPretty + case class GenericAdt[A](x: ADTs.GenericAdt[A]) derives ShowPretty + case class Interleaved[A](x: ADTs.Interleaved[A]) derives ShowPretty + case class Tree[A](x: ADTs.Tree[A]) derives ShowPretty + case class BoxBogus(x: Box[Bogus]) derives ShowPretty + +end ShowPrettySuite diff --git a/core/src/test/scala-3/cats/derived/ShowSuite.scala b/core/src/test/scala-3/cats/derived/ShowSuite.scala index 10ba0d53..b695fba3 100644 --- a/core/src/test/scala-3/cats/derived/ShowSuite.scala +++ b/core/src/test/scala-3/cats/derived/ShowSuite.scala @@ -1,104 +1,103 @@ -package cats -package derived +package cats.derived +import cats.Show import cats.laws.discipline.SerializableTests import scala.compiletime.* class ShowSuite extends KittensSuite: import ShowSuite.given import ShowSuite.* - import TestDefns.* + import ADTs.* inline def show[A](value: A): String = summonInline[Show[A]].show(value) - inline def testShow(context: String): Unit = - checkAll(s"$context.Show is Serializable", SerializableTests.serializable(summonInline[Show[Foo]])) + inline def validate(instance: String): Unit = + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[Show[Foo]])) - test(s"$context.Show[Foo]") { + test(s"$instance[Foo]") { val value = Foo(42, Option("Hello")) val shown = "Foo(i = 42, b = Some(Hello))" assertEquals(show(value), shown) } - test(s"$context.Show[Outer]") { + test(s"$instance[Outer]") { val value = Outer(Inner(3)) val shown = "Outer(in = Inner(i = 3))" assertEquals(show(value), shown) } - test(s"$context.Show[IntTree]") { + test(s"$instance[IntTree]") { val value: IntTree = IntNode(IntLeaf(1), IntNode(IntNode(IntLeaf(2), IntLeaf(3)), IntLeaf(4))) val shown = "IntNode(l = IntLeaf(t = 1), r = IntNode(l = IntNode(l = IntLeaf(t = 2), r = IntLeaf(t = 3)), r = IntLeaf(t = 4)))" assertEquals(show(value), shown) } - test(s"$context.Show[GenericAdt[Int]]") { + test(s"$instance[GenericAdt[Int]]") { val value: GenericAdt[Int] = GenericAdtCase(Some(1)) val shown = "GenericAdtCase(value = Some(1))" assertEquals(show(value), shown) } - test(s"$context.Show[People]") { + test(s"$instance[People]") { val value = People("Kai", ContactInfo("303-123-4567", Address("123 1st St", "New York", "NY"))) val shown = "People(name = Kai, contactInfo = ContactInfo(phoneNumber = 303-123-4567, address = 123 1st St New York NY))" assertEquals(show(value), shown) } - test(s"$context.Show[ListField]") { + test(s"$instance[ListField]") { val value = ListField("a", List(ListFieldChild(1))) val shown = "ListField(a = a, b = List(ListFieldChild(c = 1)))" assertEquals(show(value), shown) } - test(s"$context.Show[Interleaved[Int]]") { + test(s"$instance[Interleaved[Int]]") { val value = Interleaved(1, 2, 3, Vector(4, 5, 6), "789") val shown = "Interleaved(i = 1, t = 2, l = 3, tt = Vector(4, 5, 6), s = 789)" assertEquals(show(value), shown) } - test(s"$context.Show[Tree[Int]]") { + test(s"$instance[Tree[Int]]") { val value: Tree[Int] = Node(Leaf(1), Node(Node(Leaf(2), Leaf(3)), Leaf(4))) val shown = "Node(left = Leaf(value = 1), right = Node(left = Node(left = Leaf(value = 2), right = Leaf(value = 3)), right = Leaf(value = 4)))" assertEquals(show(value), shown) } - test(s"$context.Show[EnumK0]") { + test(s"$instance[EnumK0]") { val value: EnumK0 = EnumK0.LeafI(3) val shown = "LeafI(value = 3)" assertEquals(show(value), shown) } - test(s"$context.Show respects existing instances") { + test(s"$instance respects existing instances") { val value = Box(Bogus(42)) val shown = "Box(content = Blah)" assertEquals(show(value), shown) } - end testShow + end validate locally { import auto.show.given - testShow("auto") + validate("auto.show") } locally { import semiInstances.given - testShow("semiauto") + validate("semiauto.show") } end ShowSuite object ShowSuite: - import TestDefns.* + import ADTs.* - given Show[Address] = Show.show { a => - List(a.street, a.city, a.state).mkString(" ") - } + given Show[Address] = + Show.show(a => List(a.street, a.city, a.state).mkString(" ")) final case class Bogus(value: Int) object Bogus: @@ -117,4 +116,17 @@ object ShowSuite: given Show[Box[Bogus]] = semiauto.show given Show[EnumK0] = semiauto.show + object derivedInstances: + case class Foo(x: ADTs.Foo) derives Show + case class Outer(x: ADTs.Outer) derives Show + case class IntTree(x: ADTs.IntTree) derives Show + case class People(x: ADTs.People) derives Show + case class ListFieldChild(x: ADTs.ListFieldChild) derives Show + case class ListField(x: ADTs.ListField) derives Show + case class EnumK0(x: ADTs.EnumK0) derives Show + case class GenericAdt[A](x: ADTs.GenericAdt[A]) derives Show + case class Interleaved[A](x: ADTs.Interleaved[A]) derives Show + case class Tree[A](x: ADTs.Tree[A]) derives Show + case class BoxBogus(x: Box[Bogus]) derives Show + end ShowSuite diff --git a/core/src/test/scala-3/cats/derived/ShowTests.scala b/core/src/test/scala-3/cats/derived/ShowTests.scala deleted file mode 100644 index 1328404c..00000000 --- a/core/src/test/scala-3/cats/derived/ShowTests.scala +++ /dev/null @@ -1,9 +0,0 @@ -package cats.derived - -import alleycats.* -import cats.* -import cats.derived.* - -class ShowTests { // - case class Foo(i: Int, b: Option[String]) derives Show -} diff --git a/core/src/test/scala-3/cats/derived/TraverseSuite.scala b/core/src/test/scala-3/cats/derived/TraverseSuite.scala index 27e66c96..7b4c9f16 100644 --- a/core/src/test/scala-3/cats/derived/TraverseSuite.scala +++ b/core/src/test/scala-3/cats/derived/TraverseSuite.scala @@ -1,55 +1,58 @@ package cats.derived import cats.{Eq, Traverse} -import cats.laws.discipline.{SerializableTests, TraverseTests} -import org.scalacheck.Arbitrary - +import cats.laws.discipline.* import scala.compiletime.* class TraverseSuite extends KittensSuite: - import TestDefns.* + import ADTs.* import TraverseSuite.* - inline def traverseTests[F[_]]: TraverseTests[F] = + inline def tests[F[_]]: TraverseTests[F] = TraverseTests[F](summonInline) - inline def testTraverse(inline context: String): Unit = - checkAll(s"$context.Traverse[IList]", traverseTests[IList].traverse[Int, Double, String, Long, Option, Option]) - checkAll(s"$context.Traverse[Tree]", traverseTests[Tree].traverse[Int, Double, String, Long, Option, Option]) - checkAll( - s"$context.Traverse[GenericAdt]", - traverseTests[GenericAdt].traverse[Int, Double, String, Long, Option, Option] - ) - checkAll(s"$context.Traverse[OptList]", traverseTests[OptList].traverse[Int, Double, String, Long, Option, Option]) - checkAll( - s"$context.Traverse[ListSnoc]", - traverseTests[ListSnoc].traverse[Int, Double, String, Long, Option, Option] - ) - checkAll(s"$context.Traverse[AndChar]", traverseTests[AndChar].traverse[Int, Double, String, Long, Option, Option]) - checkAll( - s"$context.Traverse[Interleaved]", - traverseTests[Interleaved].traverse[Int, Double, String, Long, Option, Option] - ) - checkAll( - s"$context.Traverse[EnumK1]", - traverseTests[EnumK1].traverse[Int, Double, String, Long, Option, Option] - ) - checkAll(s"$context.Traverse is Serializable", SerializableTests.serializable(summonInline[Traverse[Tree]])) + inline def validate(inline instance: String): Unit = + checkAll(s"$instance[IList]", tests[IList].traverse[Int, Double, String, Long, Option, Option]) + checkAll(s"$instance[Tree]", tests[Tree].traverse[Int, Double, String, Long, Option, Option]) + checkAll(s"$instance[GenericAdt]", tests[GenericAdt].traverse[Int, Double, String, Long, Option, Option]) + checkAll(s"$instance[OptList]", tests[OptList].traverse[Int, Double, String, Long, Option, Option]) + checkAll(s"$instance[ListSnoc]", tests[ListSnoc].traverse[Int, Double, String, Long, Option, Option]) + checkAll(s"$instance[AndChar]", tests[AndChar].traverse[Int, Double, String, Long, Option, Option]) + checkAll(s"$instance[Interleaved]", tests[Interleaved].traverse[Int, Double, String, Long, Option, Option]) + checkAll(s"$instance[EnumK1]", tests[EnumK1].traverse[Int, Double, String, Long, Option, Option]) + checkAll(s"$instance[Many]", tests[Many].traverse[Int, Double, String, Long, Option, Option]) + checkAll(s"$instance[AtMostOne]", tests[AtMostOne].traverse[Int, Double, String, Long, Option, Option]) + checkAll(s"$instance[AtLeastOne]", tests[AtLeastOne].traverse[Int, Double, String, Long, Option, Option]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[Traverse[Tree]])) locally { import auto.traverse.given - testTraverse("auto") + validate("auto.traverse") } locally { import semiInstances.given - testTraverse("semiauto") + validate("semiauto.traverse") + } + + locally { + import derivedInstances.* + val instance = "derived.traverse" + checkAll(s"$instance[IList]", tests[IList].traverse[Int, Double, String, Long, Option, Option]) + checkAll(s"$instance[Tree]", tests[Tree].traverse[Int, Double, String, Long, Option, Option]) + checkAll(s"$instance[GenericAdt]", tests[GenericAdt].traverse[Int, Double, String, Long, Option, Option]) + checkAll(s"$instance[Interleaved]", tests[Interleaved].traverse[Int, Double, String, Long, Option, Option]) + checkAll(s"$instance[EnumK1]", tests[EnumK1].traverse[Int, Double, String, Long, Option, Option]) + checkAll(s"$instance[Many]", tests[Many].traverse[Int, Double, String, Long, Option, Option]) + checkAll(s"$instance[AtMostOne]", tests[AtMostOne].traverse[Int, Double, String, Long, Option, Option]) + checkAll(s"$instance[AtLeastOne]", tests[AtLeastOne].traverse[Int, Double, String, Long, Option, Option]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(Traverse[Tree])) } end TraverseSuite object TraverseSuite: - import TestDefns.* + import ADTs.* type OptList[A] = Option[List[A]] type ListSnoc[A] = List[Snoc[A]] @@ -64,5 +67,18 @@ object TraverseSuite: given Traverse[AndChar] = semiauto.traverse given Traverse[Interleaved] = semiauto.traverse given Traverse[EnumK1] = semiauto.traverse + given Traverse[Many] = semiauto.traverse + given Traverse[AtLeastOne] = semiauto.traverse + given Traverse[AtMostOne] = semiauto.traverse + + object derivedInstances: + case class IList[A](x: ADTs.IList[A]) derives Traverse + case class Tree[A](x: ADTs.Tree[A]) derives Traverse + case class GenericAdt[A](x: ADTs.GenericAdt[A]) derives Traverse + case class Interleaved[A](x: ADTs.Interleaved[A]) derives Traverse + case class EnumK1[A](x: ADTs.EnumK1[A]) derives Traverse + case class Many[A](x: ADTs.Many[A]) derives Traverse + case class AtMostOne[A](x: ADTs.AtMostOne[A]) derives Traverse + case class AtLeastOne[A](x: ADTs.AtLeastOne[A]) derives Traverse end TraverseSuite diff --git a/core/src/test/scala-3/cats/derived/TraverseTests.scala b/core/src/test/scala-3/cats/derived/TraverseTests.scala deleted file mode 100644 index d6c410ff..00000000 --- a/core/src/test/scala-3/cats/derived/TraverseTests.scala +++ /dev/null @@ -1,17 +0,0 @@ -package cats.derived - -import cats.Traverse -import cats.derived.* - -class TraverseTests { - - case class Box[A](value: A) derives Traverse - - sealed trait Maybe[+A] derives Traverse - case object Nufin extends Maybe[Nothing] - case class Just[A](value: A) extends Maybe[A] - - sealed trait CList[A] derives Traverse - case object CNil extends CList[Nothing] - case class CCons[A](head: A, tail: CList[A]) extends CList[A] -} diff --git a/core/src/test/scala-3/cats/derived/adtdefns.scala b/core/src/test/scala-3/cats/derived/adtdefns.scala deleted file mode 100644 index 5de94a4d..00000000 --- a/core/src/test/scala-3/cats/derived/adtdefns.scala +++ /dev/null @@ -1,561 +0,0 @@ -/* - * Copyright (c) 2015 Miles Sabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package cats.derived - -import cats.{Eq, Eval} -import cats.syntax.all.given -import org.scalacheck.rng.Seed -import org.scalacheck.{Arbitrary, Cogen, Gen} - -import scala.annotation.tailrec - -object TestDefns: - - enum EnumK0: - case LeafS(value: String) - case LeafI(value: Int) - - object EnumK0: - given Arbitrary[EnumK0] = Arbitrary( - Gen.oneOf( - Arbitrary.arbitrary[String].map(LeafS.apply), - Arbitrary.arbitrary[Int].map(LeafI.apply) - ) - ) - - given Cogen[EnumK0] = Cogen[Either[String, Int]].contramap { - case LeafS(s) => Left(s) - case LeafI(i) => Right(i) - } - - enum EnumK1[A]: - case Leaf(value: A) - case Rec(l: EnumK1[A], r: EnumK1[A]) - - object EnumK1: - given [A](using Arbitrary[A]): Arbitrary[EnumK1[A]] = Arbitrary( - Gen.recursive(rec => - Gen.sized(size => - val leaf = Arbitrary.arbitrary[A].map(Leaf.apply) - if size == 0 then leaf - else - Gen.resize( - size / 2, - Gen.oneOf( - leaf, - for - l <- rec - r <- rec - yield Rec(l, r) - ) - ) - ) - ) - ) - - enum EnumK1Contra[-A]: - case Leaf(value: A => Unit) - case Rec(l: EnumK1Contra[A], r: EnumK1Contra[A]) - - object EnumK1Contra: - given [A](using Arbitrary[A => Unit]): Arbitrary[EnumK1Contra[A]] = Arbitrary( - Gen.recursive(rec => - Gen.sized(size => - val leaf = Arbitrary.arbitrary[A => Unit].map(Leaf.apply) - if size == 0 then leaf - else - Gen.resize( - size / 2, - Gen.oneOf( - leaf, - for - l <- rec - r <- rec - yield Rec(l, r) - ) - ) - ) - ) - ) - - enum EnumK1Inv[A]: - case Leaf(cov: A, contra: A => Unit) - case Rec(l: EnumK1Inv[A], r: EnumK1Inv[A]) - - object EnumK1Inv: - given [A](using Arbitrary[A], Arbitrary[A => Unit]): Arbitrary[EnumK1Inv[A]] = Arbitrary( - Gen.recursive(rec => - Gen.sized(size => - val leaf = - for - cov <- Arbitrary.arbitrary[A] - contra <- Arbitrary.arbitrary[A => Unit] - yield Leaf(cov, contra) - if size == 0 then leaf - else - Gen.resize( - size / 2, - Gen.oneOf( - leaf, - for - l <- rec - r <- rec - yield Rec(l, r) - ) - ) - ) - ) - ) - - sealed trait Rgb - object Rgb: - case object Red extends Rgb - case object Green extends Rgb - case object Blue extends Rgb - - final case class ComplexProduct[T](lbl: String, set: Set[T], fns: Vector[() => T], opt: Eval[Option[T]]) - object ComplexProduct: - given [T: Arbitrary]: Arbitrary[ComplexProduct[T]] = Arbitrary(for - lbl <- Arbitrary.arbitrary[String] - set <- Arbitrary.arbitrary[Set[T]] - vec <- Arbitrary.arbitrary[Vector[T]] - fns = vec.map(x => () => x) - opt <- Arbitrary.arbitrary[Option[T]] - yield ComplexProduct(lbl, set, fns, Eval.now(opt))) - - final case class Box[+A](content: A) - object Box: - given [A: Arbitrary]: Arbitrary[Box[A]] = Arbitrary(Arbitrary.arbitrary[A].map(apply)) - given [A: Cogen]: Cogen[Box[A]] = Cogen[A].contramap(_.content) - - final case class Recursive(i: Int, is: Option[Recursive]) - object Recursive extends ((Int, Option[Recursive]) => Recursive): - given Arbitrary[Recursive] = - def recursive(size: Int): Gen[Recursive] = for - i <- Arbitrary.arbitrary[Int] - is <- if (size <= 0) Gen.const(None) else Gen.option(recursive(size / 2)) - yield Recursive(i, is) - Arbitrary(Gen.sized(recursive)) - - given Cogen[Recursive] = - @tailrec def perturb(seed: Seed, rec: Recursive): Seed = - val i = Cogen[Int].perturb(seed, rec.i) - rec.is match - case Some(is) => perturb(i, is) - case None => i - Cogen(perturb _) - - final case class Interleaved[T](i: Int, t: T, l: Long, tt: Vector[T], s: String) - object Interleaved: - def empty[T](t: T): Interleaved[T] = - Interleaved(0, t, 0, Vector.empty, "") - - given [T: Arbitrary]: Arbitrary[Interleaved[T]] = - Arbitrary(Arbitrary.arbitrary[(Int, T, Long, Vector[T], String)].map((apply[T] _).tupled)) - - given [T: Cogen]: Cogen[Interleaved[T]] = - Cogen[(Int, T, Long, Vector[T], String)].contramap(x => (x.i, x.t, x.l, x.tt, x.s)) - - case class Bivariant[A](run: A => Boolean, store: A) - object Bivariant: - given [A: Arbitrary]: Arbitrary[Bivariant[A]] = Arbitrary( - for - a <- Arbitrary.arbitrary[A] - f <- Arbitrary.arbitrary[Boolean].map(Function.const[Boolean, A]) - yield Bivariant[A](f, a) - ) - - case class Pred[A](run: A => Boolean) - - sealed trait IList[A] - final case class ICons[A](head: A, tail: IList[A]) extends IList[A] - final case class INil[A]() extends IList[A] - - object ICons: - - given [A: Arbitrary]: Arbitrary[ICons[A]] = - Arbitrary(for - head <- Arbitrary.arbitrary[A] - tail <- Arbitrary.arbitrary[IList[A]] - yield ICons(head, tail)) - - object IList: - - given [A: Arbitrary]: Arbitrary[IList[A]] = - Arbitrary(Arbitrary.arbitrary[Seq[A]].map(fromSeq)) - - given [A: Cogen]: Cogen[IList[A]] = - Cogen[Seq[A]].contramap(toList) - - def fromSeq[T](ts: Seq[T]): IList[T] = - ts.foldRight[IList[T]](INil())(ICons.apply) - - def toList[T](list: IList[T]): List[T] = - @tailrec def loop(list: IList[T], acc: List[T]): List[T] = list match - case INil() => acc.reverse - case ICons(head, tail) => loop(tail, head :: acc) - loop(list, Nil) - - sealed trait Snoc[A] - final case class SCons[A](init: Snoc[A], last: A) extends Snoc[A] - final case class SNil[A]() extends Snoc[A] - - object Snoc: - - given [A: Arbitrary]: Arbitrary[Snoc[A]] = - Arbitrary(Arbitrary.arbitrary[Seq[A]].map(fromSeq)) - - def fromSeq[T](ts: Seq[T]): Snoc[T] = - ts.foldLeft[Snoc[T]](SNil())(SCons.apply) - - def toList[T](snoc: Snoc[T]): List[T] = - @tailrec def loop(snoc: Snoc[T], acc: List[T]): List[T] = snoc match - case SNil() => acc - case SCons(init, last) => loop(init, last :: acc) - loop(snoc, Nil) - - object SCons: - - given [A: Arbitrary]: Arbitrary[SCons[A]] = - Arbitrary(for - init <- Arbitrary.arbitrary[Snoc[A]] - last <- Arbitrary.arbitrary[A] - yield SCons(init, last)) - - sealed trait Tree[A] - final case class Leaf[A](value: A) extends Tree[A] - final case class Node[A](left: Tree[A], right: Tree[A]) extends Tree[A] - - object Tree: - given [A: Arbitrary]: Arbitrary[Tree[A]] = - val leaf = Arbitrary.arbitrary[A].map(Leaf.apply) - - def tree(maxDepth: Int): Gen[Tree[A]] = - if (maxDepth <= 0) leaf - else Gen.oneOf(leaf, node(maxDepth)) - - def node(maxDepth: Int): Gen[Tree[A]] = for - depthL <- Gen.choose(0, maxDepth - 1) - depthR <- Gen.choose(0, maxDepth - 1) - left <- tree(depthL) - right <- tree(depthR) - yield Node(left, right) - - Arbitrary(Gen.sized(tree)) - - given [A: Cogen]: Cogen[Tree[A]] = - lazy val cogen: Cogen[Tree[A]] = Cogen { (seed, tree) => - tree match - case Leaf(value) => Cogen[A].perturb(seed, value) - case Node(left, right) => cogen.perturb(cogen.perturb(seed, left), right) - } - - cogen - - final case class Foo(i: Int, b: Option[String]) - object Foo: - - given Cogen[Foo] = - Cogen[Int].contramap(_.i) - - given Arbitrary[Foo] = - Arbitrary(for - i <- Arbitrary.arbitrary[Int] - b <- Arbitrary.arbitrary[Option[String]] - yield Foo(i, b)) - - final case class CommutativeFoo(i: Int, b: Option[Long]) - object CommutativeFoo: - - given Cogen[CommutativeFoo] = - Cogen[(Int, Option[Long])].contramap(x => (x.i, x.b)) - - given Arbitrary[CommutativeFoo] = - Arbitrary(for - i <- Arbitrary.arbitrary[Int] - b <- Arbitrary.arbitrary[Option[Long]] - yield CommutativeFoo(i, b)) - - case class Inner(i: Int) - case class Outer(in: Inner) - - object Inner: - - given Arbitrary[Inner] = - Arbitrary(Arbitrary.arbitrary[Int].map(apply)) - - given Cogen[Inner] = - Cogen[Int].contramap(_.i) - - object Outer: - - given Arbitrary[Outer] = - Arbitrary(Arbitrary.arbitrary[Inner].map(apply)) - - given Cogen[Outer] = - Cogen[Inner].contramap(_.in) - - sealed trait IntTree - final case class IntLeaf(t: Int) extends IntTree - final case class IntNode(l: IntTree, r: IntTree) extends IntTree - - object IntTree - - sealed trait GenericAdt[A] - final case class GenericAdtCase[A](value: Option[A]) extends GenericAdt[A] - - object GenericAdt: - - given [A: Arbitrary]: Arbitrary[GenericAdt[A]] = - Arbitrary(Arbitrary.arbitrary[Option[A]].map(GenericAdtCase.apply)) - - given [A: Cogen]: Cogen[GenericAdt[A]] = - Cogen[Option[A]].contramap { case GenericAdtCase(value) => value } - - final case class CaseClassWOption[A](value: Option[A]) - object CaseClassWOption: - given [A: Arbitrary]: Arbitrary[CaseClassWOption[A]] = - Arbitrary(Arbitrary.arbitrary[Option[A]].map(apply)) - - final case class First(value: String) - final case class Second(value: String) - final case class Middle(first: First, second: Option[Second]) - final case class Top(middle: Middle) - - final case class Address(street: String, city: String, state: String) - final case class ContactInfo(phoneNumber: String, address: Address) - final case class People(name: String, contactInfo: ContactInfo) - - final case class ListField(a: String, b: List[ListFieldChild]) - final case class ListFieldChild(c: Int) - - final case class Large( - bar1: String, - bar2: Int, - bar3: Boolean, - bar4: Large2, - bar5: List[String], - bar6: Set[Boolean], - bar7: Double, - bar8: Long, - bar9: Char, - bar10: Float, - bar11: String, - bar12: Map[String, Int], - bar13: Boolean, - bar14: Option[String], - bar15: List[String], - bar16: Set[Boolean], - bar17: Double, - bar18: Long, - bar19: Char, - bar20: Float - ) - - final case class Large2( - bar1: String, - bar2: Int, - bar3: Boolean, - bar4: Option[String], - bar5: List[String], - bar6: Set[Boolean], - bar7: Double, - bar8: Long, - bar9: Char, - bar10: Float, - bar11: String, - bar12: Map[String, Int], - bar13: Boolean, - bar14: Option[String], - bar15: List[String], - bar16: Set[Boolean], - bar17: Double, - bar18: Long, - bar19: Char, - bar20: Float, - bar21: String - ) - - final case class Large3( - bar1: String, - bar2: Int, - bar3: Boolean, - bar4: Option[String], - bar5: List[String], - bar6: Set[Boolean], - bar7: Double, - bar8: Long, - bar9: Char, - bar10: Float, - bar11: String, - bar12: Map[String, Int], - bar13: Boolean, - bar14: Option[String], - bar15: List[String], - bar16: Set[Boolean], - bar17: Double, - bar18: Long, - bar19: Char, - bar20: Float, - bar21: String - ) - - final case class Large4( - bar1: String, - bar2: Int, - bar3: Boolean, - bar4: Large5, - bar5: List[String], - bar6: List[Boolean], - bar7: Double, - bar8: Long, - bar9: Char, - bar10: Float, - bar11: String, - bar12: String, - bar13: Boolean, - bar14: Option[String], - bar15: List[String], - bar16: List[Boolean], - bar17: Double, - bar18: Long, - bar19: Char, - bar20: Float - ) - - final case class Large5( - bar1: String, - bar2: Int, - bar3: Boolean, - bar4: Option[String], - bar5: List[String], - bar6: List[Boolean], - bar7: Double, - bar8: Long, - bar9: Char, - bar10: Float, - bar11: String, - bar12: Int, - bar13: Boolean, - bar14: Option[String], - bar15: List[String], - bar16: List[Boolean], - bar17: Double, - bar18: Long, - bar19: Char, - bar20: Float, - bar21: String - ) - -end TestDefns - -trait TestEqInstances: - import TestDefns.* - - given [T: Eq]: Eq[ComplexProduct[T]] with - val eqSet = Eq[Set[T]] - val eqVec = Eq[Vector[T]] - val eqOpt = Eq[Eval[Option[T]]] - - def eqv(x: ComplexProduct[T], y: ComplexProduct[T]) = - x.lbl == y.lbl && - eqSet.eqv(x.set, y.set) && - eqVec.eqv(x.fns.map(_()), y.fns.map(_())) && - eqOpt.eqv(x.opt, y.opt) - - given [A: Eq]: Eq[Box[A]] = - Eq.by(_.content) - - given Eq[Recursive] = - Eq.fromUniversalEquals - - given [T: Eq]: Eq[Interleaved[T]] = - Eq.by(i => (i.i, i.t, i.l, i.tt, i.s)) - - given [A](using A: Eq[A]): Eq[IList[A]] = new Eq[IList[A]]: - @tailrec def eqv(x: IList[A], y: IList[A]): Boolean = (x, y) match - case (ICons(hx, tx), ICons(hy, ty)) => A.eqv(hx, hy) && eqv(tx, ty) - case (INil(), INil()) => true - case _ => false - - given [A](using A: Eq[A]): Eq[Snoc[A]] = new Eq[Snoc[A]]: - @tailrec def eqv(x: Snoc[A], y: Snoc[A]): Boolean = (x, y) match - case (SCons(ix, lx), SCons(iy, ly)) => A.eqv(lx, ly) && eqv(ix, iy) - case (SNil(), SNil()) => true - case _ => false - - given [A: Eq]: Eq[ICons[A]] = Eq.by(identity[IList[A]]) - given [A: Eq]: Eq[SCons[A]] = Eq.by(identity[Snoc[A]]) - - given [A](using A: Eq[A]): Eq[Tree[A]] with - def eqv(x: Tree[A], y: Tree[A]): Boolean = (x, y) match - case (Leaf(vx), Leaf(vy)) => A.eqv(vx, vy) - case (Node(lx, rx), Node(ly, ry)) => eqv(lx, ly) && eqv(rx, ry) - case _ => false - - given [A](using A: Eq[A]): Eq[Bivariant[A]] with - def eqv(x: Bivariant[A], y: Bivariant[A]): Boolean = (x, y) match - case (Bivariant(runX, storeX), Bivariant(runY, storeY)) => - A.eqv(storeX, storeY) && Eq[Boolean].eqv(runX(storeX), runY(storeY)) - - given Eq[Foo] = Eq.fromUniversalEquals - - given Eq[CommutativeFoo] = Eq.fromUniversalEquals - - given [A: Eq]: Eq[GenericAdt[A]] = - val eqvOpt = Eq[Option[A]] - Eq.instance { case (GenericAdtCase(vx), GenericAdtCase(vy)) => eqvOpt.eqv(vx, vy) } - - given [A: Eq]: Eq[CaseClassWOption[A]] = Eq.by(_.value) - - given Eq[Inner] = Eq.fromUniversalEquals - - given Eq[Outer] = Eq.fromUniversalEquals - - given Eq[EnumK0] = - import EnumK0.* - Eq.instance { - case (LeafS(s1), LeafS(s2)) => s1 === s2 - case (LeafI(i1), LeafI(i2)) => i1 === i2 - case _ => false - } - - given [A](using Eq[A]): Eq[EnumK1[A]] = - import EnumK1.* - Eq.instance { - case (Leaf(v1), Leaf(v2)) => v1 === v2 - case (Rec(l1, r1), Rec(l2, r2)) => l1 === l2 && r1 === r2 - case _ => false - } - - given [A](using Eq[A => Unit]): Eq[EnumK1Contra[A]] = - import EnumK1Contra.* - Eq.instance { - case (Leaf(v1), Leaf(v2)) => v1 === v2 - case (Rec(l1, r1), Rec(l2, r2)) => l1 === l2 && r1 === r2 - case _ => false - } - - given [A](using Eq[A], Eq[A => Unit]): Eq[EnumK1Inv[A]] = - import EnumK1Inv.* - Eq.instance { - case (Leaf(cov1, contra1), Leaf(cov2, contra2)) => cov1 === cov2 && contra1 === contra2 - case (Rec(l1, r1), Rec(l2, r2)) => l1 === l2 && r1 === r2 - case _ => false - } - -end TestEqInstances