Skip to content

Commit

Permalink
Initial import
Browse files Browse the repository at this point in the history
Basic port of (Haskell) SmallCheck to Scala
with list and numeric examples
with SBT buildfile and README
  • Loading branch information
dwhjames committed Jun 3, 2012
0 parents commit 6bceb12
Show file tree
Hide file tree
Showing 9 changed files with 904 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitignore
@@ -0,0 +1,6 @@
/.cache
/.classpath
/.project
/.settings
/classes/
target/
44 changes: 44 additions & 0 deletions README.md
@@ -0,0 +1,44 @@
SmallCheck for Scala
====================

SmallCheck for Scala is a Scala library for property-based testing. It
is a port of the original Haskell library,
[SmallCheck](http://www.cs.york.ac.uk/fp/smallcheck).

> Following the lead of **QuickCheck** (Claessen and Hughes 2000), …
> **SmallCheck** also [uses] type-based generators to obtain test-sets
> of finite values for which properties are checked, and report any
> counter-examples found. But instead of using a sample of randomly
> generated values they test properties for all values up to some
> limiting depth, progressively increasing this limit.
(Runciman et al. 2008).

The original SmallCheck is available on
[Hackage](http://hackage.haskell.org/package/smallcheck)
and is maintained by
[Roman Cheplyaka](http://github.com/feuerbach)
at
[http://github.com/feuerbach/smallcheck](http://github.com/feuerbach/smallcheck).

The original property-based testing library for Haskell,
[QuickCheck](http://hackage.haskell.org/package/QuickCheck)
(Claessen and Hughes 2000), has also spawned a port written in Scala:
[Rickard Nilsson](http://github.com/rickynils)’s
[ScalaCheck](https://github.com/rickynils/scalacheck). SmallCheck for Scala
has followed the design of ScalaCheck.

References
----------

Koen Claessen and John Hughes.
QuickCheck: a lightweight tool for random testing of Haskell programs.
In _Proceedings of the fifth ACM SIGPLAN international conference on Functional programming_,
ICFP ’00, pages 268–279. ACM, 2000. doi:
[10.1145/351240.351266](http://doi.acm.org/10.1145/351240.351266)

Colin Runciman, Matthew Naylor, and Fredrik Lindblad.
SmallCheck and Lazy SmallCheck: automatic exhaustive testing for small values.
In _Proceedings of the first ACM SIGPLAN symposium on Haskell_,
Haskell ’08, pages 37–48. ACM, 2008. doi:
[10.1145/1411286.1411292](http://doi.acm.org/10.1145/1411286.1411292)
5 changes: 5 additions & 0 deletions build.sbt
@@ -0,0 +1,5 @@
name := "smallcheck4scala"

version := ".1"

scalaVersion := "2.9.2"
88 changes: 88 additions & 0 deletions src/main/scala/smallcheck/Drivers.scala
@@ -0,0 +1,88 @@
package smallcheck

/**
* Methods to invoke SmallCheck
*/
object Drivers {
import Property.{TestCase, Pass, Fail, Inappropriate}

/** Run SmallCheck on a property from depth 0 to a given depth d */
def smallCheck(d: Int, p: Property) = iterCheck(0, Some(d), p)

/** Run SmallCheck on collection of properties from depth 0 to a given depth d */
def smallCheck(d: Int, p: Properties) = iterCheck(0, Some(d), p)

/** Run SmallCheck on a property at a given depth d */
def depthCheck(d: Int, p: Property) = iterCheck(d, Some(d), p)

/** Run SmallCheck on collection of properties at a given depth d */
def depthCheck(d: Int, p: Properties) = iterCheck(d, Some(d), p)

/** Run SmallCheck on a property interactively */
def smallCheckI(p: Property) = iterCheck(0, None, p)

/** Run SmallCheck on collection of properties */
def smallCheckI(p: Properties) = iterCheck(0, None, p)

private def iterCheck(dFrom: Int, dToOption: Option[Int], ps: Properties) {
def iter(d: Int) {
println("Depth "+d+":")
var ok = true
for ((name, p) <- ps.properties) {
println(name)
ok = ok & check(dToOption.isEmpty, 0, 0, true, p(d))
}
dToOption match {
case None =>
if (whenUserWishes(" Deeper")) iter(d+1)
case Some(dTo) =>
if (ok && d < dTo) iter(d+1)
}
}
iter(dFrom)
}

private def iterCheck(dFrom: Int, dToOption: Option[Int], p: Property) {
def iter(d: Int) {
println("Depth "+d+":")
val ok = check(dToOption.isEmpty, 0, 0, true, p(d))
dToOption match {
case None =>
if (whenUserWishes(" Deeper")) iter(d+1)
case Some(dTo) =>
if (ok && d < dTo) iter(d+1)
}
}
iter(dFrom)
}

private def check(interactive: Boolean, numTests: Int, numIgnored: Int, ok: Boolean, rs: Seq[TestCase]): Boolean = {
if (rs.isEmpty) {
print(" Completed "+numTests+" test(s)")
println(if (ok) " without failure." else ".")
if (numIgnored > 0)
println(" But "+numIgnored+" did not meet ==> condition.")
ok
} else {
rs.head match {
case TestCase(Inappropriate, _) =>
check(interactive, numTests+1, numIgnored+1, ok, rs.tail)
case TestCase(Pass, _) =>
check(interactive, numTests+1, numIgnored, ok, rs.tail)
case TestCase(Fail, args) =>
println(" Failed test no. "+(numTests+1)+". Test values follow.")
args foreach { arg => println(" " + arg) }
if (interactive && whenUserWishes(" Continue"))
check(interactive, numTests+1, numIgnored, false, rs.tail)
else
false
}
}
}

private def whenUserWishes(wish: String): Boolean = {
print(wish+"? ")
val reply = readLine()
reply.isEmpty() || reply == "y"
}
}
25 changes: 25 additions & 0 deletions src/main/scala/smallcheck/Properties.scala
@@ -0,0 +1,25 @@
package smallcheck
import scala.collection.mutable.ListBuffer

/**
* A collection of properties, which itself is a property.
*/
class Properties(val name: String) extends Property {
import Property._

private val props = new ListBuffer[(String, Property)]

private def oneProperty: Property = all((props.map(_._2)):_*)

def apply(d: Int): Seq[TestCase] = oneProperty(d)

def properties: Seq[(String, Property)] = props

def include(ps: Properties) = for ((n, p) <- ps.properties) property(n) = p

class PropertySpecifier() {
def update(propName: String, p: Property) = props += ((name+"."+propName, p))
}

lazy val property = new PropertySpecifier()
}

0 comments on commit 6bceb12

Please sign in to comment.