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

More coverage #1752

Merged
merged 4 commits into from
Jul 17, 2017
Merged
Show file tree
Hide file tree
Changes from 3 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
43 changes: 35 additions & 8 deletions core/src/main/scala/cats/data/IdT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,18 @@ final case class IdT[F[_], A](value: F[A]) {
def foldRight[B](lb: Eval[B])(f: (A, Eval[B]) => Eval[B])(implicit F: Foldable[F]): Eval[B] =
F.foldRight(value, lb)(f)

def reduceLeftTo[B](f: A => B)(g: (B, A) => B)(implicit F: Reducible[F]): B =
F.reduceLeftTo(value)(f)(g)

def reduceRightTo[B](f: A => B)(g: (A, Eval[B]) => Eval[B])(implicit F: Reducible[F]): Eval[B] =
F.reduceRightTo(value)(f)(g)

def traverse[G[_], B](f: A => G[B])(implicit F: Traverse[F], G: Applicative[G]): G[IdT[F, B]] =
G.map(F.traverse(value)(f))(IdT(_))

def nonEmptyTraverse[G[_], B](f: A => G[B])(implicit F: NonEmptyTraverse[F], G: Apply[G]): G[IdT[F, B]] =
G.map(F.nonEmptyTraverse(value)(f))(IdT(_))

def ap[B](f: IdT[F, A => B])(implicit F: Apply[F]): IdT[F, B] =
IdT(F.ap(f.value)(value))

Expand Down Expand Up @@ -91,42 +100,60 @@ private[data] sealed trait IdTTraverse[F[_]] extends Traverse[IdT[F, ?]] with Id
fa.traverse(f)
}

