Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Basic port of (Haskell) SmallCheck to Scala with list and numeric examples with SBT buildfile and README
- Loading branch information
0 parents
commit 6bceb12
Showing
9 changed files
with
904 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/.cache | ||
/.classpath | ||
/.project | ||
/.settings | ||
/classes/ | ||
target/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
name := "smallcheck4scala" | ||
|
||
version := ".1" | ||
|
||
scalaVersion := "2.9.2" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() | ||
} |
Oops, something went wrong.