diff options
36 files changed, 826 insertions, 435 deletions
@@ -224,6 +224,7 @@ TODO: <artifact:dependencies pathId="partest.extras.classpath" filesetId="partest.extras.fileset" versionsId="partest.extras.versions"> <dependency groupId="com.googlecode.java-diff-utils" artifactId="diffutils" version="1.3.0"/> + <dependency groupId="org.scala-tools.testing" artifactId="test-interface" version="0.5" /> </artifact:dependencies> <!-- BND support --> @@ -530,6 +531,7 @@ TODO: <pathelement location="${build-quick.dir}/classes/library"/> <pathelement location="${build-quick.dir}/classes/actors"/> <pathelement location="${build-quick.dir}/classes/scalacheck"/> + <path refid="partest.extras.classpath"/> </path> <path id="quick.scalap.build.path"> diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index e9f42ea5ce..74d6d061e3 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -51,6 +51,22 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { override def erasedTypes = true def apply(cls: IClass) = sys.error("no implementation") + // An AsmPhase starts and ends within a Run, thus the caches in question will get populated and cleared within a Run, too), SI-7422 + javaNameCache.clear() + javaNameCache ++= List( + NothingClass -> binarynme.RuntimeNothing, + RuntimeNothingClass -> binarynme.RuntimeNothing, + NullClass -> binarynme.RuntimeNull, + RuntimeNullClass -> binarynme.RuntimeNull + ) + + // unlike javaNameCache, reverseJavaName contains entries only for class symbols and their internal names. + reverseJavaName.clear() + reverseJavaName ++= List( + binarynme.RuntimeNothing.toString() -> RuntimeNothingClass, // RuntimeNothingClass is the bytecode-level return type of Scala methods with Nothing return-type. + binarynme.RuntimeNull.toString() -> RuntimeNullClass + ) + // Lazy val; can't have eager vals in Phase constructors which may // cause cycles before Global has finished initialization. lazy val BeanInfoAttr = rootMirror.getRequiredClass("scala.beans.BeanInfo") @@ -130,8 +146,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { } bytecodeWriter.close() - classes.clear() - reverseJavaName.clear() /* don't javaNameCache.clear() because that causes the following tests to fail: * test/files/run/macro-repl-dontexpand.scala @@ -155,19 +169,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { var pickledBytes = 0 // statistics - // Don't put this in per run caches. Contains entries for classes as well as members. - val javaNameCache = new mutable.WeakHashMap[Symbol, Name]() ++= List( - NothingClass -> binarynme.RuntimeNothing, - RuntimeNothingClass -> binarynme.RuntimeNothing, - NullClass -> binarynme.RuntimeNull, - RuntimeNullClass -> binarynme.RuntimeNull - ) + val javaNameCache = perRunCaches.newMap[Symbol, Name]() // unlike javaNameCache, reverseJavaName contains entries only for class symbols and their internal names. - val reverseJavaName = mutable.Map.empty[String, Symbol] ++= List( - binarynme.RuntimeNothing.toString() -> RuntimeNothingClass, // RuntimeNothingClass is the bytecode-level return type of Scala methods with Nothing return-type. - binarynme.RuntimeNull.toString() -> RuntimeNullClass - ) + val reverseJavaName = perRunCaches.newMap[String, Symbol]() private def mkFlags(args: Int*) = args.foldLeft(0)(_ | _) private def hasPublicBitSet(flags: Int) = (flags & asm.Opcodes.ACC_PUBLIC) != 0 diff --git a/src/partest/scala/tools/partest/PartestDefaults.scala b/src/partest/scala/tools/partest/PartestDefaults.scala index 16f1a6933f..8478edeb4d 100644 --- a/src/partest/scala/tools/partest/PartestDefaults.scala +++ b/src/partest/scala/tools/partest/PartestDefaults.scala @@ -1,6 +1,7 @@ package scala.tools package partest +import scala.concurrent.duration.Duration import scala.tools.nsc.Properties.{ propOrElse, propOrNone, propOrEmpty } import java.lang.Runtime.{ getRuntime => runtime } @@ -21,6 +22,7 @@ object PartestDefaults { def testBuild = propOrNone("partest.build") def errorCount = propOrElse("partest.errors", "0").toInt def numThreads = propOrNone("partest.threads") map (_.toInt) getOrElse runtime.availableProcessors + def waitTime = propOrNone("partest.timeout") map (Duration.apply) getOrElse Duration("4 hours") - def timeout = "1200000" + //def timeout = "1200000" // per-test timeout } diff --git a/src/partest/scala/tools/partest/TestState.scala b/src/partest/scala/tools/partest/TestState.scala index ce8e72f616..dbe8a222a5 100644 --- a/src/partest/scala/tools/partest/TestState.scala +++ b/src/partest/scala/tools/partest/TestState.scala @@ -12,13 +12,15 @@ sealed abstract class TestState { def isOk = false def isSkipped = false def testIdent = testFile.testIdent - def transcriptString = transcript.mkString("\n") + def transcriptString = transcript mkString EOL def identAndReason = testIdent + reasonString def status = s"$what - $identAndReason" def longStatus = status + transcriptString def reasonString = if (reason == "") "" else s" [$reason]" + def shortStatus = if (isOk) "ok" else "!!" + override def toString = status } @@ -27,18 +29,27 @@ object TestState { def what = "uninitialized" def reason = what def transcript = Nil + override def shortStatus = "??" } case class Pass(testFile: File) extends TestState { - final override def isOk = true def what = "pass" + override def isOk = true def transcript: List[String] = Nil def reason = "" } - case class Skip(testFile: File, reason: String) extends TestState { + case class Updated(testFile: File) extends TestState { + def what = "updated" override def isOk = true - final override def isSkipped = true def transcript: List[String] = Nil + def reason = "updated check file" + override def shortStatus = "++" + } + case class Skip(testFile: File, reason: String) extends TestState { def what = "skip" + override def isOk = true + override def isSkipped = true + def transcript: List[String] = Nil + override def shortStatus = "--" } case class Fail(testFile: File, reason: String, transcript: List[String]) extends TestState { def what = "fail" diff --git a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala index ddd42f5601..e5ace20062 100644 --- a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala +++ b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala @@ -86,7 +86,7 @@ class ConsoleRunner extends DirectRunner { ) ::: standardArgs private val binaryArgs = List( - "--grep", "--srcpath", "--buildpath", "--classpath" + "--grep", "--srcpath", "--buildpath", "--classpath", "--timeout" ) def main(argstr: String) { @@ -109,6 +109,7 @@ class ConsoleRunner extends DirectRunner { } parsed get "--srcpath" foreach (x => setProp("partest.srcdir", x)) + parsed get "--timeout" foreach (x => setProp("partest.timeout", x)) fileManager = if (parsed isSet "--buildpath") new ConsoleFileManager(parsed("--buildpath")) diff --git a/src/partest/scala/tools/partest/nest/DirectCompiler.scala b/src/partest/scala/tools/partest/nest/DirectCompiler.scala index 650b6c35c8..8e5ff2abc4 100644 --- a/src/partest/scala/tools/partest/nest/DirectCompiler.scala +++ b/src/partest/scala/tools/partest/nest/DirectCompiler.scala @@ -48,7 +48,7 @@ class DirectCompiler(val fileManager: FileManager) { } def compile(runner: Runner, opts0: List[String], sources: List[File]): TestState = { - import runner._ + import runner.{ sources => _, _ } val testSettings = new TestSettings(ClassPath.join(fileManager.LATEST_LIB, outDir.getPath)) val logWriter = new FileWriter(logFile) diff --git a/src/partest/scala/tools/partest/nest/DirectRunner.scala b/src/partest/scala/tools/partest/nest/DirectRunner.scala deleted file mode 100644 index 49dd39c344..0000000000 --- a/src/partest/scala/tools/partest/nest/DirectRunner.scala +++ /dev/null @@ -1,52 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2013 LAMP/EPFL - * @author Philipp Haller - */ - -package scala.tools.partest -package nest - -import java.io.File -import scala.util.Properties.setProp -import scala.tools.nsc.util.{ ScalaClassLoader, Exceptional } -import scala.tools.nsc.io.Path -import scala.collection.{ mutable, immutable } -import java.util.concurrent._ - -case class TestRunParams(val scalaCheckParentClassLoader: ScalaClassLoader) - -trait DirectRunner { - def fileManager: FileManager - - import PartestDefaults.numThreads - - Thread.setDefaultUncaughtExceptionHandler( - new Thread.UncaughtExceptionHandler { - def uncaughtException(thread: Thread, t: Throwable) { - val t1 = Exceptional unwrap t - System.err.println(s"Uncaught exception on thread $thread: $t1") - t1.printStackTrace() - } - } - ) - def runTestsForFiles(kindFiles: List[File], kind: String): List[TestState] = { - - NestUI.resetTestNumber() - - val allUrls = PathSettings.scalaCheck.toURL :: fileManager.latestUrls - val parentClassLoader = ScalaClassLoader fromURLs allUrls - val pool = Executors newFixedThreadPool numThreads - val manager = new RunnerManager(kind, fileManager, TestRunParams(parentClassLoader)) - val futures = kindFiles map (f => pool submit callable(manager runTest f)) - - pool.shutdown() - try if (!pool.awaitTermination(4, TimeUnit.HOURS)) - NestUI warning "Thread pool timeout elapsed before all tests were complete!" - catch { case t: InterruptedException => - NestUI warning "Thread pool was interrupted" - t.printStackTrace() - } - - futures map (_.get) - } -} diff --git a/src/partest/scala/tools/partest/nest/FileManager.scala b/src/partest/scala/tools/partest/nest/FileManager.scala index 25371b7d54..230ada4803 100644 --- a/src/partest/scala/tools/partest/nest/FileManager.scala +++ b/src/partest/scala/tools/partest/nest/FileManager.scala @@ -85,7 +85,6 @@ trait FileManager extends FileUtil { var SCALAC_OPTS = PartestDefaults.scalacOpts.split(' ').toSeq var JAVA_OPTS = PartestDefaults.javaOpts - var timeout = PartestDefaults.timeout /** Only when --debug is given. */ lazy val testTimings = new mutable.HashMap[String, Long] diff --git a/src/partest/scala/tools/partest/nest/NestUI.scala b/src/partest/scala/tools/partest/nest/NestUI.scala index 2e203bfd91..87ffb0fed2 100644 --- a/src/partest/scala/tools/partest/nest/NestUI.scala +++ b/src/partest/scala/tools/partest/nest/NestUI.scala @@ -27,9 +27,14 @@ class Colors(enabled: => Boolean) { object NestUI { private val testNum = new java.util.concurrent.atomic.AtomicInteger(1) + @volatile private var testNumberFmt = "%3d" // @volatile private var testNumber = 1 - private def testNumber = "%3d" format testNum.getAndIncrement() - def resetTestNumber() = testNum set 1 + private def testNumber = testNumberFmt format testNum.getAndIncrement() + def resetTestNumber(max: Int = -1) { + testNum set 1 + val width = if (max > 0) max.toString.length else 3 + testNumberFmt = s"%${width}d" + } var colorEnabled = sys.props contains "partest.colors" val color = new Colors(colorEnabled) @@ -57,12 +62,15 @@ object NestUI { def statusLine(state: TestState) = { import state._ - val word = bold( - if (isSkipped) yellow("--") - else if (isOk) green("ok") - else red("!!") - ) - word + f" $testNumber%3s - $testIdent%-40s$reasonString" + import TestState._ + val colorizer = state match { + case _: Skip => yellow + case _: Updated => cyan + case s if s.isOk => green + case _ => red + } + val word = bold(colorizer(state.shortStatus)) + f"$word $testNumber - $testIdent%-40s$reasonString" } def reportTest(state: TestState) = { diff --git a/src/partest/scala/tools/partest/nest/PathSettings.scala b/src/partest/scala/tools/partest/nest/PathSettings.scala index bae6bf819d..8e454d8de8 100644 --- a/src/partest/scala/tools/partest/nest/PathSettings.scala +++ b/src/partest/scala/tools/partest/nest/PathSettings.scala @@ -7,9 +7,9 @@ package nest import scala.tools.nsc.Properties.{ setProp, propOrEmpty, propOrNone, propOrElse } import scala.tools.nsc.util.ClassPath -import scala.tools.nsc.io +import scala.tools.nsc.io.{ Path, File, Directory } import scala.util.Properties.{ envOrElse, envOrNone, javaHome, jdkHome } -import io.{ Path, File, Directory } +import Path._ object PathSettings { import PartestDefaults.{ testRootDir, srcDirName } @@ -19,6 +19,8 @@ object PathSettings { private def findJar(d: Directory, name: String): Option[File] = findJar(d.files, name) private def findJar(files: Iterator[File], name: String): Option[File] = files filter (_ hasExtension "jar") find { _.name startsWith name } + private def findJarOrFail(name: String, ds: Directory*): File = findJar(ds flatMap (_.files) iterator, name) getOrElse + sys.error(s"'${name}.jar' not found in '${ds map (_.path) mkString ", "}'.") // Directory <root>/test lazy val testRoot: Directory = testRootDir getOrElse { @@ -73,6 +75,8 @@ object PathSettings { sys.error("No scalacheck jar found in '%s' or '%s'".format(buildPackLibDir, srcLibDir)) } + lazy val testInterface: File = findJarOrFail("test-interface", buildPackLibDir, srcLibDir) + lazy val diffUtils: File = findJar(buildPackLibDir.files, "diffutils") getOrElse sys.error(s"No diffutils.jar found in '$buildPackLibDir'.") diff --git a/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala b/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala index 9780e82cd9..734affa153 100644 --- a/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala +++ b/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala @@ -55,7 +55,7 @@ class ReflectiveRunner { // we hack into the classloader that will become parent classloader for scalac // this way we ensure that reflective macro lookup will pick correct Code.lift // it's also used to inject diffutils into the classpath when running partest from the test/partest script - val srcCodeLibAndDiff = List(PathSettings.srcCodeLib, PathSettings.diffUtils) + val srcCodeLibAndDiff = List(PathSettings.srcCodeLib, PathSettings.diffUtils, PathSettings.testInterface) val sepUrls = srcCodeLibAndDiff.map(_.toURI.toURL) ::: fileManager.latestUrls // this seems to be the core classloader that determines which classes can be found when running partest from the test/partest script val sepLoader = new URLClassLoader(sepUrls.toArray, null) diff --git a/src/partest/scala/tools/partest/nest/Runner.scala b/src/partest/scala/tools/partest/nest/Runner.scala index fc56818bfc..d8e1eeb9b9 100644 --- a/src/partest/scala/tools/partest/nest/Runner.scala +++ b/src/partest/scala/tools/partest/nest/Runner.scala @@ -7,19 +7,24 @@ package nest import java.io.{ Console => _, _ } import java.net.URL -import scala.tools.nsc.Properties.{ jdkHome, javaHome, propOrElse, propOrEmpty } -import scala.util.Properties.{ envOrElse, isWin } +import java.nio.charset.{ Charset, CharsetDecoder, CharsetEncoder, CharacterCodingException, CodingErrorAction => Action } +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit.NANOSECONDS +import scala.collection.mutable.ListBuffer +import scala.concurrent.duration.Duration +import scala.io.Codec +import scala.sys.process.Process +import scala.tools.nsc.Properties.{ envOrElse, isWin, jdkHome, javaHome, propOrElse, propOrEmpty, setProp } import scala.tools.nsc.{ Settings, CompilerCommand, Global } -import scala.tools.nsc.io.{ AbstractFile, PlainFile, Path, Directory, File => SFile } +import scala.tools.nsc.io.{ AbstractFile, PlainFile } import scala.tools.nsc.reporters.ConsoleReporter -import scala.tools.nsc.util.{ ClassPath, FakePos, ScalaClassLoader, stackTraceString } -import ClassPath.{ join, split } +import scala.tools.nsc.util.{ Exceptional, ScalaClassLoader, stackTraceString } +import scala.tools.scalap.Main.decompileScala import scala.tools.scalap.scalax.rules.scalasig.ByteCode -import scala.collection.{ mutable, immutable } -import scala.sys.process.Process -import java.util.concurrent.{ Executors, TimeUnit, TimeoutException } +import scala.util.{ Try, Success, Failure } +import ClassPath.{ join, split } import PartestDefaults.{ javaCmd, javacCmd } -import scala.tools.scalap.Main.decompileScala +import TestState.{ Pass, Fail, Crash, Uninitialized, Updated } trait PartestRunSettings { def gitPath: Path @@ -35,7 +40,7 @@ trait PartestRunSettings { class TestTranscript { import NestUI.color._ - private val buf = mutable.ListBuffer[String]() + private val buf = ListBuffer[String]() private def pass(s: String) = bold(green("% ")) + s private def fail(s: String) = bold(red("% ")) + s @@ -49,7 +54,8 @@ class TestTranscript { } } -class Runner(val testFile: File, fileManager: FileManager) { +/** Run a single test. Rubber meets road. */ +class Runner(val testFile: File, fileManager: FileManager, val testRunParams: TestRunParams) { import fileManager._ // Override to true to have the outcome of this test displayed @@ -57,12 +63,10 @@ class Runner(val testFile: File, fileManager: FileManager) { // except for a . per passing test to show progress. def isEnumeratedTest = false - def testRunParams: TestRunParams = ??? - private var _lastState: TestState = null private var _transcript = new TestTranscript - def lastState = if (_lastState == null) TestState.Uninitialized(testFile) else _lastState + def lastState = if (_lastState == null) Uninitialized(testFile) else _lastState def setLastState(s: TestState) = _lastState = s def transcript: List[String] = _transcript.fail ++ logFile.fileLines def pushTranscript(msg: String) = _transcript add msg @@ -94,10 +98,11 @@ class Runner(val testFile: File, fileManager: FileManager) { genCrash(t) } - def genPass() = TestState.Pass(testFile) - def genFail(reason: String) = TestState.Fail(testFile, reason, _transcript.fail) - def genTimeout() = TestState.Fail(testFile, "timed out", _transcript.fail) - def genCrash(caught: Throwable) = TestState.Crash(testFile, caught, _transcript.fail) + def genPass() = Pass(testFile) + def genFail(reason: String) = Fail(testFile, reason, _transcript.fail) + def genTimeout() = Fail(testFile, "timed out", _transcript.fail) + def genCrash(caught: Throwable) = Crash(testFile, caught, _transcript.fail) + def genUpdated() = Updated(testFile) def speclib = PathSettings.srcSpecLib.toString // specialization lib def codelib = PathSettings.srcCodeLib.toString // reify lib @@ -151,14 +156,18 @@ class Runner(val testFile: File, fileManager: FileManager) { case _ => "% " } + /** Evaluate an action body and update the test state. + * @param failFn optionally map a result to a test state. + */ def nextTestAction[T](body: => T)(failFn: PartialFunction[T, TestState]): T = { val result = body setLastState( if (failFn isDefinedAt result) failFn(result) else genPass() ) result } - def nextTestActionExpectTrue[T](reason: String, body: => Boolean): Boolean = { + def nextTestActionExpectTrue(reason: String, body: => Boolean): Boolean = ( nextTestAction(body) { case false => genFail(reason) } - } + ) + def nextTestActionFailing(reason: String): Boolean = nextTestActionExpectTrue(reason, false) private def assembleTestCommand(outDir: File, logFile: File): List[String] = { // check whether there is a ".javaopts" file @@ -227,6 +236,7 @@ class Runner(val testFile: File, fileManager: FileManager) { override def toString = s"""Test($testIdent, lastState = $lastState)""" + // result is unused def newTestWriters() = { val swr = new StringWriter val wr = new PrintWriter(swr, true) @@ -320,18 +330,20 @@ class Runner(val testFile: File, fileManager: FileManager) { def diffIsOk: Boolean = { val diff = currentDiff - val ok: Boolean = (diff == "") || { - fileManager.updateCheck && { + // if diff is not empty, is update needed? + val updating: Option[Boolean] = ( + if (diff == "") None + else Some(fileManager.updateCheck) + ) + pushTranscript(s"diff $logFile $checkFile") + nextTestAction(updating) { + case Some(true) => NestUI.verbose("Updating checkfile " + checkFile) checkFile writeAll file2String(logFile) - true - } - } - pushTranscript(s"diff $logFile $checkFile") - nextTestAction(ok) { - case false => + genUpdated() + case Some(false) => // Get a word-highlighted diff from git if we can find it - val bestDiff = if (ok) "" else { + val bestDiff = if (updating.isEmpty) "" else { if (checkFile.canRead) gitDiff(logFile, checkFile) getOrElse { s"diff $logFile $checkFile\n$diff" @@ -343,11 +355,13 @@ class Runner(val testFile: File, fileManager: FileManager) { // TestState.fail("output differs", "output differs", // genFail("output differs") // TestState.Fail("output differs", bestDiff) - } + case None => genPass() // redundant default case + } getOrElse true } /** 1. Creates log file and output directory. * 2. Runs script function, providing log file and output directory as arguments. + * 2b. or, just run the script without context and return a new context */ def runInContext(body: => Boolean): (Boolean, LogContext) = { val (swr, wr) = newTestWriters() @@ -356,11 +370,16 @@ class Runner(val testFile: File, fileManager: FileManager) { } /** Grouped files in group order, and lex order within each group. */ - def groupedFiles(dir: File): List[List[File]] = { - val testFiles = dir.listFiles.toList filter (_.isJavaOrScala) - val grouped = testFiles groupBy (_.group) - grouped.keys.toList.sorted map (k => grouped(k) sortBy (_.getName)) - } + def groupedFiles(files: List[File]): List[List[File]] = ( + if (files.tail.nonEmpty) { + val grouped = files filter (_.isJavaOrScala) groupBy (_.group) + grouped.keys.toList.sorted map (k => grouped(k) sortBy (_.getName)) + } + else List(files) + ) + + /** Source files for the given test file. */ + def sources(file: File): List[File] = if (file.isDirectory) file.listFiles.toList else List(file) def newCompiler = new DirectCompiler(fileManager) @@ -411,11 +430,9 @@ class Runner(val testFile: File, fileManager: FileManager) { lazy val result = { pushTranscript(description) ; attemptCompile(fs) } } - def compilationRounds(file: File): List[CompileRound] = { - val grouped = if (file.isDirectory) groupedFiles(file) else List(List(file)) - - (grouped map mixedCompileGroup).flatten - } + def compilationRounds(file: File): List[CompileRound] = ( + (groupedFiles(sources(file)) map mixedCompileGroup).flatten + ) def mixedCompileGroup(allFiles: List[File]): List[CompileRound] = { val (scalaFiles, javaFiles) = allFiles partition (_.isScala) val isMixed = javaFiles.nonEmpty && scalaFiles.nonEmpty @@ -429,12 +446,16 @@ class Runner(val testFile: File, fileManager: FileManager) { def runNegTest() = runInContext { val rounds = compilationRounds(testFile) - if (rounds forall (x => nextTestActionExpectTrue("compilation failed", x.isOk))) - nextTestActionExpectTrue("expected compilation failure", false) - else { - normalizeLog // put errors in a normal form - diffIsOk + // failing means Does Not Compile + val failing = rounds find (x => nextTestActionExpectTrue("compilation failed", x.isOk) == false) + + // which means passing if it checks and didn't crash the compiler + def checked(r: CompileRound) = r.result match { + case f: Crash => false + case f => normalizeLog(); diffIsOk } + + failing map (checked) getOrElse nextTestActionFailing("expected compilation failure") } def runTestCommon(andAlso: => Boolean): (Boolean, LogContext) = runInContext { @@ -495,24 +516,68 @@ class Runner(val testFile: File, fileManager: FileManager) { } def runScalacheckTest() = runTestCommon { - NestUI.verbose("compilation of "+testFile+" succeeded\n") + NestUI verbose f"compilation of $testFile succeeded%n" - val outURL = outDir.getAbsoluteFile.toURI.toURL + // this classloader is test specific: its parent contains library classes and others + val loader = { + import PathSettings.scalaCheck + val locations = List(outDir, scalaCheck.jfile) map (_.getAbsoluteFile.toURI.toURL) + ScalaClassLoader.fromURLs(locations, getClass.getClassLoader) + } val logWriter = new PrintStream(new FileOutputStream(logFile), true) - Output.withRedirected(logWriter) { - // this classloader is test specific: its parent contains library classes and others - ScalaClassLoader.fromURLs(List(outURL), testRunParams.scalaCheckParentClassLoader).run("Test", Nil) + def toolArgs(tool: String): List[String] = { + def argsplitter(s: String) = words(s) filter (_.nonEmpty) + def argsFor(f: File): List[String] = { + import scala.util.matching.Regex + val p = new Regex(s"(?:.*\\s)?${tool}:(.*)?", "args") + val max = 10 + val src = Path(f).toFile.chars(codec) + val args = try { + src.getLines take max collectFirst { + case s if (p findFirstIn s).nonEmpty => for (m <- p findFirstMatchIn s) yield m group "args" + } + } finally src.close() + args.flatten map argsplitter getOrElse Nil + } + sources(testFile) flatMap argsFor } - - NestUI.verbose(file2String(logFile)) - // obviously this must be improved upon - val lines = SFile(logFile).lines map (_.trim) filterNot (_ == "") toBuffer; - lines.forall(x => !x.startsWith("!")) || { - NestUI.normal("ScalaCheck test failed. Output:\n") - lines foreach (x => NestUI.normal(x + "\n")) - false + def runInFramework(): Boolean = { + import org.scalatools.testing._ + val f: Framework = loader.instantiate[Framework]("org.scalacheck.ScalaCheckFramework") + val logger = new Logger { + def ansiCodesSupported = false //params.env.isSet("colors") + def error(msg: String) = logWriter println msg + def warn(msg: String) = logWriter println msg + def info(msg: String) = logWriter println msg + def debug(msg: String) = logWriter println msg + def trace(t: Throwable) = t printStackTrace logWriter + } + var bad = 0 + val handler = new EventHandler { + // testName, description, result, error + // Result = Success, Failure, Error, Skipped + def handle(event: Event): Unit = event.result match { + case Result.Success => + //case Result.Skipped => // an exhausted test is skipped, therefore bad + case _ => bad += 1 + } + } + val loggers = Array(logger) + val r = f.testRunner(loader, loggers).asInstanceOf[Runner2] // why? + val claas = "Test" + val fingerprint = f.tests collectFirst { case x: SubclassFingerprint if x.isModule => x } + val args = toolArgs("scalacheck") + vlog(s"Run $testFile with args $args") + // set the context class loader for scaladoc/scalacheck tests (FIX ME) + ScalaClassLoader(testRunParams.scalaCheckParentClassLoader).asContext { + r.run(claas, fingerprint.get, handler, args.toArray) // synchronous? + } + val ok = (bad == 0) + if (!ok) _transcript append logFile.fileContents + ok } + try nextTestActionExpectTrue("ScalaCheck test failed", runInFramework()) finally logWriter.close() } def runResidentTest() = { @@ -626,3 +691,139 @@ class Runner(val testFile: File, fileManager: FileManager) { Directory(outDir).deleteRecursively() } } + +case class TestRunParams(val scalaCheckParentClassLoader: ScalaClassLoader) + +/** Extended by Ant- and ConsoleRunner for running a set of tests. */ +trait DirectRunner { + def fileManager: FileManager + + import PartestDefaults.{ numThreads, waitTime } + + Thread.setDefaultUncaughtExceptionHandler( + new Thread.UncaughtExceptionHandler { + def uncaughtException(thread: Thread, t: Throwable) { + val t1 = Exceptional unwrap t + System.err.println(s"Uncaught exception on thread $thread: $t1") + t1.printStackTrace() + } + } + ) + def runTestsForFiles(kindFiles: List[File], kind: String): List[TestState] = { + + NestUI.resetTestNumber(kindFiles.size) + + // this special class loader is for the benefit of scaladoc tests, which need a class path + import PathSettings.{ testInterface, scalaCheck } + val allUrls = scalaCheck.toURL :: testInterface.toURL :: fileManager.latestUrls + val parentClassLoader = ScalaClassLoader fromURLs allUrls + // add scalacheck.jar to a special classloader, but use our loader as parent with test-interface + //val parentClassLoader = ScalaClassLoader fromURLs (List(scalaCheck.toURL), getClass().getClassLoader) + val pool = Executors newFixedThreadPool numThreads + val manager = new RunnerManager(kind, fileManager, TestRunParams(parentClassLoader)) + val futures = kindFiles map (f => pool submit callable(manager runTest f)) + + pool.shutdown() + Try (pool.awaitTermination(waitTime) { + throw TimeoutException(waitTime) + }) match { + case Success(_) => futures map (_.get) + case Failure(e) => + e match { + case TimeoutException(d) => + NestUI warning "Thread pool timeout elapsed before all tests were complete!" + case ie: InterruptedException => + NestUI warning "Thread pool was interrupted" + ie.printStackTrace() + } + pool.shutdownNow() // little point in continuing + // try to get as many completions as possible, in case someone cares + val results = for (f <- futures) yield { + try { + Some(f.get(0, NANOSECONDS)) + } catch { + case _: Throwable => None + } + } + results.flatten + } + } +} + +case class TimeoutException(duration: Duration) extends RuntimeException + +class LogContext(val file: File, val writers: Option[(StringWriter, PrintWriter)]) + +object LogContext { + def apply(file: File, swr: StringWriter, wr: PrintWriter): LogContext = { + require (file != null) + new LogContext(file, Some((swr, wr))) + } + def apply(file: File): LogContext = new LogContext(file, None) +} + +object Output { + object outRedirect extends Redirecter(out) + object errRedirect extends Redirecter(err) + + System.setOut(outRedirect) + System.setErr(errRedirect) + + import scala.util.DynamicVariable + private def out = java.lang.System.out + private def err = java.lang.System.err + private val redirVar = new DynamicVariable[Option[PrintStream]](None) + + class Redirecter(stream: PrintStream) extends PrintStream(new OutputStream { + def write(b: Int) = withStream(_ write b) + + private def withStream(f: PrintStream => Unit) = f(redirVar.value getOrElse stream) + + override def write(b: Array[Byte]) = withStream(_ write b) + override def write(b: Array[Byte], off: Int, len: Int) = withStream(_.write(b, off, len)) + override def flush = withStream(_.flush) + override def close = withStream(_.close) + }) + + // this supports thread-safe nested output redirects + def withRedirected[T](newstream: PrintStream)(func: => T): T = { + // note down old redirect destination + // this may be None in which case outRedirect and errRedirect print to stdout and stderr + val saved = redirVar.value + // set new redirecter + // this one will redirect both out and err to newstream + redirVar.value = Some(newstream) + + try func + finally { + newstream.flush() + redirVar.value = saved + } + } +} + +/** Use a Runner to run a test. */ +class RunnerManager(kind: String, fileManager: FileManager, params: TestRunParams) { + import fileManager._ + fileManager.CLASSPATH += File.pathSeparator + PathSettings.scalaCheck + fileManager.CLASSPATH += File.pathSeparator + PathSettings.diffUtils // needed to put diffutils on test/partest's classpath + + def runTest(testFile: File): TestState = { + val runner = new Runner(testFile, fileManager, params) + + // when option "--failed" is provided execute test only if log + // is present (which means it failed before) + if (fileManager.failed && !runner.logFile.canRead) + runner.genPass() + else { + val (state, elapsed) = + try timed(runner.run()) + catch { + case t: Throwable => throw new RuntimeException(s"Error running $testFile", t) + } + NestUI.reportTest(state) + runner.cleanup() + state + } + } +} diff --git a/src/partest/scala/tools/partest/nest/RunnerManager.scala b/src/partest/scala/tools/partest/nest/RunnerManager.scala deleted file mode 100644 index fa2b5ea74b..0000000000 --- a/src/partest/scala/tools/partest/nest/RunnerManager.scala +++ /dev/null @@ -1,97 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2013 LAMP/EPFL - * @author Philipp Haller - */ - -package scala.tools.partest -package nest - -import java.io._ -import java.net.URL -import scala.tools.nsc.Properties.{ jdkHome, javaHome, propOrElse } -import scala.util.Properties.{ envOrElse, isWin } -import scala.tools.nsc.{ Settings, CompilerCommand, Global } -import scala.tools.nsc.io.{ AbstractFile, PlainFile, Path, Directory, File => SFile } -import scala.tools.nsc.reporters.ConsoleReporter -import scala.tools.nsc.util.{ ClassPath, FakePos, ScalaClassLoader, stackTraceString } -import ClassPath.{ join, split } -import scala.tools.scalap.scalax.rules.scalasig.ByteCode -import scala.collection.{ mutable, immutable } -import scala.sys.process._ -import java.util.concurrent.{ Executors, TimeUnit, TimeoutException } -import PartestDefaults.{ javaCmd, javacCmd } -import scala.tools.scalap.Main.decompileScala - -class LogContext(val file: File, val writers: Option[(StringWriter, PrintWriter)]) - -object LogContext { - def apply(file: File, swr: StringWriter, wr: PrintWriter): LogContext = { - require (file != null) - new LogContext(file, Some((swr, wr))) - } - def apply(file: File): LogContext = new LogContext(file, None) -} - -object Output { - object outRedirect extends Redirecter(out) - object errRedirect extends Redirecter(err) - - System.setOut(outRedirect) - System.setErr(errRedirect) - - import scala.util.DynamicVariable - private def out = java.lang.System.out - private def err = java.lang.System.err - private val redirVar = new DynamicVariable[Option[PrintStream]](None) - - class Redirecter(stream: PrintStream) extends PrintStream(new OutputStream { - def write(b: Int) = withStream(_ write b) - - private def withStream(f: PrintStream => Unit) = f(redirVar.value getOrElse stream) - - override def write(b: Array[Byte]) = withStream(_ write b) - override def write(b: Array[Byte], off: Int, len: Int) = withStream(_.write(b, off, len)) - override def flush = withStream(_.flush) - override def close = withStream(_.close) - }) - - // this supports thread-safe nested output redirects - def withRedirected[T](newstream: PrintStream)(func: => T): T = { - // note down old redirect destination - // this may be None in which case outRedirect and errRedirect print to stdout and stderr - val saved = redirVar.value - // set new redirecter - // this one will redirect both out and err to newstream - redirVar.value = Some(newstream) - - try func - finally { - newstream.flush() - redirVar.value = saved - } - } -} - -class RunnerManager(kind: String, val fileManager: FileManager, params: TestRunParams) { - import fileManager._ - - fileManager.CLASSPATH += File.pathSeparator + PathSettings.scalaCheck - fileManager.CLASSPATH += File.pathSeparator + PathSettings.diffUtils // needed to put diffutils on test/partest's classpath - PathSettings.platformTools foreach (fileManager.CLASSPATH += File.pathSeparator + _) - - def runTest(testFile: File): TestState = { - val runner = new Runner(testFile, fileManager) { - override def testRunParams = params - } - // when option "--failed" is provided execute test only if log - // is present (which means it failed before) - if (fileManager.failed && !runner.logFile.canRead) - runner.genPass() - else { - val (state, elapsed) = timed(runner.run()) - NestUI.reportTest(state) - runner.cleanup() - state - } - } -} diff --git a/src/partest/scala/tools/partest/package.scala b/src/partest/scala/tools/partest/package.scala index 9e21b0f6ba..4a516d620b 100644 --- a/src/partest/scala/tools/partest/package.scala +++ b/src/partest/scala/tools/partest/package.scala @@ -4,8 +4,9 @@ package scala.tools +import java.util.concurrent.{ Callable, ExecutorService } +import scala.concurrent.duration.Duration import scala.sys.process.javaVmArguments -import java.util.concurrent.Callable import scala.tools.partest.nest.NestUI import scala.tools.nsc.util.{ ScalaClassLoader, Exceptional } @@ -31,6 +32,8 @@ package object partest { def ojoin(xs: String*): String = oempty(xs: _*) mkString space def nljoin(xs: String*): String = oempty(xs: _*) mkString EOL + implicit val codec = scala.io.Codec.UTF8 + def setUncaughtHandler() = { Thread.setDefaultUncaughtExceptionHandler( new Thread.UncaughtExceptionHandler { @@ -85,6 +88,25 @@ package object partest { def copyTo(dest: Path): Unit = dest.toFile writeAll f.slurp(scala.io.Codec.UTF8) } + implicit class LoaderOps(val loader: ClassLoader) extends AnyVal { + import scala.util.control.Exception.catching + /** Like ScalaClassLoader.create for the case where the result type is + * available to the current class loader, implying that the current + * loader is a parent of `loader`. + */ + def instantiate[A >: Null](name: String): A = ( + catching(classOf[ClassNotFoundException], classOf[SecurityException]) opt + (loader loadClass name).newInstance.asInstanceOf[A] orNull + ) + } + + implicit class ExecutorOps(val executor: ExecutorService) { + def awaitTermination[A](wait: Duration)(failing: => A = ()): Option[A] = ( + if (executor awaitTermination (wait.length, wait.unit)) None + else Some(failing) + ) + } + implicit def temporaryPath2File(x: Path): File = x.jfile implicit def stringPathToJavaFile(path: String): File = new File(path) diff --git a/src/scalacheck/org/scalacheck/Arbitrary.scala b/src/scalacheck/org/scalacheck/Arbitrary.scala index 8c43cdaafe..db4163c8af 100644 --- a/src/scalacheck/org/scalacheck/Arbitrary.scala +++ b/src/scalacheck/org/scalacheck/Arbitrary.scala @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------*\ ** ScalaCheck ** -** Copyright (c) 2007-2011 Rickard Nilsson. All rights reserved. ** +** Copyright (c) 2007-2013 Rickard Nilsson. All rights reserved. ** ** http://www.scalacheck.org ** ** ** ** This software is released under the terms of the Revised BSD License. ** @@ -10,49 +10,44 @@ package org.scalacheck import util.{FreqMap,Buildable} -import scala.reflect.ClassTag sealed abstract class Arbitrary[T] { val arbitrary: Gen[T] } -/** Defines implicit <code>Arbitrary</code> instances for common types. +/** Defines implicit [[org.scalacheck.Arbitrary]] 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 + * uses implicit [[org.scalacheck.Arbitrary]] instances when creating properties + * out of functions with the `Prop.property` method, and when + * the `Arbitrary.arbitrary` method is used. For example, the * following code requires that there exists an implicit - * <code>Arbitrary[MyClass]</code> instance: + * `Arbitrary[MyClass]` instance: * </p> * - * <p> - * <code> - * val myProp = Prop.forAll { myClass: MyClass =><br /> - * ...<br /> - * }<br /> + * {{{ + * val myProp = Prop.forAll { myClass: MyClass => + * ... + * } * * 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>. + * The factory method `Arbitrary(...)` takes a generator of type + * `Gen[T]` and returns an instance of `Arbitrary[T]`. * </p> * * <p> - * The <code>Arbitrary</code> module defines implicit <code>Arbitrary</code> + * The `Arbitrary` module defines implicit [[org.scalacheck.Arbitrary]] * instances for common types, for convenient use in your properties and * generators. * </p> @@ -93,7 +88,7 @@ object Arbitrary { /** Arbitrary instance of Long */ implicit lazy val arbLong: Arbitrary[Long] = Arbitrary( - Gen.chooseNum(Long.MinValue / 2, Long.MaxValue / 2) + Gen.chooseNum(Long.MinValue, Long.MaxValue) ) /** Arbitrary instance of Float */ @@ -182,7 +177,13 @@ object Arbitrary { mc <- mcGen limit <- value(if(mc == UNLIMITED) 0 else math.max(x.abs.toString.length - mc.getPrecision, 0)) scale <- Gen.chooseNum(Int.MinValue + limit , Int.MaxValue) - } yield BigDecimal(x, scale, mc) + } yield { + try { + BigDecimal(x, scale, mc) + } catch { + case ae: java.lang.ArithmeticException => BigDecimal(x, scale, UNLIMITED) // Handle the case where scale/precision conflict + } + } Arbitrary(bdGen) } @@ -213,23 +214,43 @@ object Arbitrary { )) } - /** Arbitrary instance of test params */ + /** Arbitrary instance of test params + * @deprecated (in 1.10.0) Use `arbTestParameters` instead. + */ + @deprecated("Use 'arbTestParameters' instead", "1.10.0") implicit lazy val arbTestParams: Arbitrary[Test.Params] = Arbitrary(for { minSuccTests <- choose(10,200) - maxDiscardRatio <- choose(0.2f,10f) - minSize <- choose(0,500) + maxDiscTests <- choose(100,500) + mnSize <- choose(0,500) sizeDiff <- choose(0,500) - maxSize <- choose(minSize, minSize + sizeDiff) + mxSize <- choose(mnSize, mnSize + sizeDiff) ws <- choose(1,4) } yield Test.Params( minSuccessfulTests = minSuccTests, - maxDiscardRatio = maxDiscardRatio, - minSize = minSize, - maxSize = maxSize, + maxDiscardedTests = maxDiscTests, + minSize = mnSize, + maxSize = mxSize, workers = ws )) + /** Arbitrary instance of test parameters */ + implicit lazy val arbTestParameters: Arbitrary[Test.Parameters] = + Arbitrary(for { + _minSuccTests <- choose(10,200) + _maxDiscardRatio <- choose(0.2f,10f) + _minSize <- choose(0,500) + sizeDiff <- choose(0,500) + _maxSize <- choose(_minSize, _minSize + sizeDiff) + _workers <- choose(1,4) + } yield new Test.Parameters.Default { + override val minSuccessfulTests = _minSuccTests + override val maxDiscardRatio = _maxDiscardRatio + override val minSize = _minSize + override val maxSize = _maxSize + override val workers = _workers + }) + /** Arbitrary instance of gen params */ implicit lazy val arbGenParams: Arbitrary[Gen.Params] = Arbitrary(for { @@ -278,7 +299,7 @@ object Arbitrary { ): Arbitrary[C[T]] = Arbitrary(containerOf[C,T](arbitrary[T])) /** Arbitrary instance of any array. */ - implicit def arbArray[T](implicit a: Arbitrary[T], c: ClassTag[T] + implicit def arbArray[T](implicit a: Arbitrary[T], c: ClassManifest[T] ): Arbitrary[Array[T]] = Arbitrary(containerOf[Array,T](arbitrary[T])) diff --git a/src/scalacheck/org/scalacheck/Arg.scala b/src/scalacheck/org/scalacheck/Arg.scala index 8959211f09..4961c78a26 100644 --- a/src/scalacheck/org/scalacheck/Arg.scala +++ b/src/scalacheck/org/scalacheck/Arg.scala @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------*\ ** ScalaCheck ** -** Copyright (c) 2007-2011 Rickard Nilsson. All rights reserved. ** +** Copyright (c) 2007-2013 Rickard Nilsson. All rights reserved. ** ** http://www.scalacheck.org ** ** ** ** This software is released under the terms of the Revised BSD License. ** diff --git a/src/scalacheck/org/scalacheck/Commands.scala b/src/scalacheck/org/scalacheck/Commands.scala index 2acc460b5e..604b68cb36 100644 --- a/src/scalacheck/org/scalacheck/Commands.scala +++ b/src/scalacheck/org/scalacheck/Commands.scala @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------*\ ** ScalaCheck ** -** Copyright (c) 2007-2011 Rickard Nilsson. All rights reserved. ** +** Copyright (c) 2007-2013 Rickard Nilsson. All rights reserved. ** ** http://www.scalacheck.org ** ** ** ** This software is released under the terms of the Revised BSD License. ** @@ -53,7 +53,7 @@ trait Commands extends Prop { * 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 scala.collection.mutable.ListBuffer[State => Boolean] + val preConditions = new collection.mutable.ListBuffer[State => Boolean] /** Returns all postconditions merged into a single function */ def postCondition: (State,State,Any) => Prop = (s0,s1,r) => all(postConditions.map(_.apply(s0,s1,r)): _*) @@ -65,7 +65,7 @@ trait Commands extends Prop { * 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 scala.collection.mutable.ListBuffer[(State,State,Any) => Prop] + val postConditions = new collection.mutable.ListBuffer[(State,State,Any) => Prop] } /** A command that binds its result for later use */ @@ -87,6 +87,11 @@ trait Commands extends Prop { 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 { diff --git a/src/scalacheck/org/scalacheck/ConsoleReporter.scala b/src/scalacheck/org/scalacheck/ConsoleReporter.scala index 93f1dc222e..d565322d99 100644 --- a/src/scalacheck/org/scalacheck/ConsoleReporter.scala +++ b/src/scalacheck/org/scalacheck/ConsoleReporter.scala @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------*\ ** ScalaCheck ** -** Copyright (c) 2007-2011 Rickard Nilsson. All rights reserved. ** +** Copyright (c) 2007-2013 Rickard Nilsson. All rights reserved. ** ** http://www.scalacheck.org ** ** ** ** This software is released under the terms of the Revised BSD License. ** diff --git a/src/scalacheck/org/scalacheck/Gen.scala b/src/scalacheck/org/scalacheck/Gen.scala index 64bb61c2d3..aec67159f1 100644 --- a/src/scalacheck/org/scalacheck/Gen.scala +++ b/src/scalacheck/org/scalacheck/Gen.scala @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------*\ ** ScalaCheck ** -** Copyright (c) 2007-2011 Rickard Nilsson. All rights reserved. ** +** Copyright (c) 2007-2013 Rickard Nilsson. All rights reserved. ** ** http://www.scalacheck.org ** ** ** ** This software is released under the terms of the Revised BSD License. ** @@ -23,7 +23,7 @@ object Choose { implicit val chooseLong: Choose[Long] = new Choose[Long] { def choose(low: Long, high: Long) = - if(low > high || (high-low < 0)) fail + if (low > high) fail else parameterized(prms => value(prms.choose(low,high))) } @@ -204,9 +204,17 @@ object Gen { /** @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 l + math.abs(rng.nextLong % (d+1)) + if (h < l) throw new IllegalArgumentException("Invalid range") + val d = h - l + 1 + if (d <= 0) { + var n = rng.nextLong + while (n < l || n > h) { + n = rng.nextLong + } + n + } else { + l + math.abs(rng.nextLong % d) + } } /** @throws IllegalArgumentException if l is greater than h, or if @@ -225,8 +233,11 @@ object Gen { 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> */ + /* Convenience method for using the `frequency` method like this: + * {{{ + * frequency((1, "foo"), (3, "bar")) + * }}} + */ implicit def freqTuple[T](t: (Int, T)): (Int, Gen[T]) = (t._1, value(t._2)) @@ -308,9 +319,9 @@ object Gen { //// List Generators //// /** Generates a container of any type for which there exists an implicit - * <code>Buildable</code> instance. The elements in the container will + * [[org.scalacheck.util.Buildable]] 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>. */ + * is given by `n`. */ 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]] { @@ -321,14 +332,14 @@ object Gen { }) /** 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 + * [[org.scalacheck.util.Buildable]] 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 + * implicit [[org.scalacheck.util.Buildable]] 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]] = @@ -336,16 +347,16 @@ object Gen { /** 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>. */ + * `containerOf[List,T](g)`. */ 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>. */ + * `containerOf1[List,T](g)`. */ 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>. */ + * `containerOfN[List,T](n,g)`. */ def listOfN[T](n: Int, g: Gen[T]) = containerOfN[List,T](n,g) /** A generator that picks a random number of elements from a list */ diff --git a/src/scalacheck/org/scalacheck/Pretty.scala b/src/scalacheck/org/scalacheck/Pretty.scala index eeb5936086..3e8f6de5f6 100644 --- a/src/scalacheck/org/scalacheck/Pretty.scala +++ b/src/scalacheck/org/scalacheck/Pretty.scala @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------*\ ** ScalaCheck ** -** Copyright (c) 2007-2011 Rickard Nilsson. All rights reserved. ** +** Copyright (c) 2007-2013 Rickard Nilsson. All rights reserved. ** ** http://www.scalacheck.org ** ** ** ** This software is released under the terms of the Revised BSD License. ** @@ -96,7 +96,7 @@ object Pretty { } implicit def prettyTestRes(res: Test.Result) = Pretty { prms => - def labels(ls: scala.collection.immutable.Set[String]) = + def labels(ls: collection.immutable.Set[String]) = if(ls.isEmpty) "" else "> Labels of failing property: " / ls.mkString("\n") val s = res.status match { diff --git a/src/scalacheck/org/scalacheck/Prop.scala b/src/scalacheck/org/scalacheck/Prop.scala index dfd85a832a..38e00f260f 100644 --- a/src/scalacheck/org/scalacheck/Prop.scala +++ b/src/scalacheck/org/scalacheck/Prop.scala @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------*\ ** ScalaCheck ** -** Copyright (c) 2007-2011 Rickard Nilsson. All rights reserved. ** +** Copyright (c) 2007-2013 Rickard Nilsson. All rights reserved. ** ** http://www.scalacheck.org ** ** ** ** This software is released under the terms of the Revised BSD License. ** @@ -31,16 +31,27 @@ trait Prop { /** 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. */ + * from the test use the `check` methods in [[org.scalacheck.Test]] + * instead. + * @deprecated (in 1.10.0) Use `check(Test.Parameters)` instead. + */ + @deprecated("Use 'check(Test.Parameters)' instead", "1.10.0") def check(prms: Test.Params): Unit = Test.check( prms copy (testCallback = ConsoleReporter(1) chain prms.testCallback), this ) + /** 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 `check` methods in [[org.scalacheck.Test]] + * instead. */ + def check(prms: Test.Parameters): 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()) + * the `check` methods in [[org.scalacheck.Test]] instead. */ + def check: Unit = check(Test.Parameters.default) /** The logic for main, separated out to make it easier to * avoid System.exit calls. Returns exit code. @@ -60,7 +71,7 @@ trait Prop { /** Whether main should call System.exit with an exit code. * Defaults to true; override to change. */ - def mainCallsExit = false + def mainCallsExit = true /** Convenience method that makes it possible to use this property * as an application that checks itself on execution */ @@ -274,20 +285,51 @@ object Prop { def apply(r: Result): Prop = Prop(prms => r) + def apply(b: Boolean): Prop = if(b) proved else falsified - // Implicit defs + // Implicits + + /** A collection of property operators on [[Any]] values. + * Import [[Prop.AnyOperators]] to make the operators available. */ class ExtendedAny[T <% Pretty](x: => T) { + /** See [[Prop.imply]] */ def imply(f: PartialFunction[T,Prop]) = Prop.imply(x,f) + /** See [[Prop.iff]] */ def iff(f: PartialFunction[T,Prop]) = Prop.iff(x,f) - def throws[U <: Throwable](c: Class[U]) = Prop.throws(x, c) + @deprecated("Use 'Prop.throws' instead", "1.10.1") + def throws[U <: Throwable](c: Class[U]): Prop = Prop.throws(c)(x) + /** See [[Prop.?=]] */ def ?=(y: T) = Prop.?=(x, y) + /** See [[Prop.=?]] */ def =?(y: T) = Prop.=?(x, y) } + /** A collection of property operators on [[Boolean]] values. + * Import [[Prop.BooleanOperators]] to make the operators available. */ + class ExtendedBoolean(b: => Boolean) { + /** See [[Prop.==>]] */ + def ==>(p: => Prop) = Prop(b) ==> p + } + + /** Implicit method that makes a number of property operators on values of + * type [[Any]] available in the current scope. See [[Prop.ExtendedAny]] for + * documentation on the operators. */ + @deprecated("Use 'Prop.AnyOperators' instead", "1.10.1") implicit def extendedAny[T <% Pretty](x: => T) = new ExtendedAny[T](x) - implicit def propBoolean(b: Boolean): Prop = if(b) proved else falsified + /** Implicit method that makes a number of property operators on values of + * type [[Any]] available in the current scope. See [[Prop.ExtendedAny]] for + * documentation on the operators. */ + implicit def AnyOperators[T <% Pretty](x: => T) = new ExtendedAny[T](x) + + /** Implicit method that makes a number of property operators on boolean + * values available in the current scope. See [[Prop.ExtendedBoolean]] for + * documentation on the operators. */ + implicit def BooleanOperators(b: => Boolean) = new ExtendedBoolean(b) + + /** Implicit conversion of Boolean values to Prop values. */ + implicit def propBoolean(b: Boolean): Prop = Prop(b) // Private support functions @@ -318,6 +360,9 @@ object Prop { /** A property that denotes an exception */ lazy val exception: Prop = exception(null) + /** Create a property that compares to values. If the values aren't equal, + * the property will fail and report that first value doesn't match the + * expected (second) value. */ 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)) @@ -325,6 +370,9 @@ object Prop { "Expected "+exp+" but got "+act } + /** Create a property that compares to values. If the values aren't equal, + * the property will fail and report that second value doesn't match the + * expected (first) value. */ def =?[T](x: T, y: T)(implicit pp: T => Pretty): Prop = ?=(y, x) /** A property that depends on the generator size */ @@ -340,7 +388,7 @@ object 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 */ + * `x`, 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) @@ -365,9 +413,16 @@ object Prop { 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 + * @deprecated (in 1.10.1) Use `throws(...): Boolean` instead. + */ + @deprecated("Use 'throws(...): Boolean' instead", "1.10.1") + def throws[T <: Throwable](x: => Any, c: Class[T]): Prop = throws(c)(x) + + /** Returns true 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 } + def throws[T <: Throwable](c: Class[T])(x: => Any): Boolean = + try { x; false } catch { case e if c.isInstance(e) => true } /** Collect data for presentation in test report */ def collect[T, P <% Prop](f: T => P): T => Prop = t => Prop { prms => @@ -390,7 +445,7 @@ object Prop { /** Wraps and protects a property */ def secure[P <% Prop](p: => P): Prop = - try { p: Prop } catch { case e => exception(e) } + try { p: Prop } catch { case e: Throwable => exception(e) } /** Existential quantifier for an explicit generator. */ def exists[A,P](f: A => P)(implicit diff --git a/src/scalacheck/org/scalacheck/Properties.scala b/src/scalacheck/org/scalacheck/Properties.scala index 26059231d6..d4836d7420 100644 --- a/src/scalacheck/org/scalacheck/Properties.scala +++ b/src/scalacheck/org/scalacheck/Properties.scala @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------*\ ** ScalaCheck ** -** Copyright (c) 2007-2011 Rickard Nilsson. All rights reserved. ** +** Copyright (c) 2007-2013 Rickard Nilsson. All rights reserved. ** ** http://www.scalacheck.org ** ** ** ** This software is released under the terms of the Revised BSD License. ** @@ -14,15 +14,15 @@ package org.scalacheck * 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) => + * 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 { @@ -42,16 +42,27 @@ class Properties(val name: String) extends Prop { /** 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> + * from the test use the `check` methods in [[org.scalacheck.Test]] * instead. */ + override def check(prms: Test.Parameters): Unit = Test.checkProperties( + prms copy (_testCallback = ConsoleReporter(1) chain prms.testCallback), this + ) + + /** 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 `check` methods in [[org.scalacheck.Test]] + * instead. + * @deprecated (in 1.10.0) Use `check(Test.Parameters)` instead. + */ + @deprecated("Use 'check(Test.Parameters)' instead", "1.10.0") 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()) + * the `check` methods in [[org.scalacheck.Test]] instead. */ + override def check: Unit = check(Test.Parameters.default) /** The logic for main, separated out to make it easier to * avoid System.exit calls. Returns exit code. @@ -73,7 +84,10 @@ class Properties(val name: String) extends Prop { def include(ps: Properties) = for((n,p) <- ps.properties) property(n) = p /** Used for specifying properties. Usage: - * <code>property("myProp") = ...</code> */ + * {{{ + * property("myProp") = ... + * }}} + */ class PropertySpecifier() { def update(propName: String, p: Prop) = props += ((name+"."+propName, p)) } diff --git a/src/scalacheck/org/scalacheck/ScalaCheckFramework.scala b/src/scalacheck/org/scalacheck/ScalaCheckFramework.scala new file mode 100644 index 0000000000..7764101844 --- /dev/null +++ b/src/scalacheck/org/scalacheck/ScalaCheckFramework.scala @@ -0,0 +1,92 @@ +/*-------------------------------------------------------------------------*\ +** ScalaCheck ** +** Copyright (c) 2007-2013 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. ** +\*------------------------------------------------------------------------ */ + +// vim: set ts=2 sw=2 et: + +package org.scalacheck + +import org.scalatools.testing._ + +class ScalaCheckFramework extends Framework { + + private case object PropFingerprint extends TestFingerprint { + val superClassName = "org.scalacheck.Prop" + val isModule = false + } + + private case object PropsFingerprint extends TestFingerprint { + val superClassName = "org.scalacheck.Properties" + val isModule = true + } + + val name = "ScalaCheck" + + val tests = Array[Fingerprint](PropsFingerprint, PropsFingerprint) + + def testRunner(loader: ClassLoader, loggers: Array[Logger]) = new Runner2 { + + private def asEvent(nr: (String, Test.Result)) = nr match { + case (n: String, r: Test.Result) => new Event { + val testName = n + val description = n + val result = r.status match { + case Test.Passed => Result.Success + case _:Test.Proved => Result.Success + case _:Test.Failed => Result.Failure + case Test.Exhausted => Result.Skipped + case _:Test.PropException | _:Test.GenException => Result.Error + } + val error = r.status match { + case Test.PropException(_, e, _) => e + case _:Test.Failed => new Exception(Pretty.pretty(r,Pretty.Params(0))) + case _ => null + } + } + } + + def run(testClassName: String, fingerprint: Fingerprint, handler: EventHandler, args: Array[String]) { + + val testCallback = new Test.TestCallback { + override def onPropEval(n: String, w: Int, s: Int, d: Int) = {} + + override def onTestResult(n: String, r: Test.Result) = { + for (l <- loggers) { + import Pretty._ + l.info( + (if (r.passed) "+ " else "! ") + n + ": " + pretty(r, Params(0)) + ) + } + handler.handle(asEvent((n,r))) + } + } + + import Test.cmdLineParser.{Success, NoSuccess} + val prms = Test.cmdLineParser.parseParams(args) match { + case Success(params, _) => + params.copy(_testCallback = testCallback, _customClassLoader = Some(loader)) + // TODO: Maybe handle this a bit better than throwing exception? + case e: NoSuccess => throw new Exception(e.toString) + } + + fingerprint match { + case fp: SubclassFingerprint => + if(fp.isModule) { + val obj = Class.forName(testClassName + "$", true, loader) + val ps = obj.getField("MODULE$").get(null).asInstanceOf[Properties] + Test.checkProperties(prms, ps) + } else { + val p = Class.forName(testClassName, true, loader).newInstance.asInstanceOf[Prop] + handler.handle(asEvent((testClassName, Test.check(prms, p)))) + } + } + } + + } + +} diff --git a/src/scalacheck/org/scalacheck/Shrink.scala b/src/scalacheck/org/scalacheck/Shrink.scala index ae15bd9616..4895171a35 100644 --- a/src/scalacheck/org/scalacheck/Shrink.scala +++ b/src/scalacheck/org/scalacheck/Shrink.scala @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------*\ ** ScalaCheck ** -** Copyright (c) 2007-2011 Rickard Nilsson. All rights reserved. ** +** Copyright (c) 2007-2013 Rickard Nilsson. All rights reserved. ** ** http://www.scalacheck.org ** ** ** ** This software is released under the terms of the Revised BSD License. ** diff --git a/src/scalacheck/org/scalacheck/Test.scala b/src/scalacheck/org/scalacheck/Test.scala index 4368184823..6e9b6b88fd 100644 --- a/src/scalacheck/org/scalacheck/Test.scala +++ b/src/scalacheck/org/scalacheck/Test.scala @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------*\ ** ScalaCheck ** -** Copyright (c) 2007-2011 Rickard Nilsson. All rights reserved. ** +** Copyright (c) 2007-2013 Rickard Nilsson. All rights reserved. ** ** http://www.scalacheck.org ** ** ** ** This software is released under the terms of the Revised BSD License. ** @@ -16,23 +16,120 @@ object Test { import Prop.FM import util.CmdLineParser - /** Test parameters */ + /** Test parameters used by the `Test.check` method. + */ + trait Parameters { + /** The minimum number of tests that must succeed for ScalaCheck to + * consider a property passed. */ + def minSuccessfulTests: Int + + /** The starting size given as parameter to the generators. */ + def minSize: Int + + /** The maximum size given as parameter to the generators. */ + def maxSize: Int + + /** The random numbe generator used. */ + def rng: java.util.Random + + /** The number of tests run in parallell. */ + def workers: Int + + /** A callback that ScalaCheck calls each time a test is executed. */ + def testCallback: TestCallback + + /** The maximum ratio between discarded and passed tests allowed before + * ScalaCheck gives up and discards the property. At least + * `minSuccesfulTests` will always be run, though. */ + def maxDiscardRatio: Float + + /** A custom class loader that should be used during test execution. */ + def customClassLoader: Option[ClassLoader] + + // private since we can't guarantee binary compatibility for this one + private[scalacheck] def copy( + _minSuccessfulTests: Int = Parameters.this.minSuccessfulTests, + _minSize: Int = Parameters.this.minSize, + _maxSize: Int = Parameters.this.maxSize, + _rng: java.util.Random = Parameters.this.rng, + _workers: Int = Parameters.this.workers, + _testCallback: TestCallback = Parameters.this.testCallback, + _maxDiscardRatio: Float = Parameters.this.maxDiscardRatio, + _customClassLoader: Option[ClassLoader] = Parameters.this.customClassLoader + ): Parameters = new Parameters { + val minSuccessfulTests: Int = _minSuccessfulTests + val minSize: Int = _minSize + val maxSize: Int = _maxSize + val rng: java.util.Random = _rng + val workers: Int = _workers + val testCallback: TestCallback = _testCallback + val maxDiscardRatio: Float = _maxDiscardRatio + val customClassLoader: Option[ClassLoader] = _customClassLoader + } + } + + /** Test parameters used by the `Test.check` method. + * + * To override default values, extend the + * [[org.scalacheck.Test.Parameters.Default]] trait: + * + * {{{ + * val myParams = new Parameters.Default { + * override val minSuccesfulTests = 600 + * override val maxDiscardRatio = 8 + * } + * }}} + */ + object Parameters { + /** Default test parameters trait. This can be overriden if you need to + * tweak the parameters. */ + trait Default extends Parameters { + val minSuccessfulTests: Int = 100 + val minSize: Int = 0 + val maxSize: Int = Gen.Params().size + val rng: java.util.Random = Gen.Params().rng + val workers: Int = 1 + val testCallback: TestCallback = new TestCallback {} + val maxDiscardRatio: Float = 5 + val customClassLoader: Option[ClassLoader] = None + } + + /** Default test parameters instance. */ + val default: Parameters = new Default {} + } + + /** Test parameters + * @deprecated (in 1.10.0) Use [[org.scalacheck.Test.Parameters]] instead. + */ + @deprecated("Use [[org.scalacheck.Test.Parameters]] instead", "1.10.0") case class Params( minSuccessfulTests: Int = 100, - - /** @deprecated Use maxDiscardRatio instead. */ - @deprecated("Use maxDiscardRatio instead.", "1.10") maxDiscardedTests: Int = -1, - minSize: Int = 0, maxSize: Int = Gen.Params().size, rng: java.util.Random = Gen.Params().rng, workers: Int = 1, - testCallback: TestCallback = new TestCallback {}, - maxDiscardRatio: Float = 5, - customClassLoader: Option[ClassLoader] = None + testCallback: TestCallback = new TestCallback {} ) + @deprecated("Use [[org.scalacheck.Test.Parameters]] instead", "1.10.0") + private def paramsToParameters(params: Params) = new Parameters { + val minSuccessfulTests = params.minSuccessfulTests + val minSize = params.minSize + val maxSize = params.maxSize + val rng = params.rng + val workers = params.workers + val testCallback = params.testCallback + + // maxDiscardedTests is deprecated, but if someone + // uses it let it override maxDiscardRatio + val maxDiscardRatio = + if(params.maxDiscardedTests < 0) Parameters.default.maxDiscardRatio + else (params.maxDiscardedTests: Float)/(params.minSuccessfulTests: Float) + + val customClassLoader = Parameters.default.customClassLoader + } + /** Test statistics */ case class Result(status: Status, succeeded: Int, discarded: Int, freqMap: FM, time: Long = 0) { def passed = status match { @@ -92,7 +189,7 @@ object Test { } } - private def assertParams(prms: Params) = { + private def assertParams(prms: Parameters) = { import prms._ if( minSuccessfulTests <= 0 || @@ -104,16 +201,24 @@ object Test { } private def secure[T](x: => T): Either[T,Throwable] = - try { Left(x) } catch { case e => Right(e) } + try { Left(x) } catch { case e: Throwable => Right(e) } private[scalacheck] lazy val cmdLineParser = new CmdLineParser { object OptMinSuccess extends IntOpt { - val default = Test.Params().minSuccessfulTests + val default = Parameters.default.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 = -1 + val names = Set("maxDiscardedTests", "d") + val help = + "Number of tests that can be discarded before ScalaCheck stops " + + "testing a property. NOTE: this option is deprecated, please use " + + "the option maxDiscardRatio (-r) instead." + } object OptMaxDiscardRatio extends FloatOpt { - val default = Test.Params().maxDiscardRatio + val default = Parameters.default.maxDiscardRatio val names = Set("maxDiscardRatio", "r") val help = "The maximum ratio between discarded and succeeded tests " + @@ -121,17 +226,17 @@ object Test { "least minSuccessfulTests will always be tested, though." } object OptMinSize extends IntOpt { - val default = Test.Params().minSize + val default = Parameters.default.minSize val names = Set("minSize", "n") val help = "Minimum data generation size" } object OptMaxSize extends IntOpt { - val default = Test.Params().maxSize + val default = Parameters.default.maxSize val names = Set("maxSize", "x") val help = "Maximum data generation size" } object OptWorkers extends IntOpt { - val default = Test.Params().workers + val default = Parameters.default.workers val names = Set("workers", "w") val help = "Number of threads to execute in parallel for testing" } @@ -142,54 +247,63 @@ object Test { } val opts = Set[Opt[_]]( - OptMinSuccess, OptMaxDiscardRatio, OptMinSize, + OptMinSuccess, OptMaxDiscarded, OptMaxDiscardRatio, OptMinSize, OptMaxSize, OptWorkers, OptVerbosity ) def parseParams(args: Array[String]) = parseArgs(args) { - optMap => Test.Params( - minSuccessfulTests = optMap(OptMinSuccess), - maxDiscardRatio = optMap(OptMaxDiscardRatio), - minSize = optMap(OptMinSize), - maxSize = optMap(OptMaxSize), - rng = Test.Params().rng, - workers = optMap(OptWorkers), - testCallback = ConsoleReporter(optMap(OptVerbosity)) + optMap => Parameters.default.copy( + _minSuccessfulTests = optMap(OptMinSuccess), + _maxDiscardRatio = + if (optMap(OptMaxDiscarded) < 0) optMap(OptMaxDiscardRatio) + else optMap(OptMaxDiscarded).toFloat / optMap(OptMinSuccess), + _minSize = optMap(OptMinSize), + _maxSize = optMap(OptMaxSize), + _workers = optMap(OptWorkers), + _testCallback = ConsoleReporter(optMap(OptVerbosity)) ) } } /** Tests a property with the given testing parameters, and returns - * the test results. */ + * the test results. + * @deprecated (in 1.10.0) Use + * `check(Parameters, Properties)` instead. + */ + @deprecated("Use 'checkProperties(Parameters, Properties)' instead", "1.10.0") def check(params: Params, p: Prop): Result = { + check(paramsToParameters(params), p) + } - // maxDiscardedTests is deprecated, but if someone - // uses it let it override maxDiscardRatio - val mdr = - if(params.maxDiscardedTests < 0) params.maxDiscardRatio - else (params.maxDiscardedTests: Float)/(params.minSuccessfulTests: Float) - val prms = params.copy( maxDiscardRatio = mdr) - - import prms._ - import scala.actors.Futures.future + /** Tests a property with the given testing parameters, and returns + * the test results. */ + def check(params: Parameters, p: Prop): Result = { + import params._ - assertParams(prms) - if(workers > 1) + assertParams(params) + if(workers > 1) { assert(!p.isInstanceOf[Commands], "Commands cannot be checked multi-threaded") + } val iterations = math.ceil(minSuccessfulTests / (workers: Double)) val sizeStep = (maxSize-minSize) / (iterations*workers) var stop = false - def worker(workerIdx: Int) = future { - params.customClassLoader.map(Thread.currentThread.setContextClassLoader(_)) + def worker(workerIdx: Int) = + if (workers < 2) () => workerFun(workerIdx) + else actors.Futures.future { + params.customClassLoader.map(Thread.currentThread.setContextClassLoader(_)) + workerFun(workerIdx) + } + + def workerFun(workerIdx: Int) = { var n = 0 // passed tests var d = 0 // discarded tests var res: Result = null var fm = FreqMap.empty[immutable.Set[Any]] while(!stop && res == null && n < iterations) { val size = (minSize: Double) + (sizeStep * (workerIdx + (workers*(n+d)))) - val propPrms = Prop.Params(Gen.Params(size.round.toInt, prms.rng), fm) + val propPrms = Prop.Params(Gen.Params(size.round.toInt, params.rng), fm) secure(p(propPrms)) match { case Right(e) => res = Result(GenException(e), n, d, FreqMap.empty[immutable.Set[Any]]) @@ -250,11 +364,20 @@ object Test { stop = true results foreach (_.apply()) val timedRes = r.copy(time = System.currentTimeMillis-start) - prms.testCallback.onTestResult("", timedRes) + params.testCallback.onTestResult("", timedRes) timedRes } + /** Check a set of properties. + * @deprecated (in 1.10.0) Use + * `checkProperties(Parameters, Properties)` instead. + */ + @deprecated("Use 'checkProperties(Parameters, Properties)' instead", "1.10.0") def checkProperties(prms: Params, ps: Properties): Seq[(String,Result)] = + checkProperties(paramsToParameters(prms), ps) + + /** Check a set of properties. */ + def checkProperties(prms: Parameters, 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) = @@ -262,7 +385,7 @@ object Test { override def onTestResult(n: String, r: Result) = prms.testCallback.onTestResult(name,r) } - val res = check(prms copy (testCallback = testCallback), p) + val res = check(prms copy (_testCallback = testCallback), p) (name,res) } diff --git a/src/scalacheck/org/scalacheck/util/Buildable.scala b/src/scalacheck/org/scalacheck/util/Buildable.scala index 221b8a61c3..140c541a95 100644 --- a/src/scalacheck/org/scalacheck/util/Buildable.scala +++ b/src/scalacheck/org/scalacheck/util/Buildable.scala @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------*\ ** ScalaCheck ** -** Copyright (c) 2007-2011 Rickard Nilsson. All rights reserved. ** +** Copyright (c) 2007-2013 Rickard Nilsson. All rights reserved. ** ** http://www.scalacheck.org ** ** ** ** This software is released under the terms of the Revised BSD License. ** @@ -10,7 +10,6 @@ package org.scalacheck.util import scala.collection._ -import scala.reflect.ClassTag trait Buildable[T,C[_]] { def builder: mutable.Builder[T,C[T]] @@ -31,7 +30,7 @@ object Buildable { def builder = (new mutable.ListBuffer[T]).mapResult(_.toStream) } - implicit def buildableArray[T](implicit cm: ClassTag[T]) = + implicit def buildableArray[T](implicit cm: ClassManifest[T]) = new Buildable[T,Array] { def builder = mutable.ArrayBuilder.make[T] } diff --git a/src/scalacheck/org/scalacheck/util/CmdLineParser.scala b/src/scalacheck/org/scalacheck/util/CmdLineParser.scala index 4683c34a65..eb3a91fe59 100644 --- a/src/scalacheck/org/scalacheck/util/CmdLineParser.scala +++ b/src/scalacheck/org/scalacheck/util/CmdLineParser.scala @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------*\ ** ScalaCheck ** -** Copyright (c) 2007-2011 Rickard Nilsson. All rights reserved. ** +** Copyright (c) 2007-2013 Rickard Nilsson. All rights reserved. ** ** http://www.scalacheck.org ** ** ** ** This software is released under the terms of the Revised BSD License. ** @@ -30,7 +30,7 @@ trait CmdLineParser extends Parsers { trait StrOpt extends Opt[String] class OptMap { - private val opts = new scala.collection.mutable.HashMap[Opt[_], Any] + 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 diff --git a/src/scalacheck/org/scalacheck/util/FreqMap.scala b/src/scalacheck/org/scalacheck/util/FreqMap.scala index c7474d3b87..d0686aec72 100644 --- a/src/scalacheck/org/scalacheck/util/FreqMap.scala +++ b/src/scalacheck/org/scalacheck/util/FreqMap.scala @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------*\ ** ScalaCheck ** -** Copyright (c) 2007-2011 Rickard Nilsson. All rights reserved. ** +** Copyright (c) 2007-2013 Rickard Nilsson. All rights reserved. ** ** http://www.scalacheck.org ** ** ** ** This software is released under the terms of the Revised BSD License. ** diff --git a/src/scalacheck/org/scalacheck/util/StdRand.scala b/src/scalacheck/org/scalacheck/util/StdRand.scala index 317b0ccd10..7c1dc8dcc4 100644 --- a/src/scalacheck/org/scalacheck/util/StdRand.scala +++ b/src/scalacheck/org/scalacheck/util/StdRand.scala @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------*\ ** ScalaCheck ** -** Copyright (c) 2007-2011 Rickard Nilsson. All rights reserved. ** +** Copyright (c) 2007-2013 Rickard Nilsson. All rights reserved. ** ** http://www.scalacheck.org ** ** ** ** This software is released under the terms of the Revised BSD License. ** diff --git a/test/files/scalacheck/HashTrieSplit.scala b/test/files/scalacheck/HashTrieSplit.scala deleted file mode 100644 index 908c878f54..0000000000 --- a/test/files/scalacheck/HashTrieSplit.scala +++ /dev/null @@ -1,47 +0,0 @@ - - - - - -import collection._ - - - - -// checks whether hash tries split their iterators correctly -// even after some elements have been traversed -object Test { - def main(args: Array[String]) { - doesSplitOk - } - - def doesSplitOk = { - val sz = 2000 - var ht = new parallel.immutable.ParHashMap[Int, Int] - // println("creating trie") - for (i <- 0 until sz) ht += ((i + sz, i)) - // println("created trie") - for (n <- 0 until (sz - 1)) { - // println("---------> n = " + n) - val pit = ht.splitter - val pit2 = ht.splitter - var i = 0 - while (i < n) { - pit.next - pit2.next - i += 1 - } - // println("splitting") - val pits = pit.split - val fst = pits(0).toSet - val snd = pits(1).toSet - val orig = pit2.toSet - if (orig.size != (fst.size + snd.size) || orig != (fst ++ snd)) { - println("Original: " + orig) - println("First: " + fst) - println("Second: " + snd) - assert(false) - } - } - } -} diff --git a/test/files/scalacheck/parallel-collections/pc.scala b/test/files/scalacheck/parallel-collections/pc.scala index 0a91977da0..e6b6b4856d 100644 --- a/test/files/scalacheck/parallel-collections/pc.scala +++ b/test/files/scalacheck/parallel-collections/pc.scala @@ -1,12 +1,11 @@ - - - +/* + * scalac: -deprecation + * scalacheck: -workers 1 -minSize 0 -maxSize 4000 -minSuccessfulTests 5 + */ import org.scalacheck._ - import scala.collection.parallel._ - class ParCollProperties extends Properties("Parallel collections") { /* Collections */ @@ -35,8 +34,8 @@ class ParCollProperties extends Properties("Parallel collections") { include(immutable.IntParallelVectorCheck) } - -object Test { +object Test extends ParCollProperties { + /* def main(args: Array[String]) { val pc = new ParCollProperties org.scalacheck.Test.checkProperties( @@ -51,4 +50,5 @@ object Test { pc ) } + */ } diff --git a/test/partest b/test/partest index e3270f8eaa..99a731a49b 100755 --- a/test/partest +++ b/test/partest @@ -129,6 +129,7 @@ fi $JAVA_OPTS -cp "$EXT_CLASSPATH" \ ${partestDebugStr} \ "$color_opts" \ + -Dfile.encoding=UTF-8 \ -Dscala.home="${SCALA_HOME}" \ -Dpartest.javacmd="${JAVACMD}" \ -Dpartest.java_opts="${JAVA_OPTS}" \ diff --git a/test/scaladoc/scalacheck/CommentFactoryTest.scala b/test/scaladoc/scalacheck/CommentFactoryTest.scala index 96174d29d1..28043e5a06 100644 --- a/test/scaladoc/scalacheck/CommentFactoryTest.scala +++ b/test/scaladoc/scalacheck/CommentFactoryTest.scala @@ -45,7 +45,7 @@ object Test extends Properties("CommentFactory") { with MemberLookup) } - def parse(src: String, dst: Inline) = { + def parse(src: String, dst: Inline): Boolean = { factory.parseComment(src) match { case Some(inline) => inline == dst diff --git a/test/scaladoc/scalacheck/HtmlFactoryTest.scala b/test/scaladoc/scalacheck/HtmlFactoryTest.scala index d7b5e48288..03348b81d2 100644 --- a/test/scaladoc/scalacheck/HtmlFactoryTest.scala +++ b/test/scaladoc/scalacheck/HtmlFactoryTest.scala @@ -2,6 +2,8 @@ import org.scalacheck._ import org.scalacheck.Prop._ import java.net.{URLClassLoader, URLDecoder} +import scala.collection.mutable +import scala.xml.NodeSeq object XMLUtil { import scala.xml._ @@ -34,21 +36,24 @@ object Test extends Properties("HtmlFactory") { // this test previously relied on the assumption that the current thread's classloader is an url classloader and contains all the classpaths // does partest actually guarantee this? to quote Leonard Nimoy: The answer, of course, is no. // this test _will_ fail again some time in the future. - val paths = Thread.currentThread.getContextClassLoader.asInstanceOf[URLClassLoader].getURLs.map(u => URLDecoder.decode(u.getPath)) - val morepaths = Thread.currentThread.getContextClassLoader.getParent.asInstanceOf[URLClassLoader].getURLs.map(u => URLDecoder.decode(u.getPath)) - (paths ++ morepaths).mkString(java.io.File.pathSeparator) + // Footnote: java.lang.ClassCastException: org.apache.tools.ant.loader.AntClassLoader5 cannot be cast to java.net.URLClassLoader + val loader = Thread.currentThread.getContextClassLoader.asInstanceOf[URLClassLoader] + val paths = loader.getURLs.map(u => URLDecoder.decode(u.getPath)) + paths mkString java.io.File.pathSeparator } def createFactory = { val settings = new Settings({Console.err.println(_)}) + settings.scaladocQuietRun = true + settings.nowarn.value = true settings.classpath.value = getClasspath val reporter = new scala.tools.nsc.reporters.ConsoleReporter(settings) new DocFactory(reporter, settings) } - def createTemplates(basename: String) = { - val result = scala.collection.mutable.Map[String, scala.xml.NodeSeq]() + def createTemplates(basename: String): collection.Map[String, NodeSeq] = { + val result = mutable.Map[String, NodeSeq]() createFactory.makeUniverse(Left(List(RESOURCES+basename))) match { case Some(universe) => { @@ -57,7 +62,7 @@ object Test extends Properties("HtmlFactory") { result += (page.absoluteLinkTo(page.path) -> page.body) }) } - case _ => ; + case _ => } result diff --git a/test/scaladoc/scalacheck/IndexScriptTest.scala b/test/scaladoc/scalacheck/IndexScriptTest.scala index 37f6947aaa..b8b9f92965 100644 --- a/test/scaladoc/scalacheck/IndexScriptTest.scala +++ b/test/scaladoc/scalacheck/IndexScriptTest.scala @@ -8,14 +8,20 @@ import java.net.{URLClassLoader, URLDecoder} object Test extends Properties("IndexScript") { def getClasspath = { - val loader = Thread.currentThread.getContextClassLoader - val paths = loader.asInstanceOf[URLClassLoader].getURLs - val morepaths = loader.getParent.asInstanceOf[URLClassLoader].getURLs - (paths ++ morepaths).map(u => URLDecoder.decode(u.getPath)).mkString(java.io.File.pathSeparator) + // these things can be tricky + // this test previously relied on the assumption that the current thread's classloader is an url classloader and contains all the classpaths + // does partest actually guarantee this? to quote Leonard Nimoy: The answer, of course, is no. + // this test _will_ fail again some time in the future. + // Footnote: java.lang.ClassCastException: org.apache.tools.ant.loader.AntClassLoader5 cannot be cast to java.net.URLClassLoader + val loader = Thread.currentThread.getContextClassLoader.asInstanceOf[URLClassLoader] + val paths = loader.getURLs.map(u => URLDecoder.decode(u.getPath)) + paths mkString java.io.File.pathSeparator } val docFactory = { val settings = new doc.Settings({Console.err.println(_)}) + settings.scaladocQuietRun = true + settings.nowarn.value = true settings.classpath.value = getClasspath val reporter = new scala.tools.nsc.reporters.ConsoleReporter(settings) new doc.DocFactory(reporter, settings) diff --git a/test/scaladoc/scalacheck/IndexTest.scala b/test/scaladoc/scalacheck/IndexTest.scala index dc4ab126d4..abc0e5da01 100644 --- a/test/scaladoc/scalacheck/IndexTest.scala +++ b/test/scaladoc/scalacheck/IndexTest.scala @@ -12,19 +12,19 @@ object Test extends Properties("Index") { // this test previously relied on the assumption that the current thread's classloader is an url classloader and contains all the classpaths // does partest actually guarantee this? to quote Leonard Nimoy: The answer, of course, is no. // this test _will_ fail again some time in the future. - val paths = Thread.currentThread.getContextClassLoader.asInstanceOf[URLClassLoader].getURLs.map(u => URLDecoder.decode(u.getPath)) - val morepaths = Thread.currentThread.getContextClassLoader.getParent.asInstanceOf[URLClassLoader].getURLs.map(u => URLDecoder.decode(u.getPath)) - (paths ++ morepaths).mkString(java.io.File.pathSeparator) + // Footnote: java.lang.ClassCastException: org.apache.tools.ant.loader.AntClassLoader5 cannot be cast to java.net.URLClassLoader + val loader = Thread.currentThread.getContextClassLoader.asInstanceOf[URLClassLoader] + val paths = loader.getURLs.map(u => URLDecoder.decode(u.getPath)) + paths mkString java.io.File.pathSeparator } val docFactory = { val settings = new doc.Settings({Console.err.println(_)}) - + settings.scaladocQuietRun = true + settings.nowarn.value = true settings.classpath.value = getClasspath - println(settings.classpath.value) val reporter = new scala.tools.nsc.reporters.ConsoleReporter(settings) - new doc.DocFactory(reporter, settings) } |