private[data] sealed abstract class IdTInstances4 {
private[data] sealed trait IdTNonEmptyTraverse[F[_]] extends IdTTraverse[F] with NonEmptyTraverse[IdT[F, ?]] with IdTFunctor[F] {
implicit val F0: NonEmptyTraverse[F]

def nonEmptyTraverse[G[_]: Apply, A, B](fa: IdT[F, A])(f: A => G[B]): G[IdT[F, B]] =
fa.nonEmptyTraverse(f)

def reduceLeftTo[A, B](fa: IdT[F, A])(f: A => B)(g: (B, A) => B): B =
fa.reduceLeftTo(f)(g)

def reduceRightTo[A, B](fa: IdT[F, A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] =
fa.reduceRightTo(f)(g)
}

private[data] sealed abstract class IdTInstances5 {
implicit def catsDataFunctorForIdT[F[_]](implicit F: Functor[F]): Functor[IdT[F, ?]] =
new IdTFunctor[F] { implicit val F0: Functor[F] = F }
}

private[data] sealed abstract class IdTInstances3 extends IdTInstances4 {
private[data] sealed abstract class IdTInstances4 extends IdTInstances5 {
implicit def catsDataApplyForIdT[F[_]](implicit F: Apply[F]): Apply[IdT[F, ?]] =
new IdTApply[F] { implicit val F0: Apply[F] = F }
}

private[data] sealed abstract class IdTInstances2 extends IdTInstances3 {
private[data] sealed abstract class IdTInstances3 extends IdTInstances4 {
implicit def catsDataApplicativeForIdT[F[_]](implicit F: Applicative[F]): Applicative[IdT[F, ?]] =
new IdTApplicative[F] { implicit val F0: Applicative[F] = F }
}

private[data] sealed abstract class IdTInstances1 extends IdTInstances2 {
private[data] sealed abstract class IdTInstances2 extends IdTInstances3 {
implicit def catsDataFlatMapForIdT[F[_]](implicit F: FlatMap[F]): FlatMap[IdT[F, ?]] =
new IdTFlatMap[F] { implicit val F0: FlatMap[F] = F }
}

private[data] sealed abstract class IdTInstances0 extends IdTInstances1 {

private[data] sealed abstract class IdTInstances1 extends IdTInstances2 {
implicit def catsDataMonadForIdT[F[_]](implicit F: Monad[F]): Monad[IdT[F, ?]] =
new IdTMonad[F] { implicit val F0: Monad[F] = F }

implicit def catsDataFoldableForIdT[F[_]](implicit F: Foldable[F]): Foldable[IdT[F, ?]] =
new IdTFoldable[F] { implicit val F0: Foldable[F] = F }
}

private[data] sealed abstract class IdTInstances0 extends IdTInstances1 {

implicit def catsDataTraverseForIdT[F[_]](implicit F: Traverse[F]): Traverse[IdT[F, ?]] =
new IdTTraverse[F] { implicit val F0: Traverse[F] = F }

implicit def catsDataEqForIdT[F[_], A](implicit F: Eq[F[A]]): Eq[IdT[F, A]] =
F.on(_.value)
}

private[data] sealed abstract class IdTInstances extends IdTInstances0 {

implicit def catsDataTraverseForIdT[F[_]](implicit F: Traverse[F]): Traverse[IdT[F, ?]] =
new IdTTraverse[F] { implicit val F0: Traverse[F] = F }
implicit def catsDataNonEmptyTraverseForIdT[F[_]](implicit F: NonEmptyTraverse[F]): NonEmptyTraverse[IdT[F, ?]] =
new IdTNonEmptyTraverse[F] { implicit val F0: NonEmptyTraverse[F] = F }

implicit def catsDataOrderForIdT[F[_], A](implicit F: Order[F[A]]): Order[IdT[F, A]] =
F.on(_.value)
Expand Down
18 changes: 8 additions & 10 deletions core/src/main/scala/cats/data/NonEmptyList.scala
Original file line number Diff line number Diff line change
Expand Up @@ -276,11 +276,10 @@ final case class NonEmptyList[+A](head: A, tail: List[A]) {
* res0: cats.data.NonEmptyList[(Char, Int)] = NonEmptyList((z,1), (a,4), (e,22))
* }}}
*/
def sortBy[B](f: A => B)(implicit B: Order[B]): NonEmptyList[A] =
toList.sortBy(f)(B.toOrdering) match {
case x :: xs => NonEmptyList(x, xs)
case Nil => sys.error("unreachable: sorting a NonEmptyList cannot produce an empty List")
}
def sortBy[B](f: A => B)(implicit B: Order[B]): NonEmptyList[A] = {
// safe: sorting a NonEmptyList cannot produce an empty List
NonEmptyList.fromListUnsafe(toList.sortBy(f)(B.toOrdering))
}

/**
* Sorts this `NonEmptyList` according to an `Order`
Expand All @@ -293,11 +292,10 @@ final case class NonEmptyList[+A](head: A, tail: List[A]) {
* res0: cats.data.NonEmptyList[Int] = NonEmptyList(3, 4, 9, 12)
* }}}
*/
def sorted[AA >: A](implicit AA: Order[AA]): NonEmptyList[AA] =
toList.sorted(AA.toOrdering) match {
case x :: xs => NonEmptyList(x, xs)
case Nil => sys.error("unreachable: sorting a NonEmptyList cannot produce an empty List")
}
def sorted[AA >: A](implicit AA: Order[AA]): NonEmptyList[AA] = {
// safe: sorting a NonEmptyList cannot produce an empty List
NonEmptyList.fromListUnsafe(toList.sorted(AA.toOrdering))
}

/**
* Groups elements inside of this `NonEmptyList` using a mapping function
Expand Down
3 changes: 0 additions & 3 deletions core/src/main/scala/cats/instances/stream.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances {
def flatMap[A, B](fa: Stream[A])(f: A => Stream[B]): Stream[B] =
fa.flatMap(f)

override def map2[A, B, Z](fa: Stream[A], fb: Stream[B])(f: (A, B) => Z): Stream[Z] =
fa.flatMap(a => fb.map(b => f(a, b)))

def coflatMap[A, B](fa: Stream[A])(f: Stream[A] => B): Stream[B] =
fa.tails.toStream.init.map(f)

Expand Down
8 changes: 8 additions & 0 deletions free/src/test/scala/cats/free/FreeTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ class FreeTests extends CatsSuite {
assert(res == List(112358))
}

test(".run") {
val r = Free.pure[Id, Int](12358)
def recurse(r: Free[Id, Int], n: Int): Free[Id, Int] =
if (n > 0) recurse(r.flatMap(x => Free.pure(x + 1)), n - 1) else r
val res = recurse(r, 100000).run
assert(res == 112358)
}

sealed trait Test1Algebra[A]

case class Test1[A](value : Int, f: Int => A) extends Test1Algebra[A]
Expand Down
8 changes: 6 additions & 2 deletions jvm/src/test/scala/cats/tests/FutureTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import cats.tests.{CatsSuite, ListWrapper}
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global

import org.scalacheck.Arbitrary
import org.scalacheck.{Arbitrary, Cogen}
import org.scalacheck.Arbitrary.arbitrary
import org.scalacheck.rng.Seed

class FutureTests extends CatsSuite {
val timeout = 3.seconds
Expand All @@ -28,6 +28,9 @@ class FutureTests extends CatsSuite {
}
}

implicit def cogen[A: Cogen]: Cogen[Future[A]] =
Cogen[Future[A]] { (seed: Seed, t: Future[A]) => Cogen[A].perturb(seed, Await.result(t, timeout)) }

implicit val throwableEq: Eq[Throwable] =
Eq[String].on(_.toString)

Expand All @@ -37,6 +40,7 @@ class FutureTests extends CatsSuite {

checkAll("Future with Throwable", MonadErrorTests[Future, Throwable].monadError[Int, Int, Int])
checkAll("Future", MonadTests[Future].monad[Int, Int, Int])
checkAll("Future", CoflatMapTests[Future].coflatMap[Int, Int, Int])

{
implicit val F = ListWrapper.semigroup[Int]
Expand Down
2 changes: 0 additions & 2 deletions macros/src/main/scala/cats/macros/Ops.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import scala.reflect.NameTransformer

object Ops extends machinist.Ops {

def uesc(c: Char): String = "$u%04X".format(c.toInt)

val operatorNames: Map[String, String] =
List(
("===", "eqv"),
Expand Down
7 changes: 5 additions & 2 deletions tests/src/test/scala/cats/tests/ApplicativeTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import cats.Applicative

class ApplicativeTests extends CatsSuite {

test("replicateA creates a List of 'n' copies of given Applicative 'fa'") {
test("Applicative#traverse is equivalent to Traverse#traverse") {
val f: (Int) => Option[Int] = x => Some(x + 1)
Applicative[Option].traverse(List(1, 2))(f) should ===(Traverse[List].traverse(List(1, 2))(f))
}

test("replicateA creates a List of 'n' copies of given Applicative 'fa'") {
val A = Applicative[Option]
val fa = A.pure(1)
fa.replicateA(5) should === (Some(List(1,1,1,1,1)))

}

test("whenA return given argument when cond is true") {
Expand Down
5 changes: 5 additions & 0 deletions tests/src/test/scala/cats/tests/ComposeTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,9 @@ class ComposeTest extends CatsSuite {

val functionAlgebra = functionCompose.algebra[Int]
checkAll("Compose[Function1].algebra[Int]", GroupLaws[Endo[Int]].semigroup(functionAlgebra))

test("syntax") {
(((_: Int) + 1) <<< ((_: Int) / 2))(2) should be(2)
(((_: Int) + 1) >>> ((_: Int) / 2))(5) should be(3)
}
}
4 changes: 4 additions & 0 deletions tests/src/test/scala/cats/tests/EitherTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class EitherTests extends CatsSuite {
implicit val iso = CartesianTests.Isomorphisms.invariant[Either[Int, ?]]

checkAll("Either[String, Int]", GroupLaws[Either[String, Int]].monoid)
checkAll("Monoid[Either[String, Int]]", SerializableTests.serializable(Monoid[Either[String, Int]]))

checkAll("Either[Int, Int]", CartesianTests[Either[Int, ?]].cartesian[Int, Int, Int])
checkAll("Cartesian[Either[Int, ?]]", SerializableTests.serializable(Cartesian[Either[Int, ?]]))
Expand All @@ -28,6 +29,9 @@ class EitherTests extends CatsSuite {
checkAll("Either[ListWrapper[String], ?]", SemigroupKTests[Either[ListWrapper[String], ?]].semigroupK[Int])
checkAll("SemigroupK[Either[ListWrapper[String], ?]]", SerializableTests.serializable(SemigroupK[Either[ListWrapper[String], ?]]))

checkAll("Either[ListWrapper[String], Int]", GroupLaws[Either[ListWrapper[String], Int]].semigroup)
checkAll("Semigroup[Either[ListWrapper[String], Int]]", SerializableTests.serializable(Semigroup[Either[ListWrapper[String], Int]]))

val partialOrder = catsStdPartialOrderForEither[Int, String]
val order = implicitly[Order[Either[Int, String]]]
val monad = implicitly[Monad[Either[Int, ?]]]
Expand Down
19 changes: 11 additions & 8 deletions tests/src/test/scala/cats/tests/EvalTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,17 @@ class EvalTests extends CatsSuite {
}

test(".value should evaluate only once on the result of .memoize"){
forAll { i: Eval[Int] =>
val spooky = new Spooky
val i2 = i.map(_ => spooky.increment).memoize
i2.value
spooky.counter should === (1)
i2.value
spooky.counter should === (1)
}
val spooky = new Spooky
val i2 = Eval.always(spooky.increment()).memoize
val i3 = Eval.now(()).flatMap(_ => Eval.later(spooky.increment())).memoize
i2.value
spooky.counter should === (1)
i2.value
spooky.counter should === (1)
i3.value
spooky.counter should === (2)
i3.value
spooky.counter should === (2)
}

{
Expand Down
26 changes: 26 additions & 0 deletions tests/src/test/scala/cats/tests/GroupTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cats
package tests

import cats.kernel.laws.GroupLaws

class GroupTests extends CatsSuite {
test("combine minValue") {
Group[Int].combineN(1, Int.MinValue) should ===(Int.MinValue)
}

test("combine negative") {
Group[Int].combineN(1, -1) should ===(-1)
Group[Int].combineN(1, -10) should ===(-10)
}

test("companion object syntax") {
Group[Int].inverse(1) should ===(-1)
Group[Int].remove(1, 2) should ===(-1)
}

checkAll("Int", GroupLaws[Int].group)
// float and double are *not* associative, and scalacheck knows
// checkAll("Double", GroupLaws[Double].group)
// checkAll("Float", GroupLaws[Float].group)
checkAll("Long", GroupLaws[Long].group)
}
9 changes: 8 additions & 1 deletion tests/src/test/scala/cats/tests/IdTTests.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cats
package tests

import cats.data.IdT
import cats.data.{IdT, NonEmptyList}
import cats.kernel.laws.OrderLaws
import cats.laws.discipline._
import cats.laws.discipline.arbitrary._
Expand Down Expand Up @@ -76,6 +76,13 @@ class IdTTests extends CatsSuite {
checkAll("Traverse[IdT[ListWrapper, ?]]", SerializableTests.serializable(Traverse[IdT[ListWrapper, ?]]))
}

{
implicit val F = NonEmptyList.catsDataInstancesForNonEmptyList

checkAll("IdT[NonEmptyList, Int]", NonEmptyTraverseTests[IdT[NonEmptyList, ?]].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option])
checkAll("NonEmptyTraverse[IdT[NonEmptyList, ?]]", SerializableTests.serializable(NonEmptyTraverse[IdT[NonEmptyList, ?]]))
}


test("flatMap and flatMapF consistent") {
forAll { (idT: IdT[Option, Int], f: Int => IdT[Option, Int]) =>
Expand Down
19 changes: 19 additions & 0 deletions tests/src/test/scala/cats/tests/IsTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package cats
package tests

class IsTests extends CatsSuite {
import evidence._

test("syntax") {
trait Bar

val lifted: Bar Is Bar = Is.refl[Bar]
val andThen: Leibniz[Bar, Bar] = lifted.andThen(lifted)
val compose: Leibniz[Bar, Bar] = lifted.compose(lifted)
val flip: Leibniz[Bar, Bar] = lifted.flip
val lift: Leibniz[List[Bar], List[Bar]] = lifted.lift[List]
val coerce: Bar = lifted.coerce(new Bar {})
val predefEq: =:=[Bar, Bar] = lifted.predefEq
}

}
30 changes: 30 additions & 0 deletions tests/src/test/scala/cats/tests/KleisliTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,36 @@ class KleisliTests extends CatsSuite {
}
}

test("mapF") {
forAll { (f: Kleisli[List, Int, Int], t: List[Int] => List[Int], i: Int) =>
t(f.run(i)) should === (f.mapF(t).run(i))
}
}

test("flatMapF") {
forAll { (f: Kleisli[List, Int, Int], t: Int => List[Int], i: Int) =>
f.run(i).flatMap(t) should === (f.flatMapF(t).run(i))
}
}

test("lower") {
forAll { (f: Kleisli[List, Int, Int], i: Int) =>
f.run(i) should === (f.lower.run(i).flatten)
}
}

test("apply") {
forAll { (f: Kleisli[List, Int, Int], i: Int) =>
f.run(i) should === (f(i))
}
}

test("traverse") {
forAll { (f: Kleisli[List, Int, Int], i: Int) =>
f.traverse(Some(i): Option[Int]) should === ((Some(i): Option[Int]).traverse(f(_)))
}
}

test("lift") {
val f = Kleisli { (x: Int) => (Some(x + 1): Option[Int]) }
val l = f.lift[List]
Expand Down
17 changes: 12 additions & 5 deletions tests/src/test/scala/cats/tests/MonoidTests.scala
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
package cats
package tests

import org.scalatest._

import cats.functor._

class MonoidTests extends FunSuite {
class MonoidTests extends CatsSuite {
{
import cats.implicits._
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we maybe include a comment specifying that the instances below should be found when all the instances are in scope?
It might not be as obvious what we are doing here, now that we moved to CatsSuite.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, it seems like all other transformers do not have or need such a comment.

Invariant[Monoid]
Cartesian[Monoid]
InvariantMonoidal[Monoid]
}

{
test("companion object syntax") {
Monoid.empty[Int] should ===(0)
Monoid.isEmpty(1) should ===(false)
Monoid.isEmpty(0) should ===(true)
}
}

object MonoidTests {
def summonInstance(): Unit = {
import cats.instances.monoid._
Invariant[Monoid]
Cartesian[Monoid]
InvariantMonoidal[Monoid]
()
}

}
Loading