Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use spurious Fractional[MiniInt] to test Invariant[Fractional] #4216

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ import cats.kernel.{Eq, Order}
import cats.laws.discipline.{ExhaustiveCheck, MiniInt}
import cats.laws.discipline.MiniInt._
import cats.laws.discipline.eq._
import cats.laws.discipline.DeprecatedEqInstances
import org.scalacheck.Arbitrary

trait ScalaVersionSpecificFoldableSuite
trait ScalaVersionSpecificParallelSuite
Expand All @@ -35,7 +33,7 @@ trait ScalaVersionSpecificTraverseSuite

trait ScalaVersionSpecificAlgebraInvariantSuite {
// This version-specific instance is required since 2.12 and below do not have parseString on the Numeric class
protected val integralForMiniInt: Integral[MiniInt] = new Integral[MiniInt] {
protected trait MiniIntNumeric extends Numeric[MiniInt] {
def compare(x: MiniInt, y: MiniInt): Int = Order[MiniInt].compare(x, y)
def plus(x: MiniInt, y: MiniInt): MiniInt = x + y
def minus(x: MiniInt, y: MiniInt): MiniInt = x + (-y)
Expand All @@ -46,8 +44,6 @@ trait ScalaVersionSpecificAlgebraInvariantSuite {
def toLong(x: MiniInt): Long = x.toInt.toLong
def toFloat(x: MiniInt): Float = x.toInt.toFloat
def toDouble(x: MiniInt): Double = x.toInt.toDouble
def quot(x: MiniInt, y: MiniInt): MiniInt = MiniInt.unsafeFromInt(x.toInt / y.toInt)
def rem(x: MiniInt, y: MiniInt): MiniInt = MiniInt.unsafeFromInt(x.toInt % y.toInt)
}

// This version-specific instance is required since 2.12 and below do not have parseString on the Numeric class
Expand Down Expand Up @@ -75,24 +71,4 @@ trait ScalaVersionSpecificAlgebraInvariantSuite {
)
}

// This version-specific instance is required since 2.12 and below do not have parseString on the Numeric class
@annotation.nowarn("cat=deprecation")
implicit protected def eqFractional[A: Eq: Arbitrary]: Eq[Fractional[A]] = {
import DeprecatedEqInstances.catsLawsEqForFn1

Eq.by { fractional =>
(
fractional.compare _,
fractional.plus _,
fractional.minus _,
fractional.times _,
fractional.negate _,
fractional.fromInt _,
fractional.toInt _,
fractional.toLong _,
fractional.toFloat _,
fractional.toDouble _
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,13 @@ package cats.tests

import cats._
import cats.data.NonEmptyLazyList
import cats.laws.discipline.DeprecatedEqInstances
import cats.laws.discipline.ExhaustiveCheck
import cats.laws.discipline.MiniInt
import cats.laws.discipline.NonEmptyParallelTests
import cats.laws.discipline.ParallelTests
import cats.laws.discipline.arbitrary._
import cats.laws.discipline.eq._
import cats.syntax.all._
import org.scalacheck.Arbitrary
import org.scalacheck.Prop._

trait ScalaVersionSpecificFoldableSuite { self: FoldableSuiteAdditional =>
Expand Down Expand Up @@ -191,7 +189,7 @@ trait ScalaVersionSpecificTraverseSuite { self: TraverseSuiteAdditional =>
trait ScalaVersionSpecificAlgebraInvariantSuite {

// This version-specific instance is required since 2.12 and below do not have parseString on the Numeric class
protected val integralForMiniInt: Integral[MiniInt] = new Integral[MiniInt] {
protected trait MiniIntNumeric extends Numeric[MiniInt] {
def compare(x: MiniInt, y: MiniInt): Int = Order[MiniInt].compare(x, y)
def plus(x: MiniInt, y: MiniInt): MiniInt = x + y
def minus(x: MiniInt, y: MiniInt): MiniInt = x + (-y)
Expand All @@ -202,8 +200,6 @@ trait ScalaVersionSpecificAlgebraInvariantSuite {
def toLong(x: MiniInt): Long = x.toInt.toLong
def toFloat(x: MiniInt): Float = x.toInt.toFloat
def toDouble(x: MiniInt): Double = x.toInt.toDouble
def quot(x: MiniInt, y: MiniInt): MiniInt = MiniInt.unsafeFromInt(x.toInt / y.toInt)
def rem(x: MiniInt, y: MiniInt): MiniInt = MiniInt.unsafeFromInt(x.toInt % y.toInt)
def parseString(str: String): Option[MiniInt] = Integral[Int].parseString(str).flatMap(MiniInt.fromInt)
}

Expand Down Expand Up @@ -238,35 +234,6 @@ trait ScalaVersionSpecificAlgebraInvariantSuite {
)
}

// This version-specific instance is required since 2.12 and below do not have parseString on the Numeric class
@annotation.nowarn("cat=deprecation")
implicit protected def eqFractional[A: Eq: Arbitrary]: Eq[Fractional[A]] = {
// This deprecated instance is required since there is not `ExhaustiveCheck` for any types for which a `Fractional`
// can easily be defined
import DeprecatedEqInstances.catsLawsEqForFn1

Eq.by { fractional =>
val parseFloatStrings: Option[Double] => Option[A] = {
case Some(f) => fractional.parseString(f.toString)
case None => fractional.parseString("invalid") // Use this to test parsing of non-numeric strings
}

(
fractional.compare _,
fractional.plus _,
fractional.minus _,
fractional.times _,
fractional.negate _,
fractional.fromInt _,
fractional.toInt _,
fractional.toLong _,
fractional.toFloat _,
fractional.toDouble _,
parseFloatStrings
)
}
}

}

class TraverseLazyListSuite extends TraverseSuite[LazyList]("LazyList")
Expand Down
40 changes: 33 additions & 7 deletions tests/shared/src/test/scala/cats/tests/AlgebraInvariantSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,14 @@ class AlgebraInvariantSuite extends CatsSuite with ScalaVersionSpecificAlgebraIn
implicit private val arbCommutativeGroupInt: Arbitrary[CommutativeGroup[Int]] =
Arbitrary(genCommutativeGroupInt)

protected val integralForMiniInt: Numeric[MiniInt] with Integral[MiniInt] = new MiniIntNumeric
with Integral[MiniInt] {
def quot(x: MiniInt, y: MiniInt): MiniInt = MiniInt.unsafeFromInt(x.toInt / y.toInt)
def rem(x: MiniInt, y: MiniInt): MiniInt = MiniInt.unsafeFromInt(x.toInt % y.toInt)
}

implicit private val arbNumericMiniInt: Arbitrary[Numeric[MiniInt]] = Arbitrary(Gen.const(integralForMiniInt))
implicit private val arbIntegralMiniInt: Arbitrary[Integral[MiniInt]] = Arbitrary(Gen.const(integralForMiniInt))
implicit private val arbFractionalFloat: Arbitrary[Fractional[Float]] = Arbitrary(
Gen.const(implicitly[Fractional[Float]])
)

implicit protected def eqIntegral[A: Eq: ExhaustiveCheck]: Eq[Integral[A]] = {
def makeDivisionOpSafe(unsafeF: (A, A) => A): (A, A) => Option[A] =
Expand All @@ -209,6 +212,14 @@ class AlgebraInvariantSuite extends CatsSuite with ScalaVersionSpecificAlgebraIn
}
}

implicit protected def eqFractional[A: Eq: ExhaustiveCheck]: Eq[Fractional[A]] =
Eq.by { fractional =>
(
fractional: Numeric[A],
fractional.div(_, _)
)
}

checkAll("InvariantMonoidal[Semigroup]", SemigroupTests[Int](InvariantMonoidal[Semigroup].point(0)).semigroup)
checkAll("InvariantMonoidal[CommutativeSemigroup]",
CommutativeSemigroupTests[Int](InvariantMonoidal[CommutativeSemigroup].point(0)).commutativeSemigroup
Expand All @@ -218,10 +229,6 @@ class AlgebraInvariantSuite extends CatsSuite with ScalaVersionSpecificAlgebraIn
InvariantSemigroupalTests[Monoid].invariantSemigroupal[Option[MiniInt], Option[Boolean], Option[Boolean]]
)

checkAll("Invariant[Numeric]", InvariantTests[Numeric].invariant[MiniInt, Boolean, Boolean])
checkAll("Invariant[Integral]", InvariantTests[Integral].invariant[MiniInt, Boolean, Boolean])
checkAll("Invariant[Fractional]", InvariantTests[Fractional].invariant[Float, Boolean, Boolean])

{
val S: Semigroup[Int] = Semigroup[Int].imap(identity)(identity)
checkAll("Semigroup[Int]", SemigroupTests[Int](S).semigroup)
Expand Down Expand Up @@ -310,6 +317,25 @@ class AlgebraInvariantSuite extends CatsSuite with ScalaVersionSpecificAlgebraIn
checkAll("Invariant[CommutativeGroup]", InvariantTests[CommutativeGroup].invariant[MiniInt, Boolean, Boolean])
checkAll("Invariant[CommutativeGroup]", SerializableTests.serializable(Invariant[CommutativeGroup]))

checkAll("Invariant[Numeric]", InvariantTests[Numeric].invariant[MiniInt, Boolean, Boolean])
checkAll("Invariant[Integral]", InvariantTests[Integral].invariant[MiniInt, Boolean, Boolean])

{
// This is a spurious instance since MiniInt is not a Fractional data type. But we use it since we don't have a
// Fractional type for which ExhaustiveCheck is also implemented. See https://github.com/typelevel/cats/pull/4033
val fractionalForMiniInt: Fractional[MiniInt] = new MiniIntNumeric with Fractional[MiniInt] {
def div(x: MiniInt, y: MiniInt): MiniInt =
if (y == MiniInt.zero) {
MiniInt.maxValue
} else {
x / y
}
}
implicit val arbFractionalMiniInt: Arbitrary[Fractional[MiniInt]] = Arbitrary(Gen.const(fractionalForMiniInt))

checkAll("Invariant[Fractional]", InvariantTests[Fractional].invariant[MiniInt, Boolean, Boolean])
}

checkAll("InvariantMonoidal[Semigroup]",
InvariantMonoidalTests[Semigroup].invariantMonoidal[Option[MiniInt], Option[Boolean], Option[Boolean]]
)
Expand Down