Skip to content

Commit

Permalink
More coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
edmundnoble committed Jul 5, 2017
1 parent c012417 commit 9aa695d
Show file tree
Hide file tree
Showing 20 changed files with 227 additions and 59 deletions.
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("Semigroup[Either[String, Int]]", SerializableTests.serializable(Semigroup[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)
checkAll("Double", GroupLaws[Double].group)
// float is *not* associative, and scalacheck knows
// checkAll("Float", GroupLaws[Float].group)
checkAll("Long", GroupLaws[Long].group)
}
10 changes: 9 additions & 1 deletion tests/src/test/scala/cats/tests/IdTTests.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package cats
package tests

import cats.data.IdT
import cats.{Foldable, Functor, Monad, NonEmptyTraverse, Traverse}
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 +77,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._
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

0 comments on commit 9aa695d

Please sign in to comment.