diff options
20 files changed, 2960 insertions, 38 deletions
@@ -133,7 +133,7 @@ END-USER TARGETS description="Requires forkjoin library to be rebuilt. Add this target before any other if class file format is incompatible."> <property name="forkjoin.outdated" value="yes"/> </target> - + <!-- =========================================================================== PROPERTIES ============================================================================ --> @@ -451,7 +451,6 @@ QUICK BUILD (QUICK) <include name="library/**"/> <include name="dbc/**"/> <include name="actors/**"/> - <!--<include name="parallel-collections/**"/>--> <include name="continuations/**"/> <include name="swing/**"/> </srcfiles> @@ -496,18 +495,6 @@ QUICK BUILD (QUICK) <include name="**/*.scala"/> <compilationpath refid="quick.compilation.path"/> </scalacfork> - <!--<scalacfork - destdir="${build-quick.dir}/classes/library" - compilerpathref="locker.classpath" - params="${scalac.args.quick}" - srcdir="${src.dir}/parallel-collections" - jvmargs="${scalacfork.jvmargs}"> - <include name="**/*.scala"/> - <compilationpath> - <pathelement location="${build-quick.dir}/classes/library"/> - <pathelement location="${lib.dir}/forkjoin.jar"/> - </compilationpath> - </scalacfork>--> <scalacfork destdir="${build-quick.dir}/classes/library" compilerpathref="locker.classpath" @@ -652,8 +639,23 @@ QUICK BUILD (QUICK) <touch file="${build-quick.dir}/plugins.complete" verbose="no"/> <stopwatch name="quick.plugins.timer" action="total"/> </target> - - <target name="quick.pre-scalap" depends="quick.plugins"> + + <target name="quick.scalacheck" depends="quick.plugins"> + <mkdir dir="${build-quick.dir}/classes/scalacheck"/> + <scalacfork + destdir="${build-quick.dir}/classes/scalacheck" + compilerpathref="locker.classpath" + params="${scalac.args.all}" + srcdir="${src.dir}/scalacheck" + jvmargs="${scalacfork.jvmargs}"> + <include name="**/*.scala"/> + <compilationpath> + <pathelement location="${build-quick.dir}/classes/library"/> + </compilationpath> + </scalacfork> + </target> + + <target name="quick.pre-scalap" depends="quick.scalacheck"> <uptodate property="quick.scalap.available" targetfile="${build-quick.dir}/scalap.complete"> <srcfiles dir="${src.dir}/scalap"/> </uptodate> @@ -784,7 +786,7 @@ QUICK BUILD (QUICK) <chmod perm="ugo+rx" file="${build-quick.dir}/bin/scalap"/> <touch file="${build-quick.dir}/bin.complete" verbose="no"/> </target> - + <target name="quick.done" depends="quick.bin"> <path id="quick.classpath"> <pathelement location="${build-quick.dir}/classes/library"/> @@ -836,7 +838,7 @@ PACKED QUICK BUILD (PACK) </fileset> </jar> </target> - + <target name="pack.pre-comp" depends="pack.lib"> <uptodate property="pack.comp.available" @@ -874,7 +876,13 @@ PACKED QUICK BUILD (PACK) </jar> </target> - <target name="pack.pre-partest" depends="pack.plugins"> + <target name="pack.scalacheck" depends="pack.plugins"> + <jar destfile="${build-pack.dir}/lib/scalacheck.jar"> + <fileset dir="${build-quick.dir}/classes/scalacheck"/> + </jar> + </target> + + <target name="pack.pre-partest" depends="pack.scalacheck"> <uptodate property="pack.partest.available" targetfile="${build-pack.dir}/lib/scala-partest.jar" @@ -1019,18 +1027,6 @@ BOOTSTRAPPING BUILD (STRAP) <include name="**/*.scala"/> <compilationpath refid="strap.compilation.path"/> </scalacfork> - <!--<scalacfork - destdir="${build-strap.dir}/classes/library" - compilerpathref="pack.classpath" - params="${scalac.args.all}" - srcdir="${src.dir}/parallel-collections" - jvmargs="${scalacfork.jvmargs}"> - <include name="**/*.scala"/> - <compilationpath> - <pathelement location="${build-strap.dir}/classes/library"/> - <pathelement location="${forkjoin.jar}"/> - </compilationpath> - </scalacfork>--> <scalacfork destdir="${build-strap.dir}/classes/library" compilerpathref="pack.classpath" @@ -1286,7 +1282,7 @@ LIBRARIES (MSIL, FJBG maybe later) <fileset dir="${build-libs.dir}/classes/forkjoin"/> </jar> </target> - + <target name="libs.pre-msil" depends="libs.start"> <uptodate property="libs.msil.available" targetfile="${build-libs.dir}/msil.complete"> <srcfiles dir="${src.dir}/msil"> @@ -1367,7 +1363,7 @@ LIBRARIES (MSIL, FJBG maybe later) <target name="libs.done" depends="libs.msilpack, libs.fjbgpack"/> <target name="forkjoin.done" depends="libs.forkjoinpack"/> - + <target name="libs.clean" depends="pack.clean"> <delete dir="${build-libs.dir}" includeemptydirs="yes" quiet="yes" failonerror="no"/> </target> @@ -1384,7 +1380,6 @@ DOCUMENTATION <include name="library/**"/> <include name="dbc/**"/> <include name="actors/**"/> - <!--<include name="parallel-collections/**"/>--> <include name="swing/**"/> </srcfiles> </uptodate> @@ -1402,7 +1397,6 @@ DOCUMENTATION classpathref="pack.classpath"> <src> <files includes="${src.dir}/actors"/> - <!--<files includes="${src.dir}/parallel-collections"/>--> <files includes="${src.dir}/library/scala"/> <files includes="${src.dir}/swing"/> <files includes="${src.dir}/continuations/library"/> @@ -1641,7 +1635,6 @@ DISTRIBUTION <jar destfile="${dist.dir}/src/scala-library-src.jar"> <fileset dir="${src.dir}/library"/> <fileset dir="${src.dir}/actors"/> - <!--<fileset dir="${src.dir}/parallel-collections"/>--> <fileset dir="${src.dir}/continuations/library"/> </jar> <jar destfile="${dist.dir}/src/scala-dbc-src.jar"> @@ -1730,7 +1723,6 @@ STABLE REFERENCE (STARR) <jar destfile="${basedir}/lib/scala-library-src.jar"> <fileset dir="${basedir}/src/library"/> <fileset dir="${basedir}/src/actors"/> - <!--<fileset dir="${basedir}/src/parallel-collections"/>--> <fileset dir="${basedir}/src/swing"/> <fileset dir="${basedir}/src/dbc"/> </jar> diff --git a/src/scalacheck/org/scalacheck/Arbitrary.scala b/src/scalacheck/org/scalacheck/Arbitrary.scala new file mode 100644 index 0000000000..14d2b9b924 --- /dev/null +++ b/src/scalacheck/org/scalacheck/Arbitrary.scala @@ -0,0 +1,407 @@ +/*-------------------------------------------------------------------------*\ +** ScalaCheck ** +** Copyright (c) 2007-2010 Rickard Nilsson. All rights reserved. ** +** http://www.scalacheck.org ** +** ** +** This software is released under the terms of the Revised BSD License. ** +** There is NO WARRANTY. See the file LICENSE for the full text. ** +\*-------------------------------------------------------------------------*/ + +package org.scalacheck + +import util.{FreqMap,Buildable} + +sealed abstract class Arbitrary[T] { + val arbitrary: Gen[T] +} + +/** Defines implicit <code>Arbitrary</code> instances for common types. + * <p> + * ScalaCheck + * uses implicit <code>Arbitrary</code> instances when creating properties + * out of functions with the <code>Prop.property</code> method, and when + * the <code>Arbitrary.arbitrary</code> method is used. For example, the + * following code requires that there exists an implicit + * <code>Arbitrary[MyClass]</code> instance: + * </p> + * + * <p> + * <code> + * val myProp = Prop.forAll { myClass: MyClass =><br /> + * ...<br /> + * }<br /> + * + * val myGen = Arbitrary.arbitrary[MyClass] + * </code> + * </p> + * + * <p> + * The required implicit definition could look like this: + * </p> + * + * <p> + * <code> + * implicit val arbMyClass: Arbitrary[MyClass] = Arbitrary(...) + * </code> + * </p> + * + * <p> + * The factory method <code>Arbitrary(...)</code> takes a generator of type + * <code>Gen[T]</code> and returns an instance of <code>Arbitrary[T]</code>. + * </p> + * + * <p> + * The <code>Arbitrary</code> module defines implicit <code>Arbitrary</code> + * instances for common types, for convenient use in your properties and + * generators. + * </p> + */ +object Arbitrary { + + import Gen.{value, choose, sized, listOf, listOf1, + frequency, oneOf, containerOf, resize} + import util.StdRand + import scala.collection.{immutable, mutable} + import java.util.Date + + /** Creates an Arbitrary instance */ + def apply[T](g: => Gen[T]): Arbitrary[T] = new Arbitrary[T] { + lazy val arbitrary = g + } + + /** Returns an arbitrary generator for the type T. */ + def arbitrary[T](implicit a: Arbitrary[T]): Gen[T] = a.arbitrary + + /**** Arbitrary instances for each AnyVal ****/ + + /** Arbitrary AnyVal */ + implicit lazy val arbAnyVal: Arbitrary[AnyVal] = Arbitrary(oneOf( + arbitrary[Unit], arbitrary[Boolean], arbitrary[Char], arbitrary[Byte], + arbitrary[Short], arbitrary[Int], arbitrary[Long], arbitrary[Float], + arbitrary[Double] + )) + + /** Arbitrary instance of Boolean */ + implicit lazy val arbBool: Arbitrary[Boolean] = + Arbitrary(oneOf(true, false)) + + /** Arbitrary instance of Int */ + implicit lazy val arbInt: Arbitrary[Int] = Arbitrary( + Gen.chooseNum(Int.MinValue, Int.MaxValue) + ) + + /** Arbitrary instance of Long */ + implicit lazy val arbLong: Arbitrary[Long] = Arbitrary( + Gen.chooseNum(Long.MinValue / 2, Long.MaxValue / 2) + ) + + /** Arbitrary instance of Float */ + implicit lazy val arbFloat: Arbitrary[Float] = Arbitrary( + Gen.chooseNum( + Float.MinValue, Float.MaxValue + // I find that including these by default is a little TOO testy. + // Float.Epsilon, Float.NaN, Float.PositiveInfinity, Float.NegativeInfinity + ) + ) + + /** Arbitrary instance of Double */ + implicit lazy val arbDouble: Arbitrary[Double] = Arbitrary( + Gen.chooseNum( + Double.MinValue / 2, Double.MaxValue / 2 + // As above. Perhaps behind some option? + // Double.Epsilon, Double.NaN, Double.PositiveInfinity, Double.NegativeInfinity + ) + ) + + /** Arbitrary instance of Char */ + implicit lazy val arbChar: Arbitrary[Char] = Arbitrary( + Gen.choose(Char.MinValue, Char.MaxValue) + ) + + /** Arbitrary instance of Byte */ + implicit lazy val arbByte: Arbitrary[Byte] = Arbitrary( + Gen.chooseNum(Byte.MinValue, Byte.MaxValue) + ) + + /** Arbitrary instance of Short */ + implicit lazy val arbShort: Arbitrary[Short] = Arbitrary( + Gen.chooseNum(Short.MinValue, Short.MaxValue) + ) + + /** Absolutely, totally, 100% arbitrarily chosen Unit. */ + implicit lazy val arbUnit: Arbitrary[Unit] = Arbitrary(value(())) + + /**** Arbitrary instances of other common types ****/ + + /** Arbitrary instance of String */ + implicit lazy val arbString: Arbitrary[String] = + Arbitrary(arbitrary[List[Char]] map (_.mkString)) + + /** Arbitrary instance of Date */ + implicit lazy val arbDate: Arbitrary[Date] = Arbitrary(for { + l <- arbitrary[Long] + d = new Date + } yield new Date(d.getTime + l)) + + /** Arbitrary instance of Throwable */ + implicit lazy val arbThrowable: Arbitrary[Throwable] = + Arbitrary(value(new Exception)) + + /** Arbitrary BigInt */ + implicit lazy val arbBigInt: Arbitrary[BigInt] = { + def chooseBigInt: Gen[BigInt] = sized((s: Int) => choose(-s, s)) map (x => BigInt(x)) + def chooseReallyBigInt = chooseBigInt.combine(choose(32, 128))((x, y) => Some(x.get << y.get)) + + Arbitrary( + frequency( + (5, chooseBigInt), + (10, chooseReallyBigInt), + (1, BigInt(0)), + (1, BigInt(1)), + (1, BigInt(-1)), + (1, BigInt(Int.MaxValue) + 1), + (1, BigInt(Int.MinValue) - 1), + (1, BigInt(Long.MaxValue)), + (1, BigInt(Long.MinValue)), + (1, BigInt(Long.MaxValue) + 1), + (1, BigInt(Long.MinValue) - 1) + ) + ) + } + + /** Arbitrary BigDecimal */ + implicit lazy val arbBigDecimal: Arbitrary[BigDecimal] = { + import java.math.MathContext._ + val mcGen = oneOf(UNLIMITED, DECIMAL32, DECIMAL64, DECIMAL128) + val bdGen = for { + mc <- mcGen + scale <- arbInt.arbitrary + x <- arbBigInt.arbitrary + } yield BigDecimal(x, scale, mc) + Arbitrary(bdGen) + } + + /** Arbitrary java.lang.Number */ + implicit lazy val arbNumber: Arbitrary[Number] = { + val gen = Gen.oneOf( + arbitrary[Byte], arbitrary[Short], arbitrary[Int], arbitrary[Long], + arbitrary[Float], arbitrary[Double] + ) + Arbitrary(gen map (_.asInstanceOf[Number])) + // XXX TODO - restore BigInt and BigDecimal + // Arbitrary(oneOf(arbBigInt.arbitrary :: (arbs map (_.arbitrary) map toNumber) : _*)) + } + + /** Generates an arbitrary property */ + implicit lazy val arbProp: Arbitrary[Prop] = + Arbitrary(frequency( + (5, Prop.proved), + (4, Prop.falsified), + (2, Prop.undecided), + (1, Prop.exception(null)) + )) + + /** Arbitrary instance of test params */ + implicit lazy val arbTestParams: Arbitrary[Test.Params] = + Arbitrary(for { + minSuccTests <- choose(10,150) + maxDiscTests <- choose(100,500) + minSize <- choose(0,500) + sizeDiff <- choose(0,500) + maxSize <- choose(minSize, minSize + sizeDiff) + } yield Test.Params(minSuccTests,maxDiscTests,minSize,maxSize)) + + /** Arbitrary instance of gen params */ + implicit lazy val arbGenParams: Arbitrary[Gen.Params] = + Arbitrary(for { + size <- arbitrary[Int] suchThat (_ >= 0) + } yield Gen.Params(size, StdRand)) + + /** Arbitrary instance of prop params */ + implicit lazy val arbPropParams: Arbitrary[Prop.Params] = + Arbitrary(for { + genPrms <- arbitrary[Gen.Params] + } yield Prop.Params(genPrms, FreqMap.empty[immutable.Set[Any]])) + + + // Higher-order types // + + /** Arbitrary instance of Gen */ + implicit def arbGen[T](implicit a: Arbitrary[T]): Arbitrary[Gen[T]] = + Arbitrary(frequency( + (5, arbitrary[T] map (value(_))), + (1, Gen.fail) + )) + + /** Arbitrary instance of option type */ + implicit def arbOption[T](implicit a: Arbitrary[T]): Arbitrary[Option[T]] = + Arbitrary(sized(n => if(n == 0) value(None) else resize(n - 1, arbitrary[T]).map(Some(_)))) + + implicit def arbEither[T, U](implicit at: Arbitrary[T], au: Arbitrary[U]): Arbitrary[Either[T, U]] = + Arbitrary(oneOf(arbitrary[T].map(Left(_)), arbitrary[U].map(Right(_)))) + + /** Arbitrary instance of immutable map */ + implicit def arbImmutableMap[T,U](implicit at: Arbitrary[T], au: Arbitrary[U] + ): Arbitrary[immutable.Map[T,U]] = Arbitrary( + for(seq <- arbitrary[Stream[(T,U)]]) yield immutable.Map(seq: _*) + ) + + /** Arbitrary instance of mutable map */ + implicit def arbMutableMap[T,U](implicit at: Arbitrary[T], au: Arbitrary[U] + ): Arbitrary[mutable.Map[T,U]] = Arbitrary( + for(seq <- arbitrary[Stream[(T,U)]]) yield mutable.Map(seq: _*) + ) + + /** Arbitrary instance of any buildable container (such as lists, arrays, + * streams, etc). The maximum size of the container depends on the size + * generation parameter. */ + implicit def arbContainer[C[_],T](implicit a: Arbitrary[T], b: Buildable[T,C] + ): Arbitrary[C[T]] = Arbitrary(containerOf[C,T](arbitrary[T])) + + /** Arbitrary instance of any array. */ + implicit def arbArray[T](implicit a: Arbitrary[T], c: ClassManifest[T] + ): Arbitrary[Array[T]] = Arbitrary(containerOf[Array,T](arbitrary[T])) + + + // Functions // + + /** Arbitrary instance of Function1 */ + implicit def arbFunction1[T1,R](implicit a: Arbitrary[R] + ): Arbitrary[T1 => R] = Arbitrary( + for(r <- arbitrary[R]) yield (t1: T1) => r + ) + + /** Arbitrary instance of Function2 */ + implicit def arbFunction2[T1,T2,R](implicit a: Arbitrary[R] + ): Arbitrary[(T1,T2) => R] = Arbitrary( + for(r <- arbitrary[R]) yield (t1: T1, t2: T2) => r + ) + + /** Arbitrary instance of Function3 */ + implicit def arbFunction3[T1,T2,T3,R](implicit a: Arbitrary[R] + ): Arbitrary[(T1,T2,T3) => R] = Arbitrary( + for(r <- arbitrary[R]) yield (t1: T1, t2: T2, t3: T3) => r + ) + + /** Arbitrary instance of Function4 */ + implicit def arbFunction4[T1,T2,T3,T4,R](implicit a: Arbitrary[R] + ): Arbitrary[(T1,T2,T3,T4) => R] = Arbitrary( + for(r <- arbitrary[R]) yield (t1: T1, t2: T2, t3: T3, t4: T4) => r + ) + + /** Arbitrary instance of Function5 */ + implicit def arbFunction5[T1,T2,T3,T4,T5,R](implicit a: Arbitrary[R] + ): Arbitrary[(T1,T2,T3,T4,T5) => R] = Arbitrary( + for(r <- arbitrary[R]) yield (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5) => r + ) + + + // Tuples // + + /** Arbitrary instance of 2-tuple */ + implicit def arbTuple2[T1,T2](implicit + a1: Arbitrary[T1], a2: Arbitrary[T2] + ): Arbitrary[(T1,T2)] = + Arbitrary(for { + t1 <- arbitrary[T1] + t2 <- arbitrary[T2] + } yield (t1,t2)) + + /** Arbitrary instance of 3-tuple */ + implicit def arbTuple3[T1,T2,T3](implicit + a1: Arbitrary[T1], a2: Arbitrary[T2], a3: Arbitrary[T3] + ): Arbitrary[(T1,T2,T3)] = + Arbitrary(for { + t1 <- arbitrary[T1] + t2 <- arbitrary[T2] + t3 <- arbitrary[T3] + } yield (t1,t2,t3)) + + /** Arbitrary instance of 4-tuple */ + implicit def arbTuple4[T1,T2,T3,T4](implicit + a1: Arbitrary[T1], a2: Arbitrary[T2], a3: Arbitrary[T3], a4: Arbitrary[T4] + ): Arbitrary[(T1,T2,T3,T4)] = + Arbitrary(for { + t1 <- arbitrary[T1] + t2 <- arbitrary[T2] + t3 <- arbitrary[T3] + t4 <- arbitrary[T4] + } yield (t1,t2,t3,t4)) + + /** Arbitrary instance of 5-tuple */ + implicit def arbTuple5[T1,T2,T3,T4,T5](implicit + a1: Arbitrary[T1], a2: Arbitrary[T2], a3: Arbitrary[T3], a4: Arbitrary[T4], + a5: Arbitrary[T5] + ): Arbitrary[(T1,T2,T3,T4,T5)] = + Arbitrary(for { + t1 <- arbitrary[T1] + t2 <- arbitrary[T2] + t3 <- arbitrary[T3] + t4 <- arbitrary[T4] + t5 <- arbitrary[T5] + } yield (t1,t2,t3,t4,t5)) + + /** Arbitrary instance of 6-tuple */ + implicit def arbTuple6[T1,T2,T3,T4,T5,T6](implicit + a1: Arbitrary[T1], a2: Arbitrary[T2], a3: Arbitrary[T3], a4: Arbitrary[T4], + a5: Arbitrary[T5], a6: Arbitrary[T6] + ): Arbitrary[(T1,T2,T3,T4,T5,T6)] = + Arbitrary(for { + t1 <- arbitrary[T1] + t2 <- arbitrary[T2] + t3 <- arbitrary[T3] + t4 <- arbitrary[T4] + t5 <- arbitrary[T5] + t6 <- arbitrary[T6] + } yield (t1,t2,t3,t4,t5,t6)) + + /** Arbitrary instance of 7-tuple */ + implicit def arbTuple7[T1,T2,T3,T4,T5,T6,T7](implicit + a1: Arbitrary[T1], a2: Arbitrary[T2], a3: Arbitrary[T3], a4: Arbitrary[T4], + a5: Arbitrary[T5], a6: Arbitrary[T6], a7: Arbitrary[T7] + ): Arbitrary[(T1,T2,T3,T4,T5,T6,T7)] = + Arbitrary(for { + t1 <- arbitrary[T1] + t2 <- arbitrary[T2] + t3 <- arbitrary[T3] + t4 <- arbitrary[T4] + t5 <- arbitrary[T5] + t6 <- arbitrary[T6] + t7 <- arbitrary[T7] + } yield (t1,t2,t3,t4,t5,t6,t7)) + + /** Arbitrary instance of 8-tuple */ + implicit def arbTuple8[T1,T2,T3,T4,T5,T6,T7,T8](implicit + a1: Arbitrary[T1], a2: Arbitrary[T2], a3: Arbitrary[T3], a4: Arbitrary[T4], + a5: Arbitrary[T5], a6: Arbitrary[T6], a7: Arbitrary[T7], a8: Arbitrary[T8] + ): Arbitrary[(T1,T2,T3,T4,T5,T6,T7,T8)] = + Arbitrary(for { + t1 <- arbitrary[T1] + t2 <- arbitrary[T2] + t3 <- arbitrary[T3] + t4 <- arbitrary[T4] + t5 <- arbitrary[T5] + t6 <- arbitrary[T6] + t7 <- arbitrary[T7] + t8 <- arbitrary[T8] + } yield (t1,t2,t3,t4,t5,t6,t7,t8)) + + /** Arbitrary instance of 9-tuple */ + implicit def arbTuple9[T1,T2,T3,T4,T5,T6,T7,T8,T9](implicit + a1: Arbitrary[T1], a2: Arbitrary[T2], a3: Arbitrary[T3], a4: Arbitrary[T4], + a5: Arbitrary[T5], a6: Arbitrary[T6], a7: Arbitrary[T7], a8: Arbitrary[T8], + a9: Arbitrary[T9] + ): Arbitrary[(T1,T2,T3,T4,T5,T6,T7,T8,T9)] = + Arbitrary(for { + t1 <- arbitrary[T1] + t2 <- arbitrary[T2] + t3 <- arbitrary[T3] + t4 <- arbitrary[T4] + t5 <- arbitrary[T5] + t6 <- arbitrary[T6] + t7 <- arbitrary[T7] + t8 <- arbitrary[T8] + t9 <- arbitrary[T9] + } yield (t1,t2,t3,t4,t5,t6,t7,t8,t9)) + +} diff --git a/src/scalacheck/org/scalacheck/Arg.scala b/src/scalacheck/org/scalacheck/Arg.scala new file mode 100644 index 0000000000..99657db29b --- /dev/null +++ b/src/scalacheck/org/scalacheck/Arg.scala @@ -0,0 +1,20 @@ +/*-------------------------------------------------------------------------*\ +** ScalaCheck ** +** Copyright (c) 2007-2010 Rickard Nilsson. All rights reserved. ** +** http://www.scalacheck.org ** +** ** +** This software is released under the terms of the Revised BSD License. ** +** There is NO WARRANTY. See the file LICENSE for the full text. ** +\*-------------------------------------------------------------------------*/ + +package org.scalacheck + +case class Arg[+T]( + label: String, + arg: T, + shrinks: Int, + origArg: T +)(implicit prettyPrinter: T => Pretty) { + lazy val prettyArg: Pretty = prettyPrinter(arg) + lazy val prettyOrigArg: Pretty = prettyPrinter(origArg) +} diff --git a/src/scalacheck/org/scalacheck/Commands.scala b/src/scalacheck/org/scalacheck/Commands.scala new file mode 100644 index 0000000000..0d16505d96 --- /dev/null +++ b/src/scalacheck/org/scalacheck/Commands.scala @@ -0,0 +1,171 @@ +/*-------------------------------------------------------------------------*\ +** ScalaCheck ** +** Copyright (c) 2007-2010 Rickard Nilsson. All rights reserved. ** +** http://www.scalacheck.org ** +** ** +** This software is released under the terms of the Revised BSD License. ** +** There is NO WARRANTY. See the file LICENSE for the full text. ** +\*-------------------------------------------------------------------------*/ + +package org.scalacheck + +import Gen._ +import Prop._ +import Shrink._ + +/** See User Guide for usage examples */ +trait Commands extends Prop { + + /** The abstract state data type. This type must be immutable. + * The state type that encodes the abstract state. The abstract state + * should model all the features we need from the real state, the system + * under test. We should leave out all details that aren't needed for + * specifying our pre- and postconditions. The state type must be called + * State and be immutable. */ + type State <: AnyRef + + class Binding(private val key: State) { + def get: Any = bindings.find(_._1 eq key) match { + case None => error("No value bound") + case Some(x) => x + } + } + + /** Abstract commands are defined as subtypes of the traits Command or SetCommand. + * Each command must have a run method and a method that returns the new abstract + * state, as it should look after the command has been run. + * A command can also define a precondition that states how the current + * abstract state must look if the command should be allowed to run. + * Finally, we can also define a postcondition which verifies that the + * system under test is in a correct state after the command exectution. */ + trait Command { + + /** Used internally. */ + protected[Commands] def run_(s: State) = run(s) + + def run(s: State): Any + def nextState(s: State): State + + /** @deprecated Use <code>preConditions += ...</code> instead. */ + @deprecated("Use 'preConditions += ...' instead.") + def preCondition_=(f: State => Boolean) = { + preConditions.clear + preConditions += f + } + + /** Returns all preconditions merged into a single function */ + def preCondition: (State => Boolean) = s => preConditions.toList.forall(_.apply(s)) + + /** A precondition is a function that + * takes the current abstract state as parameter and returns a boolean + * that says if the precondition is fulfilled or not. You can add several + * conditions to the precondition list */ + val preConditions = new collection.mutable.ListBuffer[State => Boolean] + + /** @deprecated Use <code>postConditions += ...</code> instead. */ + @deprecated("Use 'postConditions += ...' instead.") + def postCondition_=(f: (State,Any) => Prop) = { + postConditions.clear + postConditions += ((s0,s1,r) => f(s0,r)) + } + + /** @deprecated Use <code>postConditions += ...</code> instead. */ + @deprecated("Use 'postConditions += ...' instead.") + def postCondition_=(f: (State,State,Any) => Prop) = { + postConditions.clear + postConditions += f + } + + /** Returns all postconditions merged into a single function */ + def postCondition: (State,State,Any) => Prop = (s0,s1,r) => all(postConditions.map(_.apply(s0,s1,r)): _*) + + /** A postcondition is a function that + * takes three parameters, s0, s1 and r. s0 is the abstract state before + * the command was run, s1 is the abstract state after the command was + * run, and r is the result from the command's run + * method. The postcondition function should return a Boolean (or + * a Prop instance) that says if the condition holds or not. You can add several + * conditions to the postConditions list. */ + val postConditions = new collection.mutable.ListBuffer[(State,State,Any) => Prop] + } + + /** A command that binds its result for later use */ + trait SetCommand extends Command { + /** Used internally. */ + protected[Commands] final override def run_(s: State) = { + val r = run(s) + bindings += ((s,r)) + r + } + + final def nextState(s: State) = nextState(s, new Binding(s)) + def nextState(s: State, b: Binding): State + } + + private case class Cmds(cs: List[Command], ss: List[State]) { + override def toString = cs.map(_.toString).mkString(", ") + } + + private val bindings = new scala.collection.mutable.ListBuffer[(State,Any)] + + private def initState() = { + bindings.clear() + initialState() + } + + private def genCmds: Gen[Cmds] = { + def sizedCmds(s: State)(sz: Int): Gen[Cmds] = + if(sz <= 0) value(Cmds(Nil, Nil)) else for { + c <- genCommand(s) suchThat (_.preCondition(s)) + Cmds(cs,ss) <- sizedCmds(c.nextState(s))(sz-1) + } yield Cmds(c::cs, s::ss) + + for { + s0 <- wrap(value(initialState())) + cmds <- sized(sizedCmds(s0)) + } yield cmds + } + + private def validCmds(s: State, cs: List[Command]): Option[Cmds] = + cs match { + case Nil => Some(Cmds(Nil, s::Nil)) + case c::_ if !c.preCondition(s) => None + case c::cmds => for { + Cmds(_, ss) <- validCmds(c.nextState(s), cmds) + } yield Cmds(cs, s::ss) + } + + private def runCommands(cmds: Cmds): Prop = cmds match { + case Cmds(Nil, _) => proved + case Cmds(c::cs, s::ss) => + c.postCondition(s,c.nextState(s),c.run(s)) && runCommands(Cmds(cs,ss)) + case _ => error("Should not be here") + } + + private def commandsProp: Prop = { + + def shrinkCmds(cmds: Cmds) = cmds match { case Cmds(cs,_) => + shrink(cs)(shrinkContainer).flatMap(cs => validCmds(initialState(), cs).toList) + } + + forAllShrink(genCmds label "COMMANDS", shrinkCmds)(runCommands _) + + } + + def apply(p: Prop.Params) = commandsProp(p) + + /** initialState should reset the system under test to a well defined + * initial state, and return the abstract version of that state. */ + def initialState(): State + + /** The command generator. Given an abstract state, the generator + * should return a command that is allowed to run in that state. Note that + * it is still neccessary to define preconditions on the commands if there + * are any. The generator is just giving a hint of which commands that are + * suitable for a given state, the preconditions will still be checked before + * a command runs. Sometimes you maybe want to adjust the distribution of + * your command generator according to the state, or do other calculations + * based on the state. */ + def genCommand(s: State): Gen[Command] + +} diff --git a/src/scalacheck/org/scalacheck/ConsoleReporter.scala b/src/scalacheck/org/scalacheck/ConsoleReporter.scala new file mode 100644 index 0000000000..ed2a08d3ae --- /dev/null +++ b/src/scalacheck/org/scalacheck/ConsoleReporter.scala @@ -0,0 +1,89 @@ +/*-------------------------------------------------------------------------*\ +** ScalaCheck ** +** Copyright (c) 2007-2010 Rickard Nilsson. All rights reserved. ** +** http://www.scalacheck.org ** +** ** +** This software is released under the terms of the Revised BSD License. ** +** There is NO WARRANTY. See the file LICENSE for the full text. ** +\*-------------------------------------------------------------------------*/ + +package org.scalacheck + +import Pretty._ +import util.FreqMap + +class ConsoleReporter(val verbosity: Int) extends Test.TestCallback { + + private val prettyPrms = Params(verbosity) + + override def onPropEval(name: String, w: Int, s: Int, d: Int) = + if(verbosity > 0) { + if(name == "") { + if(d == 0) printf("\rPassed %s tests\r", s) + else printf("\rPassed %s tests; %s discarded\r", s, d) + } else { + if(d == 0) printf("\r %s: Passed %s tests\r", name, s) + else printf("\r %s: Passed %s tests; %s discarded\r", name, s, d) + } + Console.flush + } + + override def onTestResult(name: String, res: Test.Result) = { + if(name == "") { + print(List.fill(78)(' ').mkString) + val s = (if(res.passed) "+ " else "! ") + pretty(res, prettyPrms) + printf("\r%s\n", format(s, "", "", 75)) + } else { + print(List.fill(78)(' ').mkString) + val s = (if(res.passed) "+ " else "! ") + name + ": " + + pretty(res, prettyPrms) + printf("\r%s\n", format(s, "", "", 75)) + } + } + +} + +object ConsoleReporter { + + /** Factory method, creates a ConsoleReporter with the + * the given verbosity */ + def apply(verbosity: Int = 0) = new ConsoleReporter(verbosity) + + @deprecated("(v1.8)") + def propReport(s: Int, d: Int) = { + if(d == 0) printf("\rPassed %s tests\r", s) + else printf("\rPassed %s tests; %s discarded\r", s, d) + Console.flush + } + + @deprecated("(v1.8)") + def propReport(pName: String, s: Int, d: Int) = { + if(d == 0) printf("\r %s: Passed %s tests\r", pName, s) + else printf("\r %s: Passed %s tests; %s discarded\r", pName, s, d) + Console.flush + } + + @deprecated("(v1.8)") + def testReport(res: Test.Result) = { + print(List.fill(78)(' ').mkString) + val s = (if(res.passed) "+ " else "! ") + pretty(res, Params(0)) + printf("\r%s\n", format(s, "", "", 75)) + res + } + + @deprecated("(v1.8)") + def testStatsEx(res: Test.Result): Unit = testStatsEx("", res) + + def testStatsEx(msg: String, res: Test.Result) = { + lazy val m = if(msg.length == 0) "" else msg + ": " + res.status match { + case Test.Proved(_) => {} + case Test.Passed => {} + case f @ Test.Failed(_, _) => error(m + f) + case Test.Exhausted => {} + case f @ Test.GenException(_) => error(m + f) + case f @ Test.PropException(_, _, _) => error(m + f) + } + } + +} diff --git a/src/scalacheck/org/scalacheck/Gen.scala b/src/scalacheck/org/scalacheck/Gen.scala new file mode 100644 index 0000000000..ca1dae0d3c --- /dev/null +++ b/src/scalacheck/org/scalacheck/Gen.scala @@ -0,0 +1,487 @@ +/*-------------------------------------------------------------------------*\ +** ScalaCheck ** +** Copyright (c) 2007-2010 Rickard Nilsson. All rights reserved. ** +** http://www.scalacheck.org ** +** ** +** This software is released under the terms of the Revised BSD License. ** +** There is NO WARRANTY. See the file LICENSE for the full text. ** +\*-------------------------------------------------------------------------*/ + +package org.scalacheck + +import scala.collection.mutable.ListBuffer +import util.Buildable +import Prop._ +import Arbitrary._ + +trait Choose[T] { + def choose(min: T, max: T): Gen[T] +} + +object Choose { + import Gen.{fail, parameterized, value} + + implicit val chooseLong: Choose[Long] = new Choose[Long] { + def choose(low: Long, high: Long) = + if(low > high || (high-low < 0)) fail + else parameterized(prms => value(prms.choose(low,high))) + } + + implicit val chooseDouble: Choose[Double] = new Choose[Double] { + def choose(low: Double, high: Double) = + if (low > high || (high-low > Double.MaxValue)) fail + else parameterized(prms => value(prms.choose(low,high))) + } + + implicit val chooseInt: Choose[Int] = new Choose[Int] { + def choose(low: Int, high: Int) = + chooseLong.choose(low, high).map(_.toInt) + } + + implicit val chooseByte: Choose[Byte] = new Choose[Byte] { + def choose(low: Byte, high: Byte) = + chooseLong.choose(low, high).map(_.toByte) + } + + implicit val chooseShort: Choose[Short] = new Choose[Short] { + def choose(low: Short, high: Short) = + chooseLong.choose(low, high).map(_.toShort) + } + + implicit val chooseChar: Choose[Char] = new Choose[Char] { + def choose(low: Char, high: Char) = + chooseLong.choose(low, high).map(_.toChar) + } + + implicit val chooseFloat: Choose[Float] = new Choose[Float] { + def choose(low: Float, high: Float) = + chooseDouble.choose(low, high).map(_.toFloat) + } +} + + +/** Class that represents a generator. */ +sealed trait Gen[+T] { + + import Gen.choose + + var label = "" // TODO: Ugly mutable field + + /** Put a label on the generator to make test reports clearer */ + def label(l: String): Gen[T] = { + label = l + this + } + + /** Put a label on the generator to make test reports clearer */ + def :|(l: String) = label(l) + + /** Put a label on the generator to make test reports clearer */ + def |:(l: String) = label(l) + + /** Put a label on the generator to make test reports clearer */ + def :|(l: Symbol) = label(l.toString.drop(1)) + + /** Put a label on the generator to make test reports clearer */ + def |:(l: Symbol) = label(l.toString.drop(1)) + + def apply(prms: Gen.Params): Option[T] + + def map[U](f: T => U): Gen[U] = Gen(prms => this(prms).map(f)).label(label) + + def map2[U, V](g: Gen[U])(f: (T, U) => V) = + combine(g)((t, u) => t.flatMap(t => u.flatMap(u => Some(f(t, u))))) + + def map3[U, V, W](gu: Gen[U], gv: Gen[V])(f: (T, U, V) => W) = + combine3(gu, gv)((t, u, v) => t.flatMap(t => u.flatMap(u => v.flatMap(v => Some(f(t, u, v)))))) + + def map4[U, V, W, X](gu: Gen[U], gv: Gen[V], gw: Gen[W])(f: (T, U, V, W) => X) = + combine4(gu, gv, gw)((t, u, v, w) => t.flatMap(t => u.flatMap(u => v.flatMap(v => w.flatMap(w => Some(f(t, u, v, w))))))) + + def map5[U, V, W, X, Y](gu: Gen[U], gv: Gen[V], gw: Gen[W], gx: Gen[X])(f: (T, U, V, W, X) => Y) = + combine5(gu, gv, gw, gx)((t, u, v, w, x) => t.flatMap(t => u.flatMap(u => v.flatMap(v => w.flatMap(w => x.flatMap(x => Some(f(t, u, v, w, x)))))))) + + def map6[U, V, W, X, Y, Z](gu: Gen[U], gv: Gen[V], gw: Gen[W], gx: Gen[X], gy: Gen[Y])(f: (T, U, V, W, X, Y) => Z) = + combine6(gu, gv, gw, gx, gy)((t, u, v, w, x, y) => t.flatMap(t => u.flatMap(u => v.flatMap(v => w.flatMap(w => x.flatMap(x => y.flatMap(y => Some(f(t, u, v, w, x, y))))))))) + + def flatMap[U](f: T => Gen[U]): Gen[U] = Gen(prms => for { + t <- this(prms) + u <- f(t)(prms) + } yield u) + + def filter(p: T => Boolean): Gen[T] = Gen(prms => for { + t <- this(prms) + u <- if (p(t)) Some(t) else None + } yield u).label(label) + + def withFilter(p: T => Boolean) = new GenWithFilter[T](this, p) + + final class GenWithFilter[+A](self: Gen[A], p: A => Boolean) { + def map[B](f: A => B): Gen[B] = self filter p map f + def flatMap[B](f: A => Gen[B]): Gen[B] = self filter p flatMap f + def withFilter(q: A => Boolean): GenWithFilter[A] = new GenWithFilter[A](self, x => p(x) && q(x)) + } + + def suchThat(p: T => Boolean): Gen[T] = filter(p) + + def combine[U,V](g: Gen[U])(f: (Option[T],Option[U]) => Option[V]): Gen[V] = + Gen(prms => f(this(prms), g(prms))) + + def combine3[U, V, W](gu: Gen[U], gv: Gen[V]) + (f: (Option[T], Option[U], Option[V]) => Option[W]) = + Gen(prms => f(this(prms), gu(prms), gv(prms))) + + def combine4[U, V, W, X](gu: Gen[U], gv: Gen[V], gw: Gen[W]) + (f: (Option[T], Option[U], Option[V], Option[W]) => Option[X]) = + Gen(prms => f(this(prms), gu(prms), gv(prms), gw(prms))) + + def combine5[U, V, W, X, Y](gu: Gen[U], gv: Gen[V], gw: Gen[W], gx: Gen[X]) + (f: (Option[T], Option[U], Option[V], Option[W], Option[X]) => Option[Y]) = + Gen(prms => f(this(prms), gu(prms), gv(prms), gw(prms), gx(prms))) + + def combine6[U, V, W, X, Y, Z](gu: Gen[U], gv: Gen[V], gw: Gen[W], gx: Gen[X], gy: Gen[Y]) + (f: (Option[T], Option[U], Option[V], Option[W], Option[X], Option[Y]) => Option[Z]) = + Gen(prms => f(this(prms), gu(prms), gv(prms), gw(prms), gx(prms), gy(prms))) + + def ap[U](g: Gen[T => U]) = flatMap(t => g.flatMap(u => Gen(p => Some(u(t))))) + + override def toString = + if(label.length == 0) "Gen()" else "Gen(\"" + label + "\")" + + /** Returns a new property that holds if and only if both this + * and the given generator generates the same result, or both + * generators generate no result. + * @deprecated Use <code>==</code> instead */ + @deprecated("Use == instead") + def ===[U](g: Gen[U]): Prop = this == g + + /** Returns a new property that holds if and only if both this + * and the given generator generates the same result, or both + * generators generate no result. */ + def ==[U](g: Gen[U]) = Prop(prms => + (this(prms.genPrms), g(prms.genPrms)) match { + case (None,None) => proved(prms) + case (Some(r1),Some(r2)) if r1 == r2 => proved(prms) + case _ => falsified(prms) + } + ) + + def !=[U](g: Gen[U]) = forAll(this)(r => forAll(g)(_ != r)) + + def !==[U](g: Gen[U]) = Prop(prms => + (this(prms.genPrms), g(prms.genPrms)) match { + case (None,None) => falsified(prms) + case (Some(r1),Some(r2)) if r1 == r2 => falsified(prms) + case _ => proved(prms) + } + ) + + private var freq = 1 + def |[U >: T](g: Gen[U]): Gen[U] = { + val h = Gen.frequency((freq, this), (1, g)) + h.freq = freq+1 + h + } + + /** Generates a sample value by using default parameters */ + def sample: Option[T] = apply(Gen.Params()) + +} + + +/** Contains combinators for building generators. */ +object Gen { + + import Arbitrary._ + import Shrink._ + + /** Record that encapsulates all parameters required for data generation */ + case class Params( + size: Int = 100, + rng: java.util.Random = util.StdRand + ) { + def resize(newSize: Int) = this.copy(size = newSize) + + /** @throws IllegalArgumentException if l is greater than h, or if + * the range between l and h doesn't fit in a Long. */ + def choose(l: Long, h: Long): Long = { + val d = h-l + if (d < 0) throw new IllegalArgumentException("Invalid range") + else if (d == 0) l + else { + val r = math.abs(rng.nextLong) + val a = if(r == Long.MinValue) 1 else 0 + l + (math.abs(r+a) % d) + a + } + } + + /** @throws IllegalArgumentException if l is greater than h, or if + * the range between l and h doesn't fit in a Double. */ + def choose(l: Double, h: Double) = { + val d = h-l + if (d < 0 || d > Double.MaxValue) + throw new IllegalArgumentException("Invalid range") + else if (d == 0) l + else rng.nextDouble * (h-l) + l + } + } + + /* Default generator parameters + * @deprecated Use <code>Gen.Params()</code> instead */ + @deprecated("Use Gen.Params() instead") + val defaultParams = Params() + + /* Generator factory method */ + def apply[T](g: Gen.Params => Option[T]) = new Gen[T] { + def apply(p: Gen.Params) = g(p) + } + + /* Convenience method for using the <code>frequency</code> method like this: + * <code>frequency((1, "foo"), (3, "bar"))</code> */ + implicit def freqTuple[T](t: (Int, T)): (Int, Gen[T]) = (t._1, value(t._2)) + + + //// Various Generator Combinators //// + + /** Sequences generators. If any of the given generators fails, the + * resulting generator will also fail. */ + def sequence[C[_],T](gs: Iterable[Gen[T]])(implicit b: Buildable[T,C]): Gen[C[T]] = Gen(prms => { + val builder = b.builder + var none = false + val xs = gs.iterator + while(xs.hasNext && !none) xs.next.apply(prms) match { + case None => none = true + case Some(x) => builder += x + } + if(none) None else Some(builder.result()) + }) + + /** Wraps a generator lazily. The given parameter is only evalutated once, + * and not until the wrapper generator is evaluated. */ + def lzy[T](g: => Gen[T]) = new Gen[T] { + lazy val h = g + def apply(prms: Params) = h(prms) + } + + /** Wraps a generator for later evaluation. The given parameter is + * evaluated each time the wrapper generator is evaluated. */ + def wrap[T](g: => Gen[T]) = Gen(p => g(p)) + + /** A generator that always generates the given value */ + implicit def value[T](x: T) = Gen(p => Some(x)) + + /** A generator that never generates a value */ + def fail[T]: Gen[T] = Gen(p => None) + + /** A generator that generates a random value in the given (inclusive) + * range. If the range is invalid, the generator will not generate any value. + */ + def choose[T](min: T, max: T)(implicit c: Choose[T]): Gen[T] = { + c.choose(min, max) + } + + /** Creates a generator that can access its generation parameters */ + def parameterized[T](f: Params => Gen[T]): Gen[T] = Gen(prms => f(prms)(prms)) + + /** Creates a generator that can access its generation size */ + def sized[T](f: Int => Gen[T]) = parameterized(prms => f(prms.size)) + + /** Creates a resized version of a generator */ + def resize[T](s: Int, g: Gen[T]) = Gen(prms => g(prms.resize(s))) + + /** Chooses one of the given generators with a weighted random distribution */ + def frequency[T](gs: (Int,Gen[T])*): Gen[T] = { + lazy val tot = (gs.map(_._1) :\ 0) (_+_) + + def pick(n: Int, l: List[(Int,Gen[T])]): Gen[T] = l match { + case Nil => fail + case (k,g)::gs => if(n <= k) g else pick(n-k, gs) + } + + for { + n <- choose(1,tot) + x <- pick(n,gs.toList) + } yield x + } + + /** Picks a random value from a list */ + def oneOf[T](xs: Seq[T]): Gen[T] = if(xs.isEmpty) fail else for { + i <- choose(0, xs.size-1) + } yield xs(i) + + /** Picks a random generator from a list */ + def oneOf[T](g1: Gen[T], g2: Gen[T], gs: Gen[T]*) = for { + i <- choose(0, gs.length+1) + x <- if(i == 0) g1 else if(i == 1) g2 else gs(i-2) + } yield x + + /** Chooses one of the given values, with a weighted random distribution. + * @deprecated Use <code>frequency</code> with constant generators + * instead. */ + @deprecated("Use 'frequency' with constant generators instead.") + def elementsFreq[T](vs: (Int, T)*): Gen[T] = + frequency(vs.map { case (w,v) => (w, value(v)) } : _*) + + /** A generator that returns a random element from a list + * @deprecated Use <code>oneOf</code> with constant generators instead. */ + @deprecated("Use 'oneOf' with constant generators instead.") + def elements[T](xs: T*): Gen[T] = if(xs.isEmpty) fail else for { + i <- choose(0,xs.length-1) + } yield xs(i) + + + //// List Generators //// + + /** Generates a container of any type for which there exists an implicit + * <code>Buildable</code> instance. The elements in the container will + * be generated by the given generator. The size of the generated container + * is given by <code>n</code>. */ + def containerOfN[C[_],T](n: Int, g: Gen[T])(implicit b: Buildable[T,C] + ): Gen[C[T]] = sequence[C,T](new Iterable[Gen[T]] { + def iterator = new Iterator[Gen[T]] { + var i = 0 + def hasNext = i < n + def next = { i += 1; g } + } + }) + + /** Generates a container of any type for which there exists an implicit + * <code>Buildable</code> instance. The elements in the container will + * be generated by the given generator. The size of the container is + * bounded by the size parameter used when generating values. */ + def containerOf[C[_],T](g: Gen[T])(implicit b: Buildable[T,C]): Gen[C[T]] = + sized(size => for(n <- choose(0,size); c <- containerOfN[C,T](n,g)) yield c) + + /** Generates a non-empty container of any type for which there exists an + * implicit <code>Buildable</code> instance. The elements in the container + * will be generated by the given generator. The size of the container is + * bounded by the size parameter used when generating values. */ + def containerOf1[C[_],T](g: Gen[T])(implicit b: Buildable[T,C]): Gen[C[T]] = + sized(size => for(n <- choose(1,size); c <- containerOfN[C,T](n,g)) yield c) + + /** Generates a list of random length. The maximum length depends on the + * size parameter. This method is equal to calling + * <code>containerOf[List,T](g)</code>. */ + def listOf[T](g: => Gen[T]) = containerOf[List,T](g) + + /** Generates a non-empty list of random length. The maximum length depends + * on the size parameter. This method is equal to calling + * <code>containerOf1[List,T](g)</code>. */ + def listOf1[T](g: => Gen[T]) = containerOf1[List,T](g) + + /** Generates a list of the given length. This method is equal to calling + * <code>containerOfN[List,T](n,g)</code>. */ + def listOfN[T](n: Int, g: Gen[T]) = containerOfN[List,T](n,g) + + /** Generates a list of the given length. This method is equal to calling + * <code>containerOfN[List,T](n,g)</code>. + * @deprecated Use the method <code>listOfN</code> instead. */ + @deprecated("Use 'listOfN' instead.") + def vectorOf[T](n: Int, g: Gen[T]) = containerOfN[List,T](n,g) + + /** A generator that picks a random number of elements from a list */ + def someOf[T](l: Iterable[T]) = choose(0,l.size) flatMap (pick(_,l)) + + /** A generator that picks a random number of elements from a list */ + def someOf[T](g1: Gen[T], g2: Gen[T], gs: Gen[T]*) = for { + n <- choose(0, gs.length+2) + x <- pick(n, g1, g2, gs: _*) + } yield x + + /** A generator that picks a given number of elements from a list, randomly */ + def pick[T](n: Int, l: Iterable[T]): Gen[Seq[T]] = + if(n > l.size || n < 0) fail + else Gen(prms => { + val buf = new ListBuffer[T] + buf ++= l + while(buf.length > n) { + val g = choose(0, buf.length-1) + buf.remove(g(prms).get) + } + Some(buf) + }) + + /** A generator that picks a given number of elements from a list, randomly */ + def pick[T](n: Int, g1: Gen[T], g2: Gen[T], gs: Gen[T]*): Gen[Seq[T]] = for { + is <- pick(n, 0 until (gs.size+2)) + allGs = gs ++ (g1::g2::Nil) + xs <- sequence[List,T](is.toList.map(allGs(_))) + } yield xs + + + //// Character Generators //// + + /* Generates a numerical character */ + def numChar: Gen[Char] = choose(48,57) map (_.toChar) + + /* Generates an upper-case alpha character */ + def alphaUpperChar: Gen[Char] = choose(65,90) map (_.toChar) + + /* Generates a lower-case alpha character */ + def alphaLowerChar: Gen[Char] = choose(97,122) map (_.toChar) + + /* Generates an alpha character */ + def alphaChar = frequency((1,alphaUpperChar), (9,alphaLowerChar)) + + /* Generates an alphanumerical character */ + def alphaNumChar = frequency((1,numChar), (9,alphaChar)) + + //// String Generators //// + + /* Generates a string that starts with a lower-case alpha character, + * and only contains alphanumerical characters */ + def identifier: Gen[String] = for { + c <- alphaLowerChar + cs <- listOf(alphaNumChar) + } yield (c::cs).mkString + + /* Generates a string of alpha characters */ + def alphaStr: Gen[String] = for(cs <- listOf(Gen.alphaChar)) yield cs.mkString + + /* Generates a string of digits */ + def numStr: Gen[String] = for(cs <- listOf(Gen.numChar)) yield cs.mkString + + //// Number Generators //// + + /* Generates positive integers + * @deprecated Use <code>posNum[Int]code> instead */ + @deprecated("Use posNum[Int] instead") + def posInt: Gen[Int] = sized(max => choose(1, max)) + + /* Generates negative integers + * @deprecated Use <code>negNum[Int]code> instead */ + @deprecated("Use negNum[Int] instead") + def negInt: Gen[Int] = sized(max => choose(-max, -1)) + + /** Generates positive numbers of uniform distribution, with an + * upper bound of the generation size parameter. */ + def posNum[T](implicit num: Numeric[T], c: Choose[T]): Gen[T] = { + import num._ + sized(max => c.choose(one, fromInt(max))) + } + + /** Generates negative numbers of uniform distribution, with an + * lower bound of the negated generation size parameter. */ + def negNum[T](implicit num: Numeric[T], c: Choose[T]): Gen[T] = { + import num._ + sized(max => c.choose(-fromInt(max), -one)) + } + + /** Generates numbers within the given inclusive range, with + * extra weight on zero, +/- unity, both extremities, and any special + * numbers provided. The special numbers must lie within the given range, + * otherwise they won't be included. */ + def chooseNum[T](minT: T, maxT: T, specials: T*)( + implicit num: Numeric[T], c: Choose[T] + ): Gen[T] = { + import num._ + val basics = List(minT, maxT, zero, one, -one) + val basicsAndSpecials = for { + t <- specials ++ basics if t >= minT && t <= maxT + } yield (1, value(t)) + val allGens = basicsAndSpecials ++ List( + (basicsAndSpecials.length, c.choose(minT, maxT)) + ) + frequency(allGens: _*) + } +} diff --git a/src/scalacheck/org/scalacheck/Pretty.scala b/src/scalacheck/org/scalacheck/Pretty.scala new file mode 100644 index 0000000000..d3945a1985 --- /dev/null +++ b/src/scalacheck/org/scalacheck/Pretty.scala @@ -0,0 +1,114 @@ +/*-------------------------------------------------------------------------*\ +** ScalaCheck ** +** Copyright (c) 2007-2010 Rickard Nilsson. All rights reserved. ** +** http://www.scalacheck.org ** +** ** +** This software is released under the terms of the Revised BSD License. ** +** There is NO WARRANTY. See the file LICENSE for the full text. ** +\*-------------------------------------------------------------------------*/ + +package org.scalacheck + +import math.round + + +sealed trait Pretty { + def apply(prms: Pretty.Params): String + + def map(f: String => String) = Pretty(prms => f(Pretty.this(prms))) + + def flatMap(f: String => Pretty) = Pretty(prms => f(Pretty.this(prms))(prms)) +} + +object Pretty { + + case class Params(verbosity: Int) + + val defaultParams = Params(0) + + def apply(f: Params => String) = new Pretty { def apply(p: Params) = f(p) } + + def pretty[T <% Pretty](t: T, prms: Params): String = t(prms) + + def pretty[T <% Pretty](t: T): String = t(defaultParams) + + implicit def strBreak(s1: String) = new { + def /(s2: String) = if(s2 == "") s1 else s1+"\n"+s2 + } + + def pad(s: String, c: Char, length: Int) = + if(s.length >= length) s + else s + List.fill(length-s.length)(c).mkString + + def break(s: String, lead: String, length: Int): String = + if(s.length <= length) s + else s.substring(0, length) / break(lead+s.substring(length), lead, length) + + def format(s: String, lead: String, trail: String, width: Int) = + s.lines.map(l => break(lead+l+trail, " ", width)).mkString("\n") + + implicit def prettyAny(t: Any) = Pretty { p => t.toString } + + implicit def prettyList(l: List[Any]) = Pretty { p => + l.map("\""+_+"\"").mkString("List(", ", ", ")") + } + + implicit def prettyThrowable(e: Throwable) = Pretty { prms => + val strs = e.getStackTrace.map { st => + import st._ + getClassName+"."+getMethodName + "("+getFileName+":"+getLineNumber+")" + } + + val strs2 = if(prms.verbosity > 0) strs else strs.take(5) + + e.getClass.getName + ": " + e.getMessage / strs2.mkString("\n") + } + + implicit def prettyArgs(args: List[Arg[Any]]): Pretty = Pretty { prms => + if(args.isEmpty) "" else { + for((a,i) <- args.zipWithIndex) yield { + val l = if(a.label == "") "ARG_"+i else a.label + val s = + if(a.shrinks == 0) "" + else " (orig arg: "+a.prettyOrigArg(prms)+")" + + "> "+l+": "+a.prettyArg(prms)+""+s + } + }.mkString("\n") + } + + implicit def prettyFreqMap(fm: Prop.FM) = Pretty { prms => + if(fm.total == 0) "" + else { + "> Collected test data: " / { + for { + (xs,r) <- fm.getRatios + ys = xs - () + if !ys.isEmpty + } yield round(r*100)+"% " + ys.mkString(", ") + }.mkString("\n") + } + } + + implicit def prettyTestRes(res: Test.Result) = Pretty { prms => + def labels(ls: collection.immutable.Set[String]) = + if(ls.isEmpty) "" + else "> Labels of failing property: " / ls.mkString("\n") + val s = res.status match { + case Test.Proved(args) => "OK, proved property."/pretty(args,prms) + case Test.Passed => "OK, passed "+res.succeeded+" tests." + case Test.Failed(args, l) => + "Falsified after "+res.succeeded+" passed tests."/labels(l)/pretty(args,prms) + case Test.Exhausted => + "Gave up after only "+res.succeeded+" passed tests. " + + res.discarded+" tests were discarded." + case Test.PropException(args,e,l) => + "Exception raised on property evaluation."/labels(l)/pretty(args,prms)/ + "> Exception: "+pretty(e,prms) + case Test.GenException(e) => + "Exception raised on argument generation."/"> Stack trace: "/pretty(e,prms) + } + s/pretty(res.freqMap,prms) + } + +} diff --git a/src/scalacheck/org/scalacheck/Prop.scala b/src/scalacheck/org/scalacheck/Prop.scala new file mode 100644 index 0000000000..68e4b89660 --- /dev/null +++ b/src/scalacheck/org/scalacheck/Prop.scala @@ -0,0 +1,741 @@ +/*-------------------------------------------------------------------------*\ +** ScalaCheck ** +** Copyright (c) 2007-2010 Rickard Nilsson. All rights reserved. ** +** http://www.scalacheck.org ** +** ** +** This software is released under the terms of the Revised BSD License. ** +** There is NO WARRANTY. See the file LICENSE for the full text. ** +\*-------------------------------------------------------------------------*/ + +package org.scalacheck + +import util.{FreqMap,Buildable} +import scala.collection._ + +/** A property is a generator that generates a property result */ +trait Prop { + + import Prop.{Result,Params,Proof,True,False,Exception,Undecided,provedToTrue} + import Test.cmdLineParser.{Success, NoSuccess} + import Result.merge + + def apply(prms: Params): Result + + def map(f: Result => Result): Prop = Prop(prms => f(this(prms))) + + def flatMap(f: Result => Prop): Prop = Prop(prms => f(this(prms))(prms)) + + def combine(p: Prop)(f: (Result, Result) => Result) = + for(r1 <- this; r2 <- p) yield f(r1,r2) + + /** Convenience method that checks this property with the given parameters + * and reports the result on the console. If you need to get the results + * from the test use the <code>check</code> methods in <code>Test</code> + * instead. */ + def check(prms: Test.Params): Unit = Test.check( + prms copy (testCallback = ConsoleReporter(1) chain prms.testCallback), this + ) + + /** Convenience method that checks this property and reports the + * result on the console. If you need to get the results from the test use + * the <code>check</code> methods in <code>Test</code> instead. */ + def check: Unit = check(Test.Params()) + + /** Convenience method that makes it possible to use a this property + * as an application that checks itself on execution */ + def main(args: Array[String]): Unit = + Test.cmdLineParser.parseParams(args) match { + case Success(params, _) => Test.check(params, this) + case e: NoSuccess => + println("Incorrect options:"+"\n"+e+"\n") + Test.cmdLineParser.printHelp + } + + /** Returns a new property that holds if and only if both this + * and the given property hold. If one of the properties doesn't + * generate a result, the new property will generate false. */ + def &&(p: Prop) = combine(p)(_ && _) + + /** Returns a new property that holds if either this + * or the given property (or both) hold. */ + def ||(p: Prop) = combine(p)(_ || _) + + /** Returns a new property that holds if and only if both this + * and the given property hold. If one of the properties doesn't + * generate a result, the new property will generate the same result + * as the other property. */ + def ++(p: Prop): Prop = combine(p)(_ ++ _) + + /** Combines two properties through implication */ + def ==>(p: => Prop): Prop = flatMap { r1 => + if(r1.proved) p map { r2 => merge(r1,r2,r2.status) } + else if(r1.success) p map { r2 => provedToTrue(merge(r1,r2,r2.status)) } + else Prop(r1.copy(status = Undecided)) + } + + /** Returns a new property that holds if and only if both this + * and the given property generates a result with the exact + * same status. Note that this means that if one of the properties is + * proved, and the other one passed, then the resulting property + * will fail. */ + def ==(p: Prop) = this.flatMap { r1 => + p.map { r2 => + Result.merge(r1, r2, if(r1.status == r2.status) True else False) + } + } + + /** Returns a new property that holds if and only if both this + * and the given property generates a result with the exact + * same status. Note that this means that if one of the properties is + * proved, and the other one passed, then the resulting property + * will fail. + * @deprecated Use <code>==</code> instead */ + @deprecated("Use == instead.") + def ===(p: Prop): Prop = this == p + + override def toString = "Prop" + + /** Put a label on the property to make test reports clearer */ + def label(l: String) = map(_.label(l)) + + /** Put a label on the property to make test reports clearer */ + def :|(l: String) = label(l) + + /** Put a label on the property to make test reports clearer */ + def |:(l: String) = label(l) + + /** Put a label on the property to make test reports clearer */ + def :|(l: Symbol) = label(l.toString.drop(1)) + + /** Put a label on the property to make test reports clearer */ + def |:(l: Symbol) = label(l.toString.drop(1)) + +} + +object Prop { + + import Gen.{value, fail, frequency, oneOf} + import Arbitrary._ + import Shrink._ + + + // Types + + type Args = List[Arg[Any]] + type FM = FreqMap[immutable.Set[Any]] + + /** Property parameters */ + case class Params(val genPrms: Gen.Params, val freqMap: FM) + + object Result { + def apply(st: Status) = new Result( + st, + Nil, + immutable.Set.empty[Any], + immutable.Set.empty[String] + ) + + def merge(x: Result, y: Result, status: Status) = new Result( + status, + x.args ++ y.args, + (x.collected.asInstanceOf[Set[AnyRef]] ++ y.collected).asInstanceOf[immutable.Set[Any]], + x.labels ++ y.labels + ) + } + + /** The result of evaluating a property */ + case class Result( + status: Status, + args: Args, + collected: immutable.Set[Any], + labels: immutable.Set[String] + ) { + def success = status match { + case True => true + case Proof => true + case _ => false + } + + def failure = status match { + case False => true + case Exception(_) => true + case _ => false + } + + def proved = status == Proof + + def addArg(a: Arg[Any]) = copy(args = a::args) + + def collect(x: Any) = copy(collected = collected+x) + + def label(l: String) = copy(labels = labels+l) + + import Result.merge + + def &&(r: Result) = (this.status, r.status) match { + case (Exception(_),_) => this + case (_,Exception(_)) => r + + case (False,_) => this + case (_,False) => r + + case (Undecided,_) => this + case (_,Undecided) => r + + case (_,Proof) => merge(this, r, this.status) + case (Proof,_) => merge(this, r, this.status) + + case (True,True) => merge(this, r, True) + } + + def ||(r: Result) = (this.status, r.status) match { + case (Exception(_),_) => this + case (_,Exception(_)) => r + + case (False,False) => merge(this, r, False) + case (False,_) => r + case (_,False) => this + + case (Proof,_) => this + case (_,Proof) => r + + case (True,_) => this + case (_,True) => r + + case (Undecided,Undecided) => merge(this, r, Undecided) + } + + def ++(r: Result) = (this.status, r.status) match { + case (Exception(_),_) => this + case (_,Exception(_)) => r + + case (_, Undecided) => this + case (Undecided, _) => r + + case (_, Proof) => this + case (Proof, _) => r + + case (_, True) => this + case (True, _) => r + + case (False, _) => this + case (_, False) => r + } + + def ==>(r: Result) = (this.status, r.status) match { + case (Exception(_),_) => this + case (_,Exception(_)) => r + + case (False,_) => merge(this, r, Undecided) + + case (Undecided,_) => this + + case (Proof,_) => merge(this, r, r.status) + case (True,_) => merge(this, r, r.status) + } + + } + + sealed trait Status + + /** The property was proved */ + case object Proof extends Status + + /** The property was true */ + case object True extends Status + + /** The property was false */ + case object False extends Status + + /** The property could not be falsified or proved */ + case object Undecided extends Status + + /** Evaluating the property raised an exception */ + sealed case class Exception(e: Throwable) extends Status { + override def equals(o: Any) = o match { + case Exception(_) => true + case _ => false + } + } + + def apply(f: Params => Result): Prop = new Prop { + def apply(prms: Params) = f(prms) + } + + def apply(r: Result): Prop = Prop(prms => r) + + + // Implicit defs + + class ExtendedAny[T <% Pretty](x: => T) { + def imply(f: PartialFunction[T,Prop]) = Prop.imply(x,f) + def iff(f: PartialFunction[T,Prop]) = Prop.iff(x,f) + def throws[U <: Throwable](c: Class[U]) = Prop.throws(x, c) + def ?=(y: T) = Prop.?=(x, y) + def =?(y: T) = Prop.=?(x, y) + } + + implicit def extendedAny[T <% Pretty](x: => T) = new ExtendedAny[T](x) + + implicit def propBoolean(b: Boolean): Prop = if(b) proved else falsified + + + // Private support functions + + private def provedToTrue(r: Result) = r.status match { + case Proof => new Result(True, r.args, r.collected, r.labels) + case _ => r + } + + + // Property combinators + + /** A property that never is proved or falsified */ + lazy val undecided = Prop(Result(Undecided)) + + /** A property that always is false */ + lazy val falsified = Prop(Result(False)) + + /** A property that always is proved */ + lazy val proved = Prop(Result(Proof)) + + /** A property that always is passed */ + lazy val passed = Prop(Result(True)) + + /** A property that denotes an exception */ + def exception(e: Throwable): Prop = Prop(Result(Exception(e))) + + /** A property that denotes an exception */ + lazy val exception: Prop = exception(null) + + def ?=[T](x: T, y: T)(implicit pp: T => Pretty): Prop = + if(x == y) proved else falsified :| { + val exp = Pretty.pretty[T](y, Pretty.Params(0)) + val act = Pretty.pretty[T](x, Pretty.Params(0)) + "Expected "+exp+" but got "+act + } + + def =?[T](x: T, y: T)(implicit pp: T => Pretty): Prop = ?=(y, x) + + /** A property that depends on the generator size */ + def sizedProp(f: Int => Prop): Prop = Prop(prms => f(prms.genPrms.size)(prms)) + + /** Implication + * @deprecated Use the implication operator of the Prop class instead + */ + @deprecated("Use the implication operator of the Prop class instead") + def ==>(b: => Boolean, p: => Prop): Prop = (b: Prop) ==> p + + /** Implication with several conditions */ + def imply[T](x: T, f: PartialFunction[T,Prop]): Prop = + secure(if(f.isDefinedAt(x)) f(x) else undecided) + + /** Property holds only if the given partial function is defined at + * <code>x</code>, and returns a property that holds */ + def iff[T](x: T, f: PartialFunction[T,Prop]): Prop = + secure(if(f.isDefinedAt(x)) f(x) else falsified) + + /** Combines properties into one, which is true if and only if all the + * properties are true */ + def all(ps: Prop*) = if(ps.isEmpty) proved else Prop(prms => + ps.map(p => p(prms)).reduceLeft(_ && _) + ) + + /** Combines properties into one, which is true if at least one of the + * properties is true */ + def atLeastOne(ps: Prop*) = if(ps.isEmpty) falsified else Prop(prms => + ps.map(p => p(prms)).reduceLeft(_ || _) + ) + + /** A property that holds if at least one of the given generators + * fails generating a value */ + def someFailing[T](gs: Seq[Gen[T]]) = atLeastOne(gs.map(_ == fail):_*) + + /** A property that holds iff none of the given generators + * fails generating a value */ + def noneFailing[T](gs: Seq[Gen[T]]) = all(gs.map(_ !== fail):_*) + + /** A property that holds if the given statement throws an exception + * of the specified type */ + def throws[T <: Throwable](x: => Any, c: Class[T]) = + try { x; falsified } catch { case e if c.isInstance(e) => proved } + + /** Collect data for presentation in test report */ + def collect[T, P <% Prop](f: T => P): T => Prop = t => Prop { prms => + val prop = f(t) + prop(prms).collect(t) + } + + /** Collect data for presentation in test report */ + def collect[T](t: T)(prop: Prop) = Prop { prms => + prop(prms).collect(t) + } + + /** Collect data for presentation in test report */ + def classify(c: => Boolean, ifTrue: Any)(prop: Prop): Prop = + if(c) collect(ifTrue)(prop) else collect(())(prop) + + /** Collect data for presentation in test report */ + def classify(c: => Boolean, ifTrue: Any, ifFalse: Any)(prop: Prop): Prop = + if(c) collect(ifTrue)(prop) else collect(ifFalse)(prop) + + /** Wraps and protects a property */ + def secure[P <% Prop](p: => P): Prop = + try { p: Prop } catch { case e => exception(e) } + + /** Existential quantifier for an explicit generator. */ + def exists[A,P](f: A => P)(implicit + pv: P => Prop, + pp: A => Pretty, + aa: Arbitrary[A] + ): Prop = exists(aa.arbitrary)(f) + + /** Existential quantifier for an explicit generator. */ + def exists[A,P](g: Gen[A])(f: A => P)(implicit + pv: P => Prop, + pp: A => Pretty + ): Prop = Prop { prms => + g(prms.genPrms) match { + case None => undecided(prms) + case Some(x) => + val p = secure(f(x)) + val r = p(prms).addArg(Arg(g.label,x,0,x)) + r.status match { + case True => new Result(Proof, r.args, r.collected, r.labels) + case False => new Result(Undecided, r.args, r.collected, r.labels) + case _ => r + } + } + } + + /** Universal quantifier for an explicit generator. Does not shrink failed + * test cases. */ + def forAllNoShrink[T1,P]( + g1: Gen[T1])( + f: T1 => P)(implicit + pv: P => Prop, + pp1: T1 => Pretty + ): Prop = Prop { prms => + g1(prms.genPrms) match { + case None => undecided(prms) + case Some(x) => + val p = secure(f(x)) + provedToTrue(p(prms)).addArg(Arg(g1.label,x,0,x)) + } + } + + /** Universal quantifier for two explicit generators. + * Does not shrink failed test cases. */ + def forAllNoShrink[T1,T2,P]( + g1: Gen[T1], g2: Gen[T2])( + f: (T1,T2) => P)(implicit + p: P => Prop, + pp1: T1 => Pretty, + pp2: T2 => Pretty + ): Prop = forAllNoShrink(g1)(t => forAllNoShrink(g2)(f(t, _:T2))) + + /** Universal quantifier for three explicit generators. + * Does not shrink failed test cases. */ + def forAllNoShrink[T1,T2,T3,P]( + g1: Gen[T1], g2: Gen[T2], g3: Gen[T3])( + f: (T1,T2,T3) => P)(implicit + p: P => Prop, + pp1: T1 => Pretty, + pp2: T2 => Pretty, + pp3: T3 => Pretty + ): Prop = forAllNoShrink(g1)(t => forAllNoShrink(g2,g3)(f(t, _:T2, _:T3))) + + /** Universal quantifier for four explicit generators. + * Does not shrink failed test cases. */ + def forAllNoShrink[T1,T2,T3,T4,P]( + g1: Gen[T1], g2: Gen[T2], g3: Gen[T3], g4: Gen[T4])( + f: (T1,T2,T3,T4) => P)(implicit + p: P => Prop, + pp1: T1 => Pretty, + pp2: T2 => Pretty, + pp3: T3 => Pretty, + pp4: T4 => Pretty + ): Prop = forAllNoShrink(g1)(t => forAllNoShrink(g2,g3,g4)(f(t, _:T2, _:T3, _:T4))) + + /** Universal quantifier for five explicit generators. + * Does not shrink failed test cases. */ + def forAllNoShrink[T1,T2,T3,T4,T5,P]( + g1: Gen[T1], g2: Gen[T2], g3: Gen[T3], g4: Gen[T4], g5: Gen[T5])( + f: (T1,T2,T3,T4,T5) => P)(implicit + p: P => Prop, + pp1: T1 => Pretty, + pp2: T2 => Pretty, + pp3: T3 => Pretty, + pp4: T4 => Pretty, + pp5: T5 => Pretty + ): Prop = forAllNoShrink(g1)(t => forAllNoShrink(g2,g3,g4,g5)(f(t, _:T2, _:T3, _:T4, _:T5))) + + /** Universal quantifier for six explicit generators. + * Does not shrink failed test cases. */ + def forAllNoShrink[T1,T2,T3,T4,T5,T6,P]( + g1: Gen[T1], g2: Gen[T2], g3: Gen[T3], g4: Gen[T4], g5: Gen[T5], g6: Gen[T6])( + f: (T1,T2,T3,T4,T5,T6) => P)(implicit + p: P => Prop, + pp1: T1 => Pretty, + pp2: T2 => Pretty, + pp3: T3 => Pretty, + pp4: T4 => Pretty, + pp5: T5 => Pretty, + pp6: T6 => Pretty + ): Prop = forAllNoShrink(g1)(t => forAllNoShrink(g2,g3,g4,g5,g6)(f(t, _:T2, _:T3, _:T4, _:T5, _:T6))) + + /** Universal quantifier for seven explicit generators. + * Does not shrink failed test cases. */ + def forAllNoShrink[T1,T2,T3,T4,T5,T6,T7,P]( + g1: Gen[T1], g2: Gen[T2], g3: Gen[T3], g4: Gen[T4], g5: Gen[T5], g6: Gen[T6], g7: Gen[T7])( + f: (T1,T2,T3,T4,T5,T6,T7) => P)(implicit + p: P => Prop, + pp1: T1 => Pretty, + pp2: T2 => Pretty, + pp3: T3 => Pretty, + pp4: T4 => Pretty, + pp5: T5 => Pretty, + pp6: T6 => Pretty, + pp7: T7 => Pretty + ): Prop = forAllNoShrink(g1)(t => forAllNoShrink(g2,g3,g4,g5,g6,g7)(f(t, _:T2, _:T3, _:T4, _:T5, _:T6, _:T7))) + + /** Universal quantifier for eight explicit generators. + * Does not shrink failed test cases. */ + def forAllNoShrink[T1,T2,T3,T4,T5,T6,T7,T8,P]( + g1: Gen[T1], g2: Gen[T2], g3: Gen[T3], g4: Gen[T4], g5: Gen[T5], g6: Gen[T6], g7: Gen[T7], g8: Gen[T8])( + f: (T1,T2,T3,T4,T5,T6,T7,T8) => P)(implicit + p: P => Prop, + pp1: T1 => Pretty, + pp2: T2 => Pretty, + pp3: T3 => Pretty, + pp4: T4 => Pretty, + pp5: T5 => Pretty, + pp6: T6 => Pretty, + pp7: T7 => Pretty, + pp8: T8 => Pretty + ): Prop = forAllNoShrink(g1)(t => forAllNoShrink(g2,g3,g4,g5,g6,g7,g8)(f(t, _:T2, _:T3, _:T4, _:T5, _:T6, _:T7, _:T8))) + + /** Universal quantifier for an explicit generator. Shrinks failed arguments + * with the given shrink function */ + def forAllShrink[T <% Pretty, P <% Prop](g: Gen[T], + shrink: T => Stream[T])(f: T => P + ): Prop = Prop { prms => + + /** Returns the first failed result in Left or success in Right */ + def getFirstFailure(xs: Stream[T]): Either[(T,Result),(T,Result)] = { + assert(!xs.isEmpty, "Stream cannot be empty") + val results = xs.map { x => + val p = secure(f(x)) + (x, provedToTrue(p(prms))) + } + results.dropWhile(!_._2.failure).headOption match { + case None => Right(results.head) + case Some(xr) => Left(xr) + } + } + + def shrinker(x: T, r: Result, shrinks: Int, orig: T): Result = { + val xs = shrink(x) + val res = r.addArg(Arg(g.label,x,shrinks,orig)) + if(xs.isEmpty) res else getFirstFailure(xs) match { + case Right(_) => res + case Left((x2,r2)) => shrinker(x2, r2, shrinks+1, orig) + } + } + + g(prms.genPrms) match { + case None => undecided(prms) + case Some(x) => getFirstFailure(Stream.cons(x, Stream.empty)) match { + case Right((x,r)) => r.addArg(Arg(g.label,x,0,x)) + case Left((x,r)) => shrinker(x,r,0,x) + } + } + + } + + /** Universal quantifier for an explicit generator. Shrinks failed arguments + * with the default shrink function for the type */ + def forAll[T1,P]( + g1: Gen[T1])( + f: T1 => P)(implicit + p: P => Prop, + s1: Shrink[T1], + pp1: T1 => Pretty + ): Prop = forAllShrink(g1, shrink[T1])(f) + + /** Universal quantifier for two explicit generators. Shrinks failed arguments + * with the default shrink function for the type */ + def forAll[T1,T2,P]( + g1: Gen[T1], g2: Gen[T2])( + f: (T1,T2) => P)(implicit + p: P => Prop, + s1: Shrink[T1], pp1: T1 => Pretty, + s2: Shrink[T2], pp2: T2 => Pretty + ): Prop = forAll(g1)(t => forAll(g2)(f(t, _:T2))) + + /** Universal quantifier for three explicit generators. Shrinks failed arguments + * with the default shrink function for the type */ + def forAll[T1,T2,T3,P]( + g1: Gen[T1], g2: Gen[T2], g3: Gen[T3])( + f: (T1,T2,T3) => P)(implicit + p: P => Prop, + s1: Shrink[T1], pp1: T1 => Pretty, + s2: Shrink[T2], pp2: T2 => Pretty, + s3: Shrink[T3], pp3: T3 => Pretty + ): Prop = forAll(g1)(t => forAll(g2,g3)(f(t, _:T2, _:T3))) + + /** Universal quantifier for four explicit generators. Shrinks failed arguments + * with the default shrink function for the type */ + def forAll[T1,T2,T3,T4,P]( + g1: Gen[T1], g2: Gen[T2], g3: Gen[T3], g4: Gen[T4])( + f: (T1,T2,T3,T4) => P)(implicit + p: P => Prop, + s1: Shrink[T1], pp1: T1 => Pretty, + s2: Shrink[T2], pp2: T2 => Pretty, + s3: Shrink[T3], pp3: T3 => Pretty, + s4: Shrink[T4], pp4: T4 => Pretty + ): Prop = forAll(g1)(t => forAll(g2,g3,g4)(f(t, _:T2, _:T3, _:T4))) + + /** Universal quantifier for five explicit generators. Shrinks failed arguments + * with the default shrink function for the type */ + def forAll[T1,T2,T3,T4,T5,P]( + g1: Gen[T1], g2: Gen[T2], g3: Gen[T3], g4: Gen[T4], g5: Gen[T5])( + f: (T1,T2,T3,T4,T5) => P)(implicit + p: P => Prop, + s1: Shrink[T1], pp1: T1 => Pretty, + s2: Shrink[T2], pp2: T2 => Pretty, + s3: Shrink[T3], pp3: T3 => Pretty, + s4: Shrink[T4], pp4: T4 => Pretty, + s5: Shrink[T5], pp5: T5 => Pretty + ): Prop = forAll(g1)(t => forAll(g2,g3,g4,g5)(f(t, _:T2, _:T3, _:T4, _:T5))) + + /** Universal quantifier for six explicit generators. Shrinks failed arguments + * with the default shrink function for the type */ + def forAll[T1,T2,T3,T4,T5,T6,P]( + g1: Gen[T1], g2: Gen[T2], g3: Gen[T3], g4: Gen[T4], g5: Gen[T5], g6: Gen[T6])( + f: (T1,T2,T3,T4,T5,T6) => P)(implicit + p: P => Prop, + s1: Shrink[T1], pp1: T1 => Pretty, + s2: Shrink[T2], pp2: T2 => Pretty, + s3: Shrink[T3], pp3: T3 => Pretty, + s4: Shrink[T4], pp4: T4 => Pretty, + s5: Shrink[T5], pp5: T5 => Pretty, + s6: Shrink[T6], pp6: T6 => Pretty + ): Prop = forAll(g1)(t => forAll(g2,g3,g4,g5,g6)(f(t, _:T2, _:T3, _:T4, _:T5, _:T6))) + + /** Universal quantifier for seven explicit generators. Shrinks failed arguments + * with the default shrink function for the type */ + def forAll[T1,T2,T3,T4,T5,T6,T7,P]( + g1: Gen[T1], g2: Gen[T2], g3: Gen[T3], g4: Gen[T4], g5: Gen[T5], g6: Gen[T6], g7: Gen[T7])( + f: (T1,T2,T3,T4,T5,T6,T7) => P)(implicit + p: P => Prop, + s1: Shrink[T1], pp1: T1 => Pretty, + s2: Shrink[T2], pp2: T2 => Pretty, + s3: Shrink[T3], pp3: T3 => Pretty, + s4: Shrink[T4], pp4: T4 => Pretty, + s5: Shrink[T5], pp5: T5 => Pretty, + s6: Shrink[T6], pp6: T6 => Pretty, + s7: Shrink[T7], pp7: T7 => Pretty + ): Prop = forAll(g1)(t => forAll(g2,g3,g4,g5,g6,g7)(f(t, _:T2, _:T3, _:T4, _:T5, _:T6, _:T7))) + + /** Universal quantifier for eight explicit generators. Shrinks failed arguments + * with the default shrink function for the type */ + def forAll[T1,T2,T3,T4,T5,T6,T7,T8,P]( + g1: Gen[T1], g2: Gen[T2], g3: Gen[T3], g4: Gen[T4], g5: Gen[T5], g6: Gen[T6], g7: Gen[T7], g8: Gen[T8])( + f: (T1,T2,T3,T4,T5,T6,T7,T8) => P)(implicit + p: P => Prop, + s1: Shrink[T1], pp1: T1 => Pretty, + s2: Shrink[T2], pp2: T2 => Pretty, + s3: Shrink[T3], pp3: T3 => Pretty, + s4: Shrink[T4], pp4: T4 => Pretty, + s5: Shrink[T5], pp5: T5 => Pretty, + s6: Shrink[T6], pp6: T6 => Pretty, + s7: Shrink[T7], pp7: T7 => Pretty, + s8: Shrink[T8], pp8: T8 => Pretty + ): Prop = forAll(g1)(t => forAll(g2,g3,g4,g5,g6,g7,g8)(f(t, _:T2, _:T3, _:T4, _:T5, _:T6, _:T7, _:T8))) + + /** Converts a function into a universally quantified property */ + def forAll[A1,P] ( + f: A1 => P)(implicit + p: P => Prop, + a1: Arbitrary[A1], s1: Shrink[A1], pp1: A1 => Pretty + ): Prop = forAllShrink(arbitrary[A1],shrink[A1])(f andThen p) + + /** Converts a function into a universally quantified property */ + def forAll[A1,A2,P] ( + f: (A1,A2) => P)(implicit + p: P => Prop, + a1: Arbitrary[A1], s1: Shrink[A1], pp1: A1 => Pretty, + a2: Arbitrary[A2], s2: Shrink[A2], pp2: A2 => Pretty + ): Prop = forAll((a: A1) => forAll(f(a, _:A2))) + + /** Converts a function into a universally quantified property */ + def forAll[A1,A2,A3,P] ( + f: (A1,A2,A3) => P)(implicit + p: P => Prop, + a1: Arbitrary[A1], s1: Shrink[A1], pp1: A1 => Pretty, + a2: Arbitrary[A2], s2: Shrink[A2], pp2: A2 => Pretty, + a3: Arbitrary[A3], s3: Shrink[A3], pp3: A3 => Pretty + ): Prop = forAll((a: A1) => forAll(f(a, _:A2, _:A3))) + + /** Converts a function into a universally quantified property */ + def forAll[A1,A2,A3,A4,P] ( + f: (A1,A2,A3,A4) => P)(implicit + p: P => Prop, + a1: Arbitrary[A1], s1: Shrink[A1], pp1: A1 => Pretty, + a2: Arbitrary[A2], s2: Shrink[A2], pp2: A2 => Pretty, + a3: Arbitrary[A3], s3: Shrink[A3], pp3: A3 => Pretty, + a4: Arbitrary[A4], s4: Shrink[A4], pp4: A4 => Pretty + ): Prop = forAll((a: A1) => forAll(f(a, _:A2, _:A3, _:A4))) + + /** Converts a function into a universally quantified property */ + def forAll[A1,A2,A3,A4,A5,P] ( + f: (A1,A2,A3,A4,A5) => P)(implicit + p: P => Prop, + a1: Arbitrary[A1], s1: Shrink[A1], pp1: A1 => Pretty, + a2: Arbitrary[A2], s2: Shrink[A2], pp2: A2 => Pretty, + a3: Arbitrary[A3], s3: Shrink[A3], pp3: A3 => Pretty, + a4: Arbitrary[A4], s4: Shrink[A4], pp4: A4 => Pretty, + a5: Arbitrary[A5], s5: Shrink[A5], pp5: A5 => Pretty + ): Prop = forAll((a: A1) => forAll(f(a, _:A2, _:A3, _:A4, _:A5))) + + /** Converts a function into a universally quantified property */ + def forAll[A1,A2,A3,A4,A5,A6,P] ( + f: (A1,A2,A3,A4,A5,A6) => P)(implicit + p: P => Prop, + a1: Arbitrary[A1], s1: Shrink[A1], pp1: A1 => Pretty, + a2: Arbitrary[A2], s2: Shrink[A2], pp2: A2 => Pretty, + a3: Arbitrary[A3], s3: Shrink[A3], pp3: A3 => Pretty, + a4: Arbitrary[A4], s4: Shrink[A4], pp4: A4 => Pretty, + a5: Arbitrary[A5], s5: Shrink[A5], pp5: A5 => Pretty, + a6: Arbitrary[A6], s6: Shrink[A6], pp6: A6 => Pretty + ): Prop = forAll((a: A1) => forAll(f(a, _:A2, _:A3, _:A4, _:A5, _:A6))) + + /** Converts a function into a universally quantified property */ + def forAll[A1,A2,A3,A4,A5,A6,A7,P] ( + f: (A1,A2,A3,A4,A5,A6,A7) => P)(implicit + p: P => Prop, + a1: Arbitrary[A1], s1: Shrink[A1], pp1: A1 => Pretty, + a2: Arbitrary[A2], s2: Shrink[A2], pp2: A2 => Pretty, + a3: Arbitrary[A3], s3: Shrink[A3], pp3: A3 => Pretty, + a4: Arbitrary[A4], s4: Shrink[A4], pp4: A4 => Pretty, + a5: Arbitrary[A5], s5: Shrink[A5], pp5: A5 => Pretty, + a6: Arbitrary[A6], s6: Shrink[A6], pp6: A6 => Pretty, + a7: Arbitrary[A7], s7: Shrink[A7], pp7: A7 => Pretty + ): Prop = forAll((a: A1) => forAll(f(a, _:A2, _:A3, _:A4, _:A5, _:A6, _:A7))) + + /** Converts a function into a universally quantified property */ + def forAll[A1,A2,A3,A4,A5,A6,A7,A8,P] ( + f: (A1,A2,A3,A4,A5,A6,A7,A8) => P)(implicit + p: P => Prop, + a1: Arbitrary[A1], s1: Shrink[A1], pp1: A1 => Pretty, + a2: Arbitrary[A2], s2: Shrink[A2], pp2: A2 => Pretty, + a3: Arbitrary[A3], s3: Shrink[A3], pp3: A3 => Pretty, + a4: Arbitrary[A4], s4: Shrink[A4], pp4: A4 => Pretty, + a5: Arbitrary[A5], s5: Shrink[A5], pp5: A5 => Pretty, + a6: Arbitrary[A6], s6: Shrink[A6], pp6: A6 => Pretty, + a7: Arbitrary[A7], s7: Shrink[A7], pp7: A7 => Pretty, + a8: Arbitrary[A8], s8: Shrink[A8], pp8: A8 => Pretty + ): Prop = forAll((a: A1) => forAll(f(a, _:A2, _:A3, _:A4, _:A5, _:A6, _:A7, _:A8))) + +} diff --git a/src/scalacheck/org/scalacheck/Properties.scala b/src/scalacheck/org/scalacheck/Properties.scala new file mode 100644 index 0000000000..bb2fe77c47 --- /dev/null +++ b/src/scalacheck/org/scalacheck/Properties.scala @@ -0,0 +1,76 @@ +/*-------------------------------------------------------------------------*\ +** ScalaCheck ** +** Copyright (c) 2007-2010 Rickard Nilsson. All rights reserved. ** +** http://www.scalacheck.org ** +** ** +** This software is released under the terms of the Revised BSD License. ** +** There is NO WARRANTY. See the file LICENSE for the full text. ** +\*-------------------------------------------------------------------------*/ + +package org.scalacheck + +/** Represents a collection of properties, with convenient methods + * for checking all properties at once. This class is itself a property, which + * holds if and only if all of the contained properties hold. + * <p>Properties are added in the following way:</p> + * + * <p> + * <code> + * object MyProps extends Properties("MyProps") { + * property("myProp1") = forAll { (n:Int, m:Int) => + * n+m == m+n + * } + * + * property("myProp2") = ((0/1) throws classOf[ArithmeticException]) + * } + */ +class Properties(val name: String) extends Prop { + + import Test.cmdLineParser.{Success, NoSuccess} + + private val props = new scala.collection.mutable.ListBuffer[(String,Prop)] + + /** Returns one property which holds if and only if all of the + * properties in this property collection hold */ + private def oneProperty: Prop = Prop.all((properties map (_._2)):_*) + + /** Returns all properties of this collection in a list of name/property + * pairs. */ + def properties: Seq[(String,Prop)] = props + + def apply(p: Prop.Params) = oneProperty(p) + + /** Convenience method that checks the properties with the given parameters + * and reports the result on the console. If you need to get the results + * from the test use the <code>check</code> methods in <code>Test</code> + * instead. */ + override def check(prms: Test.Params): Unit = Test.checkProperties( + prms copy (testCallback = ConsoleReporter(1) chain prms.testCallback), this + ) + + /** Convenience method that checks the properties and reports the + * result on the console. If you need to get the results from the test use + * the <code>check</code> methods in <code>Test</code> instead. */ + override def check: Unit = check(Test.Params()) + + /** Convenience method that makes it possible to use a this instance + * as an application that checks itself on execution */ + override def main(args: Array[String]): Unit = + Test.cmdLineParser.parseParams(args) match { + case Success(params, _) => Test.checkProperties(params, this) + case e: NoSuccess => + println("Incorrect options:"+"\n"+e+"\n") + Test.cmdLineParser.printHelp + } + + /** Adds all properties from another property collection to this one. */ + def include(ps: Properties) = for((n,p) <- ps.properties) property(n) = p + + /** Used for specifying properties. Usage: + * <code>property("myProp") = ...</code> */ + class PropertySpecifier() { + def update(propName: String, p: Prop) = props += ((name+"."+propName, p)) + } + + lazy val property = new PropertySpecifier() +} diff --git a/src/scalacheck/org/scalacheck/Shrink.scala b/src/scalacheck/org/scalacheck/Shrink.scala new file mode 100644 index 0000000000..70ab5f6d23 --- /dev/null +++ b/src/scalacheck/org/scalacheck/Shrink.scala @@ -0,0 +1,208 @@ +/*-------------------------------------------------------------------------*\ +** ScalaCheck ** +** Copyright (c) 2007-2010 Rickard Nilsson. All rights reserved. ** +** http://www.scalacheck.org ** +** ** +** This software is released under the terms of the Revised BSD License. ** +** There is NO WARRANTY. See the file LICENSE for the full text. ** +\*-------------------------------------------------------------------------*/ + +package org.scalacheck + +import util.Buildable +import scala.collection.{ JavaConversions => jcl } + +sealed abstract class Shrink[T] { + def shrink(x: T): Stream[T] +} + +object Shrink { + + import Stream.{cons, empty} + import scala.collection._ + import java.util.ArrayList + + /** Interleaves to streams */ + private def interleave[T](xs: Stream[T], ys: Stream[T]): Stream[T] = + if(xs.isEmpty) ys + else if(ys.isEmpty) xs + else Stream(xs.head, ys.head) append interleave(xs.tail, ys.tail) + + /** Shrink instance factory */ + def apply[T](s: T => Stream[T]): Shrink[T] = new Shrink[T] { + override def shrink(x: T) = s(x) + } + + /** Shrink a value */ + def shrink[T](x: T)(implicit s: Shrink[T]): Stream[T] = s.shrink(x) + + /** Default shrink instance */ + implicit def shrinkAny[T]: Shrink[T] = Shrink(x => empty) + + /** Shrink instance of container */ + implicit def shrinkContainer[C[_],T](implicit v: C[T] => Traversable[T], s: Shrink[T], + b: Buildable[T,C] + ): Shrink[C[T]] = Shrink { xs: C[T] => + + def removeChunks(n: Int, xs: Stream[T]): Stream[Stream[T]] = + if(xs.isEmpty) empty + else if(xs.tail.isEmpty) cons(empty, empty) + else { + val n1 = n / 2 + val n2 = n - n1 + lazy val xs1 = xs.take(n1) + lazy val xs2 = xs.drop(n1) + lazy val xs3 = + for(ys1 <- removeChunks(n1,xs1) if !ys1.isEmpty) yield ys1 append xs2 + lazy val xs4 = + for(ys2 <- removeChunks(n2,xs2) if !ys2.isEmpty) yield xs1 append ys2 + + cons(xs1, cons(xs2, interleave(xs3,xs4))) + } + + def shrinkOne(zs: Stream[T]): Stream[Stream[T]] = + if(zs.isEmpty) empty + else { + val x = zs.head + val xs = zs.tail + (for(y <- shrink(x)) yield cons(y,xs)) append + (for(ys <- shrinkOne(xs)) yield cons(x,ys)) + } + + val ys = v(xs) + val zs = ys.toStream + removeChunks(ys.size,zs).append(shrinkOne(zs)).map(b.fromIterable) + + } + + /** Shrink instance of integer */ + implicit lazy val shrinkInt: Shrink[Int] = Shrink { n => + + def halfs(n: Int): Stream[Int] = + if(n == 0) empty else cons(n, halfs(n/2)) + + if(n == 0) empty else { + val ns = halfs(n/2).map(n - _) + cons(0, interleave(ns, ns.map(-1 * _))) + } + } + + /** Shrink instance of String */ + implicit lazy val shrinkString: Shrink[String] = Shrink { s => + shrinkContainer[List,Char].shrink(s.toList).map(_.mkString) + } + + /** Shrink instance of Option */ + implicit def shrinkOption[T](implicit s: Shrink[T]): Shrink[Option[T]] = + Shrink { + case None => empty + case Some(x) => cons(None, for(y <- shrink(x)) yield Some(y)) + } + + /** Shrink instance of 2-tuple */ + implicit def shrinkTuple2[T1,T2](implicit + s1: Shrink[T1], s2: Shrink[T2] + ): Shrink[(T1,T2)] = + Shrink { case (t1,t2) => + (for(x1 <- shrink(t1)) yield (x1, t2)) append + (for(x2 <- shrink(t2)) yield (t1, x2)) + } + + /** Shrink instance of 3-tuple */ + implicit def shrinkTuple3[T1,T2,T3](implicit + s1: Shrink[T1], s2: Shrink[T2], s3: Shrink[T3] + ): Shrink[(T1,T2,T3)] = + Shrink { case (t1,t2,t3) => + (for(x1 <- shrink(t1)) yield (x1, t2, t3)) append + (for(x2 <- shrink(t2)) yield (t1, x2, t3)) append + (for(x3 <- shrink(t3)) yield (t1, t2, x3)) + } + + /** Shrink instance of 4-tuple */ + implicit def shrinkTuple4[T1,T2,T3,T4](implicit + s1: Shrink[T1], s2: Shrink[T2], s3: Shrink[T3], s4: Shrink[T4] + ): Shrink[(T1,T2,T3,T4)] = + Shrink { case (t1,t2,t3,t4) => + (for(x1 <- shrink(t1)) yield (x1, t2, t3, t4)) append + (for(x2 <- shrink(t2)) yield (t1, x2, t3, t4)) append + (for(x3 <- shrink(t3)) yield (t1, t2, x3, t4)) append + (for(x4 <- shrink(t4)) yield (t1, t2, t3, x4)) + } + + /** Shrink instance of 5-tuple */ + implicit def shrinkTuple5[T1,T2,T3,T4,T5](implicit + s1: Shrink[T1], s2: Shrink[T2], s3: Shrink[T3], s4: Shrink[T4], + s5: Shrink[T5] + ): Shrink[(T1,T2,T3,T4,T5)] = + Shrink { case (t1,t2,t3,t4,t5) => + (for(x1 <- shrink(t1)) yield (x1, t2, t3, t4, t5)) append + (for(x2 <- shrink(t2)) yield (t1, x2, t3, t4, t5)) append + (for(x3 <- shrink(t3)) yield (t1, t2, x3, t4, t5)) append + (for(x4 <- shrink(t4)) yield (t1, t2, t3, x4, t5)) append + (for(x5 <- shrink(t5)) yield (t1, t2, t3, t4, x5)) + } + + /** Shrink instance of 6-tuple */ + implicit def shrinkTuple6[T1,T2,T3,T4,T5,T6](implicit + s1: Shrink[T1], s2: Shrink[T2], s3: Shrink[T3], s4: Shrink[T4], + s5: Shrink[T5], s6: Shrink[T6] + ): Shrink[(T1,T2,T3,T4,T5,T6)] = + Shrink { case (t1,t2,t3,t4,t5,t6) => + (for(x1 <- shrink(t1)) yield (x1, t2, t3, t4, t5, t6)) append + (for(x2 <- shrink(t2)) yield (t1, x2, t3, t4, t5, t6)) append + (for(x3 <- shrink(t3)) yield (t1, t2, x3, t4, t5, t6)) append + (for(x4 <- shrink(t4)) yield (t1, t2, t3, x4, t5, t6)) append + (for(x5 <- shrink(t5)) yield (t1, t2, t3, t4, x5, t6)) append + (for(x6 <- shrink(t6)) yield (t1, t2, t3, t4, t5, x6)) + } + + /** Shrink instance of 7-tuple */ + implicit def shrinkTuple7[T1,T2,T3,T4,T5,T6,T7](implicit + s1: Shrink[T1], s2: Shrink[T2], s3: Shrink[T3], s4: Shrink[T4], + s5: Shrink[T5], s6: Shrink[T6], s7: Shrink[T7] + ): Shrink[(T1,T2,T3,T4,T5,T6,T7)] = + Shrink { case (t1,t2,t3,t4,t5,t6,t7) => + (for(x1 <- shrink(t1)) yield (x1, t2, t3, t4, t5, t6, t7)) append + (for(x2 <- shrink(t2)) yield (t1, x2, t3, t4, t5, t6, t7)) append + (for(x3 <- shrink(t3)) yield (t1, t2, x3, t4, t5, t6, t7)) append + (for(x4 <- shrink(t4)) yield (t1, t2, t3, x4, t5, t6, t7)) append + (for(x5 <- shrink(t5)) yield (t1, t2, t3, t4, x5, t6, t7)) append + (for(x6 <- shrink(t6)) yield (t1, t2, t3, t4, t5, x6, t7)) append + (for(x7 <- shrink(t7)) yield (t1, t2, t3, t4, t5, t6, x7)) + } + + /** Shrink instance of 8-tuple */ + implicit def shrinkTuple8[T1,T2,T3,T4,T5,T6,T7,T8](implicit + s1: Shrink[T1], s2: Shrink[T2], s3: Shrink[T3], s4: Shrink[T4], + s5: Shrink[T5], s6: Shrink[T6], s7: Shrink[T7], s8: Shrink[T8] + ): Shrink[(T1,T2,T3,T4,T5,T6,T7,T8)] = + Shrink { case (t1,t2,t3,t4,t5,t6,t7,t8) => + (for(x1 <- shrink(t1)) yield (x1, t2, t3, t4, t5, t6, t7, t8)) append + (for(x2 <- shrink(t2)) yield (t1, x2, t3, t4, t5, t6, t7, t8)) append + (for(x3 <- shrink(t3)) yield (t1, t2, x3, t4, t5, t6, t7, t8)) append + (for(x4 <- shrink(t4)) yield (t1, t2, t3, x4, t5, t6, t7, t8)) append + (for(x5 <- shrink(t5)) yield (t1, t2, t3, t4, x5, t6, t7, t8)) append + (for(x6 <- shrink(t6)) yield (t1, t2, t3, t4, t5, x6, t7, t8)) append + (for(x7 <- shrink(t7)) yield (t1, t2, t3, t4, t5, t6, x7, t8)) append + (for(x8 <- shrink(t8)) yield (t1, t2, t3, t4, t5, t6, t7, x8)) + } + + /** Shrink instance of 9-tuple */ + implicit def shrinkTuple9[T1,T2,T3,T4,T5,T6,T7,T8,T9](implicit + s1: Shrink[T1], s2: Shrink[T2], s3: Shrink[T3], s4: Shrink[T4], + s5: Shrink[T5], s6: Shrink[T6], s7: Shrink[T7], s8: Shrink[T8], + s9: Shrink[T9] + ): Shrink[(T1,T2,T3,T4,T5,T6,T7,T8,T9)] = + Shrink { case (t1,t2,t3,t4,t5,t6,t7,t8,t9) => + (for(x1 <- shrink(t1)) yield (x1, t2, t3, t4, t5, t6, t7, t8, t9)) append + (for(x2 <- shrink(t2)) yield (t1, x2, t3, t4, t5, t6, t7, t8, t9)) append + (for(x3 <- shrink(t3)) yield (t1, t2, x3, t4, t5, t6, t7, t8, t9)) append + (for(x4 <- shrink(t4)) yield (t1, t2, t3, x4, t5, t6, t7, t8, t9)) append + (for(x5 <- shrink(t5)) yield (t1, t2, t3, t4, x5, t6, t7, t8, t9)) append + (for(x6 <- shrink(t6)) yield (t1, t2, t3, t4, t5, x6, t7, t8, t9)) append + (for(x7 <- shrink(t7)) yield (t1, t2, t3, t4, t5, t6, x7, t8, t9)) append + (for(x8 <- shrink(t8)) yield (t1, t2, t3, t4, t5, t6, t7, x8, t9)) append + (for(x9 <- shrink(t9)) yield (t1, t2, t3, t4, t5, t6, t7, t8, x9)) + } + +} diff --git a/src/scalacheck/org/scalacheck/Test.scala b/src/scalacheck/org/scalacheck/Test.scala new file mode 100644 index 0000000000..82d9f679b6 --- /dev/null +++ b/src/scalacheck/org/scalacheck/Test.scala @@ -0,0 +1,312 @@ +/*-------------------------------------------------------------------------*\ +** ScalaCheck ** +** Copyright (c) 2007-2010 Rickard Nilsson. All rights reserved. ** +** http://www.scalacheck.org ** +** ** +** This software is released under the terms of the Revised BSD License. ** +** There is NO WARRANTY. See the file LICENSE for the full text. ** +\*-------------------------------------------------------------------------*/ + +package org.scalacheck + +object Test { + + import util.FreqMap + import scala.collection.immutable + import Prop.FM + import util.CmdLineParser + + /** Test parameters */ + case class Params( + minSuccessfulTests: Int = 100, + maxDiscardedTests: Int = 500, + minSize: Int = 0, + maxSize: Int = Gen.Params().size, + rng: java.util.Random = Gen.Params().rng, + workers: Int = 1, + testCallback: TestCallback = new TestCallback {} + ) + + /** Test statistics */ + case class Result(status: Status, succeeded: Int, discarded: Int, freqMap: FM) { + def passed = status match { + case Passed => true + case Proved(_) => true + case _ => false + } + } + + /** Test status */ + sealed trait Status + + /** ScalaCheck found enough cases for which the property holds, so the + * property is considered correct. (It is not proved correct, though). */ + case object Passed extends Status + + /** ScalaCheck managed to prove the property correct */ + sealed case class Proved(args: Prop.Args) extends Status + + /** The property was proved wrong with the given concrete arguments. */ + sealed case class Failed(args: Prop.Args, labels: Set[String]) extends Status + + /** The property test was exhausted, it wasn't possible to generate enough + * concrete arguments satisfying the preconditions to get enough passing + * property evaluations. */ + case object Exhausted extends Status + + /** An exception was raised when trying to evaluate the property with the + * given concrete arguments. */ + sealed case class PropException(args: Prop.Args, e: Throwable, + labels: Set[String]) extends Status + + /** An exception was raised when trying to generate concrete arguments + * for evaluating the property. */ + sealed case class GenException(e: Throwable) extends Status + + trait TestCallback { self => + /** Called each time a property is evaluated */ + def onPropEval(name: String, threadIdx: Int, succeeded: Int, + discarded: Int): Unit = () + + /** Called whenever a property has finished testing */ + def onTestResult(name: String, result: Result): Unit = () + + def chain(testCallback: TestCallback) = new TestCallback { + override def onPropEval(name: String, threadIdx: Int, + succeeded: Int, discarded: Int + ): Unit = { + self.onPropEval(name,threadIdx,succeeded,discarded) + testCallback.onPropEval(name,threadIdx,succeeded,discarded) + } + + override def onTestResult(name: String, result: Result): Unit = { + self.onTestResult(name,result) + testCallback.onTestResult(name,result) + } + } + } + + private def assertParams(prms: Params) = { + import prms._ + if( + minSuccessfulTests <= 0 || + maxDiscardedTests < 0 || + minSize < 0 || + maxSize < minSize || + workers <= 0 + ) throw new IllegalArgumentException("Invalid test parameters") + } + + private def secure[T](x: => T): Either[T,Throwable] = + try { Left(x) } catch { case e => Right(e) } + + private[scalacheck] lazy val cmdLineParser = new CmdLineParser { + object OptMinSuccess extends IntOpt { + val default = Test.Params().minSuccessfulTests + val names = Set("minSuccessfulTests", "s") + val help = "Number of tests that must succeed in order to pass a property" + } + object OptMaxDiscarded extends IntOpt { + val default = Test.Params().maxDiscardedTests + val names = Set("maxDiscardedTests", "d") + val help = + "Number of tests that can be discarded before ScalaCheck stops " + + "testing a property" + } + object OptMinSize extends IntOpt { + val default = Test.Params().minSize + val names = Set("minSize", "n") + val help = "Minimum data generation size" + } + object OptMaxSize extends IntOpt { + val default = Test.Params().maxSize + val names = Set("maxSize", "x") + val help = "Maximum data generation size" + } + object OptWorkers extends IntOpt { + val default = Test.Params().workers + val names = Set("workers", "w") + val help = "Number of threads to execute in parallel for testing" + } + object OptVerbosity extends IntOpt { + val default = 1 + val names = Set("verbosity", "v") + val help = "Verbosity level" + } + + val opts = Set[Opt[_]]( + OptMinSuccess, OptMaxDiscarded, OptMinSize, + OptMaxSize, OptWorkers, OptVerbosity + ) + + def parseParams(args: Array[String]) = parseArgs(args) { + optMap => Test.Params( + optMap(OptMinSuccess), + optMap(OptMaxDiscarded), + optMap(OptMinSize), + optMap(OptMaxSize), + Test.Params().rng, + optMap(OptWorkers), + ConsoleReporter(optMap(OptVerbosity)) + ) + } + } + + /** Tests a property with the given testing parameters, and returns + * the test results. */ + def check(prms: Params, p: Prop): Result = { + import prms._ + import actors.Futures.future + + assertParams(prms) + if(workers > 1) + assert(!p.isInstanceOf[Commands], "Commands cannot be checked multi-threaded") + + val iterations = minSuccessfulTests / workers + val sizeStep = (maxSize-minSize) / (minSuccessfulTests: Float) + var stop = false + + def worker(workerdIdx: Int) = future { + var n = 0 + var d = 0 + var size = workerdIdx*sizeStep + var res: Result = null + var fm = FreqMap.empty[immutable.Set[Any]] + while(!stop && res == null && n < iterations) { + val propPrms = Prop.Params(Gen.Params(size.round, prms.rng), fm) + secure(p(propPrms)) match { + case Right(e) => res = + Result(GenException(e), n, d, FreqMap.empty[immutable.Set[Any]]) + case Left(propRes) => + fm = + if(propRes.collected.isEmpty) fm + else fm + propRes.collected + propRes.status match { + case Prop.Undecided => + d += 1 + testCallback.onPropEval("", workerdIdx, n, d) + if(d >= maxDiscardedTests) res = Result(Exhausted, n, d, fm) + case Prop.True => + n += 1 + testCallback.onPropEval("", workerdIdx, n, d) + case Prop.Proof => + n += 1 + res = Result(Proved(propRes.args), n, d, fm) + case Prop.False => res = + Result(Failed(propRes.args, propRes.labels), n, d, fm) + case Prop.Exception(e) => res = + Result(PropException(propRes.args, e, propRes.labels), n, d, fm) + } + } + size += sizeStep + } + if(res != null) stop = true + else res = Result(Passed, n, d, fm) + res + } + + def mergeResults(r1: () => Result, r2: () => Result) = r1() match { + case Result(Passed, s1, d1, fm1) => r2() match { + case Result(Passed, s2, d2, fm2) if d1+d2 >= maxDiscardedTests => + () => Result(Exhausted, s1+s2, d1+d2, fm1++fm2) + case Result(st, s2, d2, fm2) => + () => Result(st, s1+s2, d1+d2, fm1++fm2) + } + case r => () => r + } + + val results = for(i <- 0 until workers) yield worker(i) + val r = results.reduceLeft(mergeResults)() + stop = true + results foreach (_.apply()) + prms.testCallback.onTestResult("", r) + r + } + + def checkProperties(prms: Params, ps: Properties): Seq[(String,Result)] = + ps.properties.map { case (name,p) => + val testCallback = new TestCallback { + override def onPropEval(n: String, t: Int, s: Int, d: Int) = + prms.testCallback.onPropEval(name,t,s,d) + override def onTestResult(n: String, r: Result) = + prms.testCallback.onTestResult(name,r) + } + val res = check(prms copy (testCallback = testCallback), p) + (name,res) + } + + + // Deprecated methods // + + /** Default testing parameters + * @deprecated Use <code>Test.Params()</code> instead */ + @deprecated("Use Test.Params() instead") + val defaultParams = Params() + + /** Property evaluation callback. Takes number of passed and + * discarded tests, respectively */ + @deprecated("(v1.8)") + type PropEvalCallback = (Int,Int) => Unit + + /** Property evaluation callback. Takes property name, and number of passed + * and discarded tests, respectively */ + @deprecated("(v1.8)") + type NamedPropEvalCallback = (String,Int,Int) => Unit + + /** Test callback. Takes property name, and test results. */ + @deprecated("(v1.8)") + type TestResCallback = (String,Result) => Unit + + /** @deprecated (v1.8) Use <code>check(prms.copy(testCallback = myCallback), p)</code> instead. */ + @deprecated("(v1.8) Use check(prms.copy(testCallback = myCallback), p) instead") + def check(prms: Params, p: Prop, propCallb: PropEvalCallback): Result = { + val testCallback = new TestCallback { + override def onPropEval(n: String, t: Int, s: Int, d: Int) = propCallb(s,d) + } + check(prms copy (testCallback = testCallback), p) + } + + /** Tests a property and prints results to the console. The + * <code>maxDiscarded</code> parameter specifies how many + * discarded tests that should be allowed before ScalaCheck + * @deprecated (v1.8) Use <code>check(Params(maxDiscardedTests = n, testCallback = ConsoleReporter()), p)</code> instead. */ + @deprecated("(v1.8) Use check(Params(maxDiscardedTests = n, testCallback = ConsoleReporter()), p) instead.") + def check(p: Prop, maxDiscarded: Int): Result = + check(Params(maxDiscardedTests = maxDiscarded, testCallback = ConsoleReporter()), p) + + /** Tests a property and prints results to the console + * @deprecated (v1.8) Use <code>check(Params(testCallback = ConsoleReporter()), p)</code> instead. */ + @deprecated("(v1.8) Use check(Params(testCallback = ConsoleReporter()), p) instead.") + def check(p: Prop): Result = check(Params(testCallback = ConsoleReporter()), p) + + /** Tests all properties with the given testing parameters, and returns + * the test results. <code>f</code> is a function which is called each + * time a property is evaluted. <code>g</code> is a function called each + * time a property has been fully tested. + * @deprecated (v1.8) Use <code>checkProperties(prms.copy(testCallback = myCallback), ps)</code> instead. */ + @deprecated("(v1.8) Use checkProperties(prms.copy(testCallback = myCallback), ps) instead.") + def checkProperties(ps: Properties, prms: Params, + propCallb: NamedPropEvalCallback, testCallb: TestResCallback + ): Seq[(String,Result)] = { + val testCallback = new TestCallback { + override def onPropEval(n: String, t: Int, s: Int, d: Int) = propCallb(n,s,d) + override def onTestResult(n: String, r: Result) = testCallb(n,r) + } + checkProperties(prms copy (testCallback = testCallback), ps) + } + + /** Tests all properties with the given testing parameters, and returns + * the test results. + * @deprecated (v1.8) Use checkProperties(prms, ps) instead */ + @deprecated("(v1.8) Use checkProperties(prms, ps) instead") + def checkProperties(ps: Properties, prms: Params): Seq[(String,Result)] = + checkProperties(ps, prms, (n,s,d) => (), (n,s) => ()) + + /** Tests all properties with default testing parameters, and returns + * the test results. The results are also printed on the console during + * testing. + * @deprecated (v1.8) Use <code>checkProperties(Params(), ps)</code> instead. */ + @deprecated("(v1.8) Use checkProperties(Params(), ps) instead.") + def checkProperties(ps: Properties): Seq[(String,Result)] = + checkProperties(Params(), ps) +} diff --git a/src/scalacheck/org/scalacheck/util/Buildable.scala b/src/scalacheck/org/scalacheck/util/Buildable.scala new file mode 100644 index 0000000000..a41448ee94 --- /dev/null +++ b/src/scalacheck/org/scalacheck/util/Buildable.scala @@ -0,0 +1,63 @@ +/*-------------------------------------------------------------------------*\ +** ScalaCheck ** +** Copyright (c) 2007-2010 Rickard Nilsson. All rights reserved. ** +** http://www.scalacheck.org ** +** ** +** This software is released under the terms of the Revised BSD License. ** +** There is NO WARRANTY. See the file LICENSE for the full text. ** +\*-------------------------------------------------------------------------*/ + +package org.scalacheck.util + +import scala.collection._ + +trait Buildable[T,C[_]] { + def builder: mutable.Builder[T,C[T]] + def fromIterable(it: Traversable[T]): C[T] = { + val b = builder + b ++= it + b.result() + } +} + +object Buildable { + + implicit def buildableList[T] = new Buildable[T,List] { + def builder = new mutable.ListBuffer[T] + } + + implicit def buildableStream[T] = new Buildable[T,Stream] { + def builder = (new mutable.ListBuffer[T]).mapResult(_.toStream) + } + + implicit def buildableArray[T](implicit cm: ClassManifest[T]) = + new Buildable[T,Array] { + def builder = mutable.ArrayBuilder.make[T] + } + + implicit def buildableMutableSet[T] = new Buildable[T,mutable.Set] { + def builder = new mutable.SetBuilder(mutable.Set.empty[T]) + } + + implicit def buildableImmutableSet[T] = new Buildable[T,immutable.Set] { + def builder = new mutable.SetBuilder(immutable.Set.empty[T]) + } + + implicit def buildableSet[T] = new Buildable[T,Set] { + def builder = new mutable.SetBuilder(Set.empty[T]) + } + + import java.util.ArrayList + implicit def buildableArrayList[T] = new Buildable[T,ArrayList] { + def builder = new mutable.Builder[T,ArrayList[T]] { + val al = new ArrayList[T] + def +=(x: T) = { + al.add(x) + this + } + def clear() = al.clear() + def result() = al + } + } + +} diff --git a/src/scalacheck/org/scalacheck/util/CmdLineParser.scala b/src/scalacheck/org/scalacheck/util/CmdLineParser.scala new file mode 100644 index 0000000000..5fb572ac2d --- /dev/null +++ b/src/scalacheck/org/scalacheck/util/CmdLineParser.scala @@ -0,0 +1,94 @@ +/*-------------------------------------------------------------------------*\ +** ScalaCheck ** +** Copyright (c) 2007-2010 Rickard Nilsson. All rights reserved. ** +** http://www.scalacheck.org ** +** ** +** This software is released under the terms of the Revised BSD License. ** +** There is NO WARRANTY. See the file LICENSE for the full text. ** +\*-------------------------------------------------------------------------*/ + +package org.scalacheck.util + +import scala.util.parsing.combinator.Parsers +import scala.util.parsing.input.Reader +import scala.util.parsing.input.Position +import scala.collection.Set +import org.scalacheck.Test + +trait CmdLineParser extends Parsers { + + type Elem = String + + trait Opt[+T] { + val default: T + val names: Set[String] + val help: String + } + trait Flag extends Opt[Unit] + trait IntOpt extends Opt[Int] + trait StrOpt extends Opt[String] + + class OptMap { + private val opts = new collection.mutable.HashMap[Opt[_], Any] + def apply(flag: Flag): Boolean = opts.contains(flag) + def apply[T](opt: Opt[T]): T = opts.get(opt) match { + case None => opt.default + case Some(v) => v.asInstanceOf[T] + } + def update[T](opt: Opt[T], optVal: T) = opts.update(opt, optVal) + } + + val opts: Set[Opt[_]] + + private class ArgsReader(args: Array[String], i: Int) extends Reader[String] { + val pos = new Position { + val column = (args take i).foldLeft(1)(_ + _.length + 1) + val line = 1 + val lineContents = args.mkString(" ") + } + val atEnd = i >= args.length + def first = if(atEnd) null else args(i) + def rest = if(atEnd) this else new ArgsReader(args, i+1) + } + + private def getOpt(s: String) = { + if(s == null || s.length == 0 || s.charAt(0) != '-') None + else opts.find(_.names.contains(s.drop(1))) + } + + private val opt: Parser[Opt[Any]] = accept("option name", { + case s if getOpt(s).isDefined => getOpt(s).get + }) + + private val strVal: Parser[String] = accept("string", { + case s if s != null => s + }) + + private val intVal: Parser[Int] = accept("integer", { + case s if s != null && s.length > 0 && s.forall(_.isDigit) => s.toInt + }) + + private case class OptVal[T](o: Opt[T], v: T) + + private val optVal: Parser[OptVal[Any]] = opt into { + case o: Flag => success(OptVal(o, ())) + case o: IntOpt => intVal ^^ (v => OptVal(o, v)) + case o: StrOpt => strVal ^^ (v => OptVal(o, v)) + } + + val options: Parser[OptMap] = rep(optVal) ^^ { xs => + val map = new OptMap + xs.foreach { case OptVal(o,v) => map(o) = v } + map + } + + def printHelp = { + println("Available options:") + opts.foreach { opt => + println(" " + opt.names.map("-"+_).mkString(", ") + ": " + opt.help) + } + } + + def parseArgs[T](args: Array[String])(f: OptMap => T) = + phrase(options map f)(new ArgsReader(args,0)) +} diff --git a/src/scalacheck/org/scalacheck/util/FreqMap.scala b/src/scalacheck/org/scalacheck/util/FreqMap.scala new file mode 100644 index 0000000000..11cdb3ec28 --- /dev/null +++ b/src/scalacheck/org/scalacheck/util/FreqMap.scala @@ -0,0 +1,65 @@ +/*-------------------------------------------------------------------------*\ +** ScalaCheck ** +** Copyright (c) 2007-2010 Rickard Nilsson. All rights reserved. ** +** http://www.scalacheck.org ** +** ** +** This software is released under the terms of the Revised BSD License. ** +** There is NO WARRANTY. See the file LICENSE for the full text. ** +\*-------------------------------------------------------------------------*/ + +package org.scalacheck.util + +trait FreqMap[T] { + protected val underlying: scala.collection.immutable.Map[T,Int] + val total: Int + + def +(t: T) = new FreqMap[T] { + private val n = FreqMap.this.underlying.get(t) match { + case None => 1 + case Some(n) => n+1 + } + val underlying = FreqMap.this.underlying + (t -> n) + val total = FreqMap.this.total + 1 + } + + def -(t: T) = new FreqMap[T] { + val underlying = FreqMap.this.underlying.get(t) match { + case None => FreqMap.this.underlying + case Some(n) => FreqMap.this.underlying + (t -> (n-1)) + } + val total = FreqMap.this.total + 1 + } + + def ++(fm: FreqMap[T]) = new FreqMap[T] { + private val keys = FreqMap.this.underlying.keySet ++ fm.underlying.keySet + private val mappings = keys.toStream.map { x => + (x, fm.getCount(x).getOrElse(0) + FreqMap.this.getCount(x).getOrElse(0)) + } + val underlying = scala.collection.immutable.Map(mappings: _*) + val total = FreqMap.this.total + fm.total + } + + def --(fm: FreqMap[T]) = new FreqMap[T] { + val underlying = FreqMap.this.underlying transform { + case (x,n) => n - fm.getCount(x).getOrElse(0) + } + lazy val total = (0 /: underlying.valuesIterator) (_ + _) + } + + def getCount(t: T) = underlying.get(t) + + def getCounts: List[(T,Int)] = underlying.toList.sortBy(-_._2) + + def getRatio(t: T) = for(c <- getCount(t)) yield (c: Float)/total + + def getRatios = for((t,c) <- getCounts) yield (t, (c: Float)/total) + + override def toString = underlying.toString +} + +object FreqMap { + def empty[T] = new FreqMap[T] { + val underlying = scala.collection.immutable.Map.empty[T,Int] + val total = 0 + } +} diff --git a/src/scalacheck/org/scalacheck/util/StdRand.scala b/src/scalacheck/org/scalacheck/util/StdRand.scala new file mode 100644 index 0000000000..ccdbbea301 --- /dev/null +++ b/src/scalacheck/org/scalacheck/util/StdRand.scala @@ -0,0 +1,12 @@ +/*-------------------------------------------------------------------------*\ +** ScalaCheck ** +** Copyright (c) 2007-2010 Rickard Nilsson. All rights reserved. ** +** http://www.scalacheck.org ** +** ** +** This software is released under the terms of the Revised BSD License. ** +** There is NO WARRANTY. See the file LICENSE for the full text. ** +\*-------------------------------------------------------------------------*/ + +package org.scalacheck.util + +object StdRand extends java.util.Random diff --git a/test/benchmarks/source.list b/test/benchmarks/source.list index 88d2b257b2..9ca7a406dd 100644 --- a/test/benchmarks/source.list +++ b/test/benchmarks/source.list @@ -23,6 +23,7 @@ src/scala/collection/parallel/benchmarks/parallel_array/PatchHalf.scala src/scala/collection/parallel/benchmarks/parallel_array/DiffHalf.scala src/scala/collection/parallel/benchmarks/parallel_array/TakeMany.scala src/scala/collection/parallel/benchmarks/parallel_array/PartialMapLight.scala +src/scala/collection/parallel/benchmarks/parallel_array/ScanLight.scala src/scala/collection/parallel/benchmarks/parallel_array/Reverse.scala src/scala/collection/parallel/benchmarks/parallel_array/SpanLight.scala src/scala/collection/parallel/benchmarks/parallel_array/PlusPlus.scala @@ -62,7 +63,10 @@ src/scala/collection/parallel/benchmarks/generic/Dummy.scala src/scala/collection/parallel/benchmarks/parallel_range/RangeBenches.scala src/scala/collection/parallel/benchmarks/Bench.scala src/scala/collection/parallel/benchmarks/hashtries/Foreach.scala +src/scala/collection/parallel/benchmarks/hashtries/Combine.scala +src/scala/collection/parallel/benchmarks/hashtries/MultipleCombine.scala src/scala/collection/parallel/benchmarks/hashtries/Iterate.scala src/scala/collection/parallel/benchmarks/hashtries/Construct.scala src/scala/collection/parallel/benchmarks/hashtries/IntInit.scala src/scala/collection/parallel/benchmarks/hashtries/Lookup.scala +src/scala/collection/parallel/benchmarks/hashtries/ParallelHashTries.scala diff --git a/test/benchmarks/src/scala/collection/parallel/Benchmarking.scala b/test/benchmarks/src/scala/collection/parallel/Benchmarking.scala index 89b5696f9d..2cc3abaf27 100644 --- a/test/benchmarks/src/scala/collection/parallel/Benchmarking.scala +++ b/test/benchmarks/src/scala/collection/parallel/Benchmarking.scala @@ -99,6 +99,7 @@ trait BenchmarkRegister { // parallel hash trie benchmarks register(hashtries.RefParHashTrieBenches.Reduce) register(hashtries.RefParHashTrieBenches.ReduceMedium) + register(hashtries.RefParHashTrieBenches.Reduce2) register(hashtries.RefParHashTrieBenches.Map) register(hashtries.RefParHashTrieBenches.Map2) } diff --git a/test/benchmarks/src/scala/collection/parallel/benchmarks/hashtries/ParallelHashTries.scala b/test/benchmarks/src/scala/collection/parallel/benchmarks/hashtries/ParallelHashTries.scala index bec8ba6650..c68bce93b2 100644 --- a/test/benchmarks/src/scala/collection/parallel/benchmarks/hashtries/ParallelHashTries.scala +++ b/test/benchmarks/src/scala/collection/parallel/benchmarks/hashtries/ParallelHashTries.scala @@ -21,6 +21,7 @@ trait ParHashTrieBenches[K, V] extends StandardParIterableBench[(K, V), ParHashT object Map2 extends IterableBenchCompanion { override def defaultSize = 5000 + override def comparisons = List("jhashtable", "hashtable") def benchName = "map2"; def apply(sz: Int, p: Int, w: String) = new Map2(sz, p, w) } @@ -28,7 +29,7 @@ trait ParHashTrieBenches[K, V] extends StandardParIterableBench[(K, V), ParHashT class Map2(val size: Int, val parallelism: Int, val runWhat: String) extends IterableBench with StandardParIterableBench[(K, V), ParHashTrie[K, V]] { var result: Int = 0 - def comparisonMap = collection.Map() + def comparisonMap = collection.Map("jhashtable" -> runjhashtable _, "hashtable" -> runhashtable _) def runseq = { val r = this.seqcoll.asInstanceOf[collection.immutable.HashMap[K, V]].map(operators.mapper2) result = r.size @@ -38,6 +39,29 @@ trait ParHashTrieBenches[K, V] extends StandardParIterableBench[(K, V), ParHashT //println(collection.parallel.immutable.ParHashTrie.totalcombines) //System.exit(1) } + def runjhashtable = { + val jumap = new java.util.HashMap[K, V]() + val it = this.seqcoll.iterator + while (it.hasNext) { + val p = it.next + jumap.put(p._1, p._2) + } + result = jumap.size + } + def runhashtable = { + val smap = collection.mutable.HashMap[K, V]() + val it = this.seqcoll.iterator + while (it.hasNext) { + val p = it.next + smap.put(p._1, p._2) + } + result = smap.size + } + override def reset = runWhat match { + case "jhashtable" => this.seqcoll = createSequential(size, parallelism) + case "hashtable" => this.seqcoll = createSequential(size, parallelism) + case _ => super.reset + } def companion = Map2 override def repetitionsPerRun = 50 override def printResults { @@ -46,6 +70,29 @@ trait ParHashTrieBenches[K, V] extends StandardParIterableBench[(K, V), ParHashT } } + object Reduce2 extends IterableBenchCompanion { + override def defaultSize = 50000 + override def comparisons = List("hashtable") + def benchName = "reduce2"; + def apply(sz: Int, p: Int, w: String) = new Reduce2(sz, p, w) + } + + class Reduce2(val size: Int, val parallelism: Int, val runWhat: String) + extends IterableBench with StandardParIterableBench[(K, V), ParHashTrie[K, V]] { + private var ht: collection.mutable.HashMap[K, V] = _ + def comparisonMap = collection.Map("hashtable" -> runhashtable _) + def runseq = this.seqcoll.reduceLeft(operators.reducer) + def runpar = this.parcoll.reduce(operators.reducer) + def runhashtable = ht.reduceLeft(operators.reducer) + override def reset = runWhat match { + case "hashtable" => ht = createHashTable(size) + case _ => super.reset + } + def companion = Reduce2 + } + + def createHashTable(sz: Int): collection.mutable.HashMap[K, V] + } @@ -118,4 +165,10 @@ object RefParHashTrieBenches extends ParHashTrieBenches[Dummy, Dummy] with NotBe pht } + def createHashTable(sz: Int) = { + val hm = collection.mutable.HashMap[Dummy, Dummy]() + for (i <- 0 until sz) hm.put(new Dummy(i), new Dummy(i)) + hm + } + } diff --git a/test/benchmarks/src/scala/collection/parallel/benchmarks/parallel_array/CountLight.scala b/test/benchmarks/src/scala/collection/parallel/benchmarks/parallel_array/CountLight.scala index 87eb07452f..569b304660 100644 --- a/test/benchmarks/src/scala/collection/parallel/benchmarks/parallel_array/CountLight.scala +++ b/test/benchmarks/src/scala/collection/parallel/benchmarks/parallel_array/CountLight.scala @@ -19,3 +19,4 @@ extends Resettable(sz, p, what, new Cont(_), new Array[Any](_), classOf[Cont]) { def runjsr = jsrarr.withFilter(Cont.predjsr).size def comparisonMap = collection.Map("jsr" -> runjsr _) } + diff --git a/tools/updatescalacheck b/tools/updatescalacheck new file mode 100755 index 0000000000..46112cf29d --- /dev/null +++ b/tools/updatescalacheck @@ -0,0 +1,12 @@ +# +# Updates ScalaCheck sources from ScalaCheck nightly branch +# + +# update sources +rm -r -f ../src/scalacheck +svn export https://scalacheck.googlecode.com/svn/branches/scalanightly/src/main/scala ../src/scalacheck + +# remove unneeded class +rm ../src/scalacheck/org/scalacheck/ScalaCheckFramework.scala + + |