From a854cd18f963820684938172b686b84fc337bae4 Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Thu, 14 Sep 2017 01:36:24 -0400 Subject: [PATCH 1/2] Improve test coverage, especially around caches. We still have coverage gaps in Rule.scala and Dag.scala, which will have to be dealt with another time. --- .../main/scala/com/stripe/dagon/HMap.scala | 2 + .../scala/com/stripe/dagon/CacheTests.scala | 50 ++++++++++++++++++ .../scala/com/stripe/dagon/HCacheTests.scala | 52 +++++++++++++++++++ .../scala/com/stripe/dagon/HMapTests.scala | 5 ++ 4 files changed, 109 insertions(+) create mode 100644 core/src/test/scala/com/stripe/dagon/CacheTests.scala create mode 100644 core/src/test/scala/com/stripe/dagon/HCacheTests.scala diff --git a/core/src/main/scala/com/stripe/dagon/HMap.scala b/core/src/main/scala/com/stripe/dagon/HMap.scala index dbc3b17..efd6b7f 100644 --- a/core/src/main/scala/com/stripe/dagon/HMap.scala +++ b/core/src/main/scala/com/stripe/dagon/HMap.scala @@ -58,6 +58,8 @@ final class HMap[K[_], V[_]](protected val map: Map[K[_], V[_]]) { def contains[T](id: K[T]): Boolean = get(id).isDefined + def isEmpty: Boolean = map.isEmpty + def size: Int = map.size def forallKeys(p: K[_] => Boolean): Boolean = diff --git a/core/src/test/scala/com/stripe/dagon/CacheTests.scala b/core/src/test/scala/com/stripe/dagon/CacheTests.scala new file mode 100644 index 0000000..02bb929 --- /dev/null +++ b/core/src/test/scala/com/stripe/dagon/CacheTests.scala @@ -0,0 +1,50 @@ +package com.stripe.dagon + +import org.scalacheck.Prop._ +import org.scalacheck.{Arbitrary, Cogen, Properties} + +import scala.reflect.runtime.universe._ + +abstract class CacheTests[K: TypeTag: Cogen: Arbitrary, V: TypeTag: Arbitrary] + extends Properties(s"Cache[${typeTag[K].tpe}, ${typeTag[V].tpe}]") { + + def buildMap(c: Cache[K, V], ks: Iterable[K], f: K => V): Map[K, V] = + ks.iterator.foldLeft(Map.empty[K, V]) { + (m, k) => m.updated(k, c.getOrElseUpdate(k, f(k))) + } + + property("getOrElseUpdate") = + forAll { (f: K => V, k: K, v1: V, v2: V) => + val c = Cache.empty[K, V] + var count = 0 + val x = c.getOrElseUpdate(k, { count += 1; v1 }) + val y = c.getOrElseUpdate(k, { count += 1; v2 }) + x == v1 && y == v1 && count == 1 + } + + property("toMap") = + forAll { (f: K => V, ks: Set[K]) => + val c = Cache.empty[K, V] + val m = buildMap(c, ks, f) + c.toMap == m + } + + property("duplicate") = + forAll { (f: K => V, ks: Set[K]) => + val c = Cache.empty[K, V] + val d = c.duplicate + buildMap(c, ks, f) + d.toMap.isEmpty + } + + property("reset works") = + forAll { (f: K => V, ks: Set[K]) => + val c = Cache.empty[K, V] + buildMap(c, ks, f) + val d = c.duplicate + c.reset() + c.toMap.isEmpty && d.toMap.size == ks.size + } +} + +object CacheTestsSL extends CacheTests[String, Long] diff --git a/core/src/test/scala/com/stripe/dagon/HCacheTests.scala b/core/src/test/scala/com/stripe/dagon/HCacheTests.scala new file mode 100644 index 0000000..a1c76ff --- /dev/null +++ b/core/src/test/scala/com/stripe/dagon/HCacheTests.scala @@ -0,0 +1,52 @@ +package com.stripe.dagon + +import org.scalacheck.Prop._ +import org.scalacheck.{Arbitrary, Cogen, Properties} + +import scala.reflect.runtime.universe._ + +abstract class HCacheTests[K[_], V[_]](implicit + kt: TypeTag[K[Int]], ka: Arbitrary[K[Int]], kc: Cogen[K[Int]], + vt: TypeTag[V[Int]], va: Arbitrary[V[Int]]) + extends Properties(s"HCache[${kt.tpe}, ${vt.tpe}]") { + + def buildHMap(c: HCache[K, V], ks: Iterable[K[Int]], f: K[Int] => V[Int]): HMap[K, V] = + ks.iterator.foldLeft(HMap.empty[K, V]) { + (m, k) => m.updated(k, c.getOrElseUpdate(k, f(k))) + } + + property("getOrElseUpdate") = + forAll { (f: K[Int] => V[Int], k: K[Int], v1: V[Int], v2: V[Int]) => + val c = HCache.empty[K, V] + var count = 0 + val x = c.getOrElseUpdate(k, { count += 1; v1 }) + val y = c.getOrElseUpdate(k, { count += 1; v2 }) + x == v1 && y == v1 && count == 1 + } + + property("toHMap") = + forAll { (f: K[Int] => V[Int], ks: Set[K[Int]]) => + val c = HCache.empty[K, V] + val m = buildHMap(c, ks, f) + c.toHMap == m + } + + property("duplicate") = + forAll { (f: K[Int] => V[Int], ks: Set[K[Int]]) => + val c = HCache.empty[K, V] + val d = c.duplicate + buildHMap(c, ks, f) + d.toHMap.isEmpty + } + + property("reset works") = + forAll { (f: K[Int] => V[Int], ks: Set[K[Int]]) => + val c = HCache.empty[K, V] + buildHMap(c, ks, f) + val d = c.duplicate + c.reset() + c.toHMap.isEmpty && d.toHMap.size == ks.size + } +} + +object HCacheTestsLL extends HCacheTests[List, List] diff --git a/core/src/test/scala/com/stripe/dagon/HMapTests.scala b/core/src/test/scala/com/stripe/dagon/HMapTests.scala index 4e4ab42..b728fc8 100644 --- a/core/src/test/scala/com/stripe/dagon/HMapTests.scala +++ b/core/src/test/scala/com/stripe/dagon/HMapTests.scala @@ -142,4 +142,9 @@ object HMapTests extends Properties("HMap") { property("HMap.from works") = forAll { (m: Map[K, V]) => HMap.from[Key, Value](m.asInstanceOf[Map[Key[_], Value[_]]]) == fromPairs(m) } + + property("heterogenous equality is false") = + forAll { (h: H) => + h != null && h != 33 + } } From 61058c5c0b1e7f33744bc1c1f102070ecde0e5ec Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Thu, 14 Sep 2017 12:33:45 -0400 Subject: [PATCH 2/2] Fix tests for JS. --- .../src/test/scala/com/stripe/dagon/CacheTests.scala | 7 ++----- .../test/scala/com/stripe/dagon/HCacheTests.scala | 12 +++++------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/core/src/test/scala/com/stripe/dagon/CacheTests.scala b/core/src/test/scala/com/stripe/dagon/CacheTests.scala index 02bb929..d39a749 100644 --- a/core/src/test/scala/com/stripe/dagon/CacheTests.scala +++ b/core/src/test/scala/com/stripe/dagon/CacheTests.scala @@ -3,10 +3,7 @@ package com.stripe.dagon import org.scalacheck.Prop._ import org.scalacheck.{Arbitrary, Cogen, Properties} -import scala.reflect.runtime.universe._ - -abstract class CacheTests[K: TypeTag: Cogen: Arbitrary, V: TypeTag: Arbitrary] - extends Properties(s"Cache[${typeTag[K].tpe}, ${typeTag[V].tpe}]") { +abstract class CacheTests[K: Cogen: Arbitrary, V: Arbitrary](name: String) extends Properties(name) { def buildMap(c: Cache[K, V], ks: Iterable[K], f: K => V): Map[K, V] = ks.iterator.foldLeft(Map.empty[K, V]) { @@ -47,4 +44,4 @@ abstract class CacheTests[K: TypeTag: Cogen: Arbitrary, V: TypeTag: Arbitrary] } } -object CacheTestsSL extends CacheTests[String, Long] +object CacheTestsSL extends CacheTests[String, Long]("CacheTests[String, Long]") diff --git a/core/src/test/scala/com/stripe/dagon/HCacheTests.scala b/core/src/test/scala/com/stripe/dagon/HCacheTests.scala index a1c76ff..3ec1f5d 100644 --- a/core/src/test/scala/com/stripe/dagon/HCacheTests.scala +++ b/core/src/test/scala/com/stripe/dagon/HCacheTests.scala @@ -3,12 +3,10 @@ package com.stripe.dagon import org.scalacheck.Prop._ import org.scalacheck.{Arbitrary, Cogen, Properties} -import scala.reflect.runtime.universe._ - -abstract class HCacheTests[K[_], V[_]](implicit - kt: TypeTag[K[Int]], ka: Arbitrary[K[Int]], kc: Cogen[K[Int]], - vt: TypeTag[V[Int]], va: Arbitrary[V[Int]]) - extends Properties(s"HCache[${kt.tpe}, ${vt.tpe}]") { +abstract class HCacheTests[K[_], V[_]](name: String)(implicit + ka: Arbitrary[K[Int]], kc: Cogen[K[Int]], + va: Arbitrary[V[Int]]) + extends Properties(name) { def buildHMap(c: HCache[K, V], ks: Iterable[K[Int]], f: K[Int] => V[Int]): HMap[K, V] = ks.iterator.foldLeft(HMap.empty[K, V]) { @@ -49,4 +47,4 @@ abstract class HCacheTests[K[_], V[_]](implicit } } -object HCacheTestsLL extends HCacheTests[List, List] +object HCacheTestsLL extends HCacheTests[List, List]("HCacheTests[List, List]")