diff --git a/build.sbt b/build.sbt
index 24c1f9d..49f6537 100755
--- a/build.sbt
+++ b/build.sbt
@@ -11,11 +11,11 @@ lazy val scalacheck = (project in file("."))
Resolver.sonatypeRepo("releases")
),
libraryDependencies ++= Seq(
- "org.scalatest" %% "scalatest" % "2.2.4",
- "org.scala-exercises" %% "exercise-compiler" % version.value,
- "org.scala-exercises" %% "definitions" % version.value,
- "org.scalacheck" %% "scalacheck" % "1.12.5",
- "com.github.alexarchambault" %% "scalacheck-shapeless_1.12" % "0.3.1",
+ "org.scalatest" %% "scalatest" % "3.0.1" exclude("org.scalacheck", "scalacheck"),
+ "org.scala-exercises" %% "exercise-compiler" % version.value excludeAll ExclusionRule("com.github.alexarchambault"),
+ "org.scala-exercises" %% "definitions" % version.value excludeAll ExclusionRule("com.github.alexarchambault"),
+ "com.fortysevendeg" %% "scalacheck-datetime" % "0.2.0",
+ "com.github.alexarchambault" %% "scalacheck-shapeless_1.13" % "1.1.3",
compilerPlugin("org.spire-math" %% "kind-projector" % "0.9.0")
)
)
diff --git a/src/main/scala/scalachecklib/ArbitrarySection.scala b/src/main/scala/scalachecklib/ArbitrarySection.scala
index d43249a..c8d8ed2 100644
--- a/src/main/scala/scalachecklib/ArbitrarySection.scala
+++ b/src/main/scala/scalachecklib/ArbitrarySection.scala
@@ -3,7 +3,7 @@ package scalachecklib
import org.scalatest.Matchers
import org.scalatest.prop.Checkers
-/** ==The `arbitrary` Generator
+/** ==The `arbitrary` Generator==
*
* There is a special generator, `org.scalacheck.Arbitrary.arbitrary`, which generates arbitrary values of any
* supported type.
diff --git a/src/main/scala/scalachecklib/PropertiesSection.scala b/src/main/scala/scalachecklib/PropertiesSection.scala
index d0606e0..72efb1b 100644
--- a/src/main/scala/scalachecklib/PropertiesSection.scala
+++ b/src/main/scala/scalachecklib/PropertiesSection.scala
@@ -185,7 +185,7 @@ object PropertiesSection extends Checkers with Matchers with org.scalaexercises.
*
*/
def groupingProperties(res0: Int, res1: Int, res2: Int) = {
- import org.scalacheck.Properties
+ import org.scalacheck.{Prop, Properties}
class ZeroSpecification extends Properties("Zero") {
@@ -199,6 +199,6 @@ object PropertiesSection extends Checkers with Matchers with org.scalaexercises.
}
- check(new ZeroSpecification)
+ check(Prop.all(new ZeroSpecification().properties.map(_._2): _*))
}
}
\ No newline at end of file
diff --git a/src/main/scala/scalachecklib/ScalacheckDatetimeSection.scala b/src/main/scala/scalachecklib/ScalacheckDatetimeSection.scala
new file mode 100644
index 0000000..68b5057
--- /dev/null
+++ b/src/main/scala/scalachecklib/ScalacheckDatetimeSection.scala
@@ -0,0 +1,153 @@
+package scalachecklib
+
+import org.scalatest.Matchers
+import org.scalatest.prop.Checkers
+
+/** scalacheck-datetime is a library for helping use datetime libraries with ScalaCheck
+ *
+ * The motivation behind this library is to provide a simple, easy way to provide generated date and time instances
+ * that are useful to your own domain.
+ *
+ * For SBT, you can add the dependency to your project’s build file:
+ *
+ * {{{
+ * resolvers += Resolver.sonatypeRepo("releases")
+ *
+ * "com.fortysevendeg" %% "scalacheck-datetime" % "0.2.0" % "test"
+ * }}}
+ *
+ * Please, visit the [[https://47deg.github.io/scalacheck-datetime homepage]] for more information
+ *
+ * @param name scalacheck-datetime
+ */
+object ScalacheckDatetimeSection extends Checkers with Matchers with org.scalaexercises.definitions.Section {
+
+ /** ==Usage==
+ *
+ * To arbitrarily generate dates and times, you need to have the `Arbitrary` in scope for your date/time class.
+ * Assuming Joda Time:
+ */
+ def usage(res0: Boolean) = {
+
+ import com.fortysevendeg.scalacheck.datetime.joda.ArbitraryJoda._
+ import org.joda.time.DateTime
+ import org.scalacheck.Prop.forAll
+
+ check {
+ forAll { dt: DateTime =>
+ (dt.getDayOfMonth >= 1 && dt.getDayOfMonth <= 31) == res0
+ }
+ }
+ }
+
+ /** ==A note on imports==
+ *
+ * For all of the examples given in this document, you can substitute `jdk8` for `joda` and vice-versa,
+ * depending on which library you would like to generate instances for.
+ *
+ * ==Implementation==
+ *
+ * The infrastructure behind the generation of date/time instances for any given date/time library,
+ * which may take ranges into account, is done using a fairly simple typeclass, which has the type signature
+ * `ScalaCheckDateTimeInfra[D, R]`. That is to say, as long as there is an implicit `ScalaCheckDateTimeInfra`
+ * instance in scope for a given date/time type `D` (such as Joda’s `DateTime`) and a range type `R`
+ * (such as Joda’s `Period`), then the code will compile and be able to provide generated date/time instances.
+ *
+ * As stated, currently there are two instances, `ScalaCheckDateTimeInfra[DateTime, Period]` for Joda Time and
+ * `ScalaCheckDateTimeInfra[ZonedDateTime, Duration]` for Java SE 8’s Date and Time.
+ *
+ * ==Granularity==
+ *
+ * If you wish to restrict the precision of the generated instances, this library refers to that as granularity.
+ *
+ * You can constrain the granularity to:
+ *
+ *
+ * - Seconds
+ * - Minutes
+ * - Hours
+ * - Days
+ * - Years
+ *
+ *
+ * When a value is constrained, the time fields are set to zero, and the rest to the first day of the month,
+ * or day of the year. For example, if you constrain a field to be years, the generated instance will be midnight
+ * exactly, on the first day of January.
+ *
+ * To constrain a generated type, you simply need to provide an import for the typeclass for your date/time and
+ * range, and also an import for the granularity. As an example, this time using Java SE 8's `java.time` package:
+ */
+ def granularity(res0: Int, res1: Int, res2: Int, res3: Int, res4: Int) = {
+
+ import java.time._
+ import com.fortysevendeg.scalacheck.datetime.jdk8.ArbitraryJdk8._
+ import com.fortysevendeg.scalacheck.datetime.jdk8.granularity.years
+ import org.scalacheck.Prop.forAll
+
+ check {
+ forAll { zdt: ZonedDateTime =>
+ zdt.getMonth == Month.JANUARY
+ (zdt.getDayOfMonth == res0) &&
+ (zdt.getHour == res1) &&
+ (zdt.getMinute == res2) &&
+ (zdt.getSecond == res3) &&
+ (zdt.getNano == res4)
+ }
+ }
+
+ }
+
+ /** ==Creating Ranges==
+ *
+ * You can generate date/time instances only within a certain range, using the `genDateTimeWithinRange` in the
+ * `GenDateTime` class. The function takes two parameters, the date/time instances as a base from which to generate
+ * new date/time instances, and a range for the generated instances.
+ *
+ * If the range is positive, it will be in the future from the base date/time, negative in the past.
+ *
+ * Showing this usage with Joda Time:
+ */
+ def ranges(res0: Int) = {
+
+ import org.joda.time._
+ import com.fortysevendeg.scalacheck.datetime.instances.joda._
+ import com.fortysevendeg.scalacheck.datetime.GenDateTime.genDateTimeWithinRange
+ import org.scalacheck.Prop.forAll
+
+ val from = new DateTime(2016, 1, 1, 0, 0)
+ val range = Period.years(1)
+
+ check {
+ forAll(genDateTimeWithinRange(from, range)) { dt =>
+ dt.getYear == res0
+ }
+ }
+ }
+
+ /** ==Using Granularity and Ranges Together==
+ *
+ * As you would expect, it is possible to use the granularity and range concepts together.
+ * This example should not show anything surprising by now:
+ */
+ def granularityAndRanges(res0: Int, res1: Int, res2: Int, res3: Int, res4: Int) = {
+
+ import org.joda.time._
+ import com.fortysevendeg.scalacheck.datetime.instances.joda._
+ import com.fortysevendeg.scalacheck.datetime.GenDateTime.genDateTimeWithinRange
+ import com.fortysevendeg.scalacheck.datetime.joda.granularity.days
+ import org.scalacheck.Prop.forAll
+
+ val from = new DateTime(2016, 1, 1, 0, 0)
+ val range = Period.years(1)
+
+ check {
+ forAll(genDateTimeWithinRange(from, range)) { dt =>
+ (dt.getYear == res0) &&
+ (dt.getHourOfDay == res1) &&
+ (dt.getMinuteOfHour == res2) &&
+ (dt.getSecondOfMinute == res3) &&
+ (dt.getMillisOfSecond == res4)
+ }
+ }
+ }
+}
diff --git a/src/main/scala/scalachecklib/ScalacheckLibrary.scala b/src/main/scala/scalachecklib/ScalacheckLibrary.scala
index 2e6e4e9..1cd6bc9 100644
--- a/src/main/scala/scalachecklib/ScalacheckLibrary.scala
+++ b/src/main/scala/scalachecklib/ScalacheckLibrary.scala
@@ -12,7 +12,8 @@ object ScalacheckLibrary extends org.scalaexercises.definitions.Library {
override def sections = List(
PropertiesSection,
- GeneratorsSection
+ GeneratorsSection,
+ ScalacheckDatetimeSection
)
override def logoPath = "scalacheck"
diff --git a/src/test/scala/scalachecklib/ArbitrarySpec.scala b/src/test/scala/scalachecklib/ArbitrarySpec.scala
index 54d863e..063bc55 100644
--- a/src/test/scala/scalachecklib/ArbitrarySpec.scala
+++ b/src/test/scala/scalachecklib/ArbitrarySpec.scala
@@ -1,14 +1,13 @@
package scalachecklib
import org.scalacheck.Shapeless._
-import org.scalaexercises.Test
-import org.scalatest.Spec
+import org.scalatest.FunSuite
import org.scalatest.prop.Checkers
import shapeless.HNil
-class ArbitrarySpec extends Spec with Checkers {
+class ArbitrarySpec extends FunSuite with Checkers {
- def `implicit arbitrary char` = {
+ test("implicit arbitrary char") {
check(
Test.testSuccess(
@@ -18,7 +17,7 @@ class ArbitrarySpec extends Spec with Checkers {
)
}
- def `implicit arbitrary case class` = {
+ test("implicit arbitrary case class") {
check(
Test.testSuccess(
@@ -28,7 +27,7 @@ class ArbitrarySpec extends Spec with Checkers {
)
}
- def `arbitrary on gen` = {
+ test("arbitrary on gen") {
check(
Test.testSuccess(
diff --git a/src/test/scala/scalachecklib/GeneratorsSpec.scala b/src/test/scala/scalachecklib/GeneratorsSpec.scala
index 69de8c3..5871bad 100644
--- a/src/test/scala/scalachecklib/GeneratorsSpec.scala
+++ b/src/test/scala/scalachecklib/GeneratorsSpec.scala
@@ -1,14 +1,13 @@
package scalachecklib
import org.scalacheck.Shapeless._
-import org.scalaexercises.Test
-import org.scalatest.Spec
+import org.scalatest.FunSuite
import org.scalatest.prop.Checkers
import shapeless.HNil
-class GeneratorsSpec extends Spec with Checkers {
+class GeneratorsSpec extends FunSuite with Checkers {
- def `for-comprehension generator` = {
+ test("for-comprehension generator") {
check(
Test.testSuccess(
@@ -19,7 +18,7 @@ class GeneratorsSpec extends Spec with Checkers {
}
- def `oneOf method` = {
+ test("oneOf method") {
check(
Test.testSuccess(
@@ -30,7 +29,7 @@ class GeneratorsSpec extends Spec with Checkers {
}
- def `alphaChar, posNum and listOfN` = {
+ test("alphaChar, posNum and listOfN") {
check(
Test.testSuccess(
@@ -41,7 +40,7 @@ class GeneratorsSpec extends Spec with Checkers {
}
- def `suchThat condition` = {
+ test("suchThat condition") {
check(
Test.testSuccess(
@@ -52,7 +51,7 @@ class GeneratorsSpec extends Spec with Checkers {
}
- def `case class generator` = {
+ test("case class generator") {
check(
Test.testSuccess(
@@ -63,7 +62,7 @@ class GeneratorsSpec extends Spec with Checkers {
}
- def `sized generator` = {
+ test("sized generator") {
check(
Test.testSuccess(
@@ -74,7 +73,7 @@ class GeneratorsSpec extends Spec with Checkers {
}
- def `list container` = {
+ test("list container") {
check(
Test.testSuccess(
diff --git a/src/test/scala/scalachecklib/PropertiesSpec.scala b/src/test/scala/scalachecklib/PropertiesSpec.scala
index 9abc594..9dbe8b8 100644
--- a/src/test/scala/scalachecklib/PropertiesSpec.scala
+++ b/src/test/scala/scalachecklib/PropertiesSpec.scala
@@ -1,15 +1,13 @@
package scalachecklib
import org.scalacheck.Shapeless._
-import org.scalaexercises.Test
-import org.scalatest.Spec
+import org.scalatest.FunSuite
import org.scalatest.prop.Checkers
import shapeless.HNil
+class PropertiesSpec extends FunSuite with Checkers {
-class PropertiesSpec extends Spec with Checkers {
-
- def `always ends with the second string` = {
+ test("always ends with the second string") {
check(
Test.testSuccess(
@@ -19,7 +17,7 @@ class PropertiesSpec extends Spec with Checkers {
)
}
- def `all numbers are generated between the desired interval` = {
+ test("all numbers are generated between the desired interval") {
check(
Test.testSuccess(
@@ -29,7 +27,7 @@ class PropertiesSpec extends Spec with Checkers {
)
}
- def `all generated numbers are even` = {
+ test("all generated numbers are even") {
check(
Test.testSuccess(
@@ -39,7 +37,7 @@ class PropertiesSpec extends Spec with Checkers {
)
}
- def `only the second condition is true` = {
+ test("only the second condition is true") {
check(
Test.testSuccess(
@@ -49,7 +47,7 @@ class PropertiesSpec extends Spec with Checkers {
)
}
- def `the zero specification only works for 0` = {
+ test("the zero specification only works for 0") {
check(
Test.testSuccess(
diff --git a/src/test/scala/scalachecklib/ScalacheckDatetimeSpec.scala b/src/test/scala/scalachecklib/ScalacheckDatetimeSpec.scala
new file mode 100644
index 0000000..3410944
--- /dev/null
+++ b/src/test/scala/scalachecklib/ScalacheckDatetimeSpec.scala
@@ -0,0 +1,50 @@
+package scalachecklib
+
+import org.scalacheck.Shapeless._
+import org.scalatest.FunSuite
+import org.scalatest.prop.Checkers
+import shapeless.HNil
+
+class ScalacheckDatetimeSpec extends FunSuite with Checkers {
+
+ test("simple usage") {
+
+ check(
+ Test.testSuccess(
+ ScalacheckDatetimeSection.usage _,
+ true :: HNil
+ )
+ )
+ }
+
+ test("granularity") {
+
+ check(
+ Test.testSuccess(
+ ScalacheckDatetimeSection.granularity _,
+ 1 :: 0 :: 0 :: 0 :: 0 :: HNil
+ )
+ )
+ }
+
+ test("ranges") {
+
+ check(
+ Test.testSuccess(
+ ScalacheckDatetimeSection.ranges _,
+ 2016 :: HNil
+ )
+ )
+ }
+
+ test("granularity and ranges together") {
+
+ check(
+ Test.testSuccess(
+ ScalacheckDatetimeSection.granularityAndRanges _,
+ 2016 :: 0 :: 0 :: 0 :: 0 :: HNil
+ )
+ )
+ }
+
+}
diff --git a/src/test/scala/scalachecklib/Test.scala b/src/test/scala/scalachecklib/Test.scala
new file mode 100644
index 0000000..b176537
--- /dev/null
+++ b/src/test/scala/scalachecklib/Test.scala
@@ -0,0 +1,53 @@
+package scalachecklib
+
+import shapeless._
+import shapeless.ops.function._
+
+import cats.implicits._
+import org.scalacheck.{ Prop, Arbitrary }
+import org.scalacheck.Gen
+import Prop.forAll
+
+import org.scalatest.exceptions._
+
+import org.scalacheck.Shapeless._
+
+object Test {
+
+ def testSuccess[F, R, L <: HList](method: F, answer: L)(
+ implicit
+ A: Arbitrary[L],
+ fntop: FnToProduct.Aux[F, L ⇒ R]
+ ): Prop = {
+ val rightGen = genRightAnswer(answer)
+ val rightProp = forAll(rightGen)({ p ⇒
+
+ val result = Either.catchOnly[GeneratorDrivenPropertyCheckFailedException]({ fntop(method)(p) })
+ result match {
+ case Left(exc) ⇒ exc.cause match {
+ case Some(originalException) ⇒ throw originalException
+ case _ ⇒ false
+ }
+ case _ ⇒ true
+ }
+ })
+
+ val wrongGen = genWrongAnswer(answer)
+ val wrongProp = forAll(wrongGen)({ p ⇒
+ Either.catchNonFatal({ fntop(method)(p) }).isLeft
+ })
+
+ Prop.all(rightProp, wrongProp)
+ }
+
+ def genRightAnswer[L <: HList](answer: L): Gen[L] = {
+ Gen.const(answer)
+ }
+
+ def genWrongAnswer[L <: HList](l: L)(
+ implicit
+ A: Arbitrary[L]
+ ): Gen[L] = {
+ A.arbitrary.suchThat(_ != l)
+ }
+}