mirror of https://github.com/sbt/sbt.git
Tests and fixes for analysis plugin and the task scheduler.
This commit is contained in:
parent
ec7074a340
commit
a70ddd8e32
|
|
@ -15,13 +15,13 @@ import xsbti.{AnalysisCallback, AnalysisCallbackContainer}
|
|||
class Analyzer(val global: Global) extends Plugin
|
||||
{
|
||||
val callback = global.asInstanceOf[AnalysisCallbackContainer].analysisCallback
|
||||
|
||||
|
||||
import global._
|
||||
|
||||
|
||||
val name = "xsbt-analyze"
|
||||
val description = "A plugin to find all concrete instances of a given class and extract dependency information."
|
||||
val components = List[PluginComponent](Component)
|
||||
|
||||
|
||||
/* ================================================== */
|
||||
// These two templates abuse scope for source compatibility between Scala 2.7.x and 2.8.x so that a single
|
||||
// sbt codebase compiles with both series of versions.
|
||||
|
|
@ -39,7 +39,7 @@ class Analyzer(val global: Global) extends Plugin
|
|||
override val runsAfter = afterPhase :: runsBefore
|
||||
}
|
||||
/* ================================================== */
|
||||
|
||||
|
||||
private object Component extends CompatiblePluginComponent("jvm")
|
||||
{
|
||||
val global = Analyzer.this.global
|
||||
|
|
@ -53,16 +53,8 @@ class Analyzer(val global: Global) extends Plugin
|
|||
def run
|
||||
{
|
||||
val outputDirectory = new File(global.settings.outdir.value)
|
||||
val superclassNames = callback.superclassNames.map(newTermName)
|
||||
val superclassesAll =
|
||||
for(name <- superclassNames) yield
|
||||
{
|
||||
try { Some(global.definitions.getClass(name)) }
|
||||
catch { case fe: scala.tools.nsc.FatalError => callback.superclassNotFound(name.toString); None }
|
||||
}
|
||||
val superclasses = superclassesAll.filter(_.isDefined).map(_.get)
|
||||
//println("Superclass names: " + superclassNames.mkString(", ") + "\n\tall: " + superclasses.mkString(", "))
|
||||
|
||||
val superclasses = callback.superclassNames flatMap(classForName)
|
||||
|
||||
for(unit <- currentRun.units)
|
||||
{
|
||||
// build dependencies structure
|
||||
|
|
@ -90,7 +82,7 @@ class Analyzer(val global: Global) extends Plugin
|
|||
else
|
||||
callback.sourceDependency(onSource.file, sourceFile)
|
||||
}
|
||||
|
||||
|
||||
// find subclasses and modules with main methods
|
||||
for(clazz @ ClassDef(mods, n, _, _) <- unit.body)
|
||||
{
|
||||
|
|
@ -105,7 +97,7 @@ class Analyzer(val global: Global) extends Plugin
|
|||
callback.foundApplication(sourceFile, sym.fullNameString)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// build list of generated classes
|
||||
for(iclass <- unit.icode)
|
||||
{
|
||||
|
|
@ -129,11 +121,25 @@ class Analyzer(val global: Global) extends Plugin
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private def classForName(name: String) =
|
||||
{
|
||||
try
|
||||
{
|
||||
if(name.indexOf('.') < 0)
|
||||
{
|
||||
val sym = definitions.EmptyPackageClass.info.member(newTypeName(name))
|
||||
if(sym != NoSymbol) Some( sym ) else { callback.superclassNotFound(name); None }
|
||||
}
|
||||
else
|
||||
Some( global.definitions.getClass(newTermName(name)) )
|
||||
}
|
||||
catch { case fe: scala.tools.nsc.FatalError => callback.superclassNotFound(name); None }
|
||||
}
|
||||
private def classFile(sym: Symbol): Option[AbstractFile] =
|
||||
{
|
||||
import scala.tools.nsc.symtab.Flags
|
||||
val name = sym.fullNameString(java.io.File.separatorChar) + (if (sym.hasFlag(Flags.MODULE)) "$" else "")
|
||||
val name = sym.fullNameString(File.separatorChar) + (if (sym.hasFlag(Flags.MODULE)) "$" else "")
|
||||
val entry = classPath.root.find(name, false)
|
||||
if (entry ne null)
|
||||
Some(entry.classFile)
|
||||
|
|
@ -148,7 +154,7 @@ class Analyzer(val global: Global) extends Plugin
|
|||
else
|
||||
None
|
||||
}
|
||||
|
||||
|
||||
private def isTopLevelModule(sym: Symbol): Boolean =
|
||||
atPhase (currentRun.picklerPhase.next) {
|
||||
sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass
|
||||
|
|
@ -169,29 +175,32 @@ class Analyzer(val global: Global) extends Plugin
|
|||
else
|
||||
new File(packageFile(outputDirectory, s.owner.enclClass), s.simpleName.toString)
|
||||
}
|
||||
|
||||
|
||||
private def hasMainMethod(sym: Symbol): Boolean =
|
||||
{
|
||||
val main = sym.info.nonPrivateMember(newTermName("main"))//nme.main)
|
||||
main.tpe match
|
||||
{
|
||||
case OverloadedType(pre, alternatives) => alternatives.exists(alt => isVisible(alt) && isMainType(pre.memberType(alt)))
|
||||
case tpe => isVisible(main) && isMainType(main.owner.thisType.memberType(main))
|
||||
atPhase(currentRun.typerPhase.next) {
|
||||
main.tpe match
|
||||
{
|
||||
case OverloadedType(pre, alternatives) => alternatives.exists(alt => isVisible(alt) && isMainType(pre.memberType(alt)))
|
||||
case tpe => isVisible(main) && isMainType(main.owner.thisType.memberType(main))
|
||||
}
|
||||
}
|
||||
}
|
||||
private def isVisible(sym: Symbol) = sym != NoSymbol && sym.isPublic && !sym.isDeferred
|
||||
private def isMainType(tpe: Type) =
|
||||
private def isMainType(tpe: Type): Boolean =
|
||||
{
|
||||
tpe match
|
||||
{
|
||||
// singleArgument is of type Symbol in 2.8.0 and type Type in 2.7.x
|
||||
case MethodType(List(singleArgument), result) => isUnitType(result) && isStringArray(singleArgument)
|
||||
case _ => false
|
||||
case PolyType(typeParams, result) => isMainType(result)
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
private lazy val StringArrayType = appliedType(definitions.ArrayClass.typeConstructor, definitions.StringClass.tpe :: Nil)
|
||||
// isStringArray is overloaded to handle the incompatibility between 2.7.x and 2.8.0
|
||||
private def isStringArray(tpe: Type): Boolean = tpe.typeSymbol == StringArrayType.typeSymbol
|
||||
private def isStringArray(tpe: Type): Boolean = tpe =:= StringArrayType
|
||||
private def isStringArray(sym: Symbol): Boolean = isStringArray(sym.tpe)
|
||||
private def isUnitType(tpe: Type) = tpe.typeSymbol == definitions.UnitClass
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
package xsbt
|
||||
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import org.specs.Specification
|
||||
|
||||
/** Verifies that the analyzer plugin properly detects main methods. The main method must be
|
||||
* public with the right signature and be defined on a public, top-level module.*/
|
||||
object ApplicationsTest extends Specification
|
||||
{
|
||||
val sourceContent =
|
||||
"""
|
||||
object Main { def main(args: Array[String]) {} }
|
||||
""" :: """
|
||||
class Main2 { def main(args: Array[String]) {} }
|
||||
""" :: """
|
||||
object Main3 { private def main(args: Array[String]) {} }
|
||||
private object Main3b extends Main2
|
||||
object Main3c { private def main(args: Array[String]) {} }
|
||||
protected object Main3d { def main(args: Array[String]) {} }
|
||||
object Main3e {
|
||||
protected def main(args: Array[String]) {}
|
||||
}
|
||||
package a {
|
||||
object Main3f { private[a] def main(args: Array[String]) {} }
|
||||
object Main3g { protected[a] def main(args: Array[String]) {} }
|
||||
}
|
||||
""" ::"""
|
||||
object Main4 extends Main2
|
||||
""" :: """
|
||||
trait Main5 { def main(args: Array[String]) {} }; trait Main5b extends Main5; trait Main5c extends Main2; abstract class Main5d { def main(args: Array[String]) {} }
|
||||
""" :: """
|
||||
object Main6a { var main = () }
|
||||
object Main6b { var main = (args: Array[String]) => () }
|
||||
""" :: """
|
||||
object Main7 { object Main7b extends Main2 }
|
||||
""" :: """
|
||||
object Main8 extends Main2 { object Main7b extends Main2 }
|
||||
""" :: """
|
||||
object Main9 {
|
||||
def main() {}
|
||||
def main(i: Int) {}
|
||||
def main(args: Array[String]) {}
|
||||
}
|
||||
""" :: """
|
||||
object MainA {
|
||||
def main() {}
|
||||
def main(i: Int) {}
|
||||
def main(args: Array[String], other: String) {}
|
||||
def main(i: Array[Int]) {}
|
||||
}
|
||||
object MainA2 {
|
||||
def main[T](args: Array[T]) {}
|
||||
}
|
||||
""" :: """
|
||||
object MainB extends Main2 {
|
||||
def main() {}
|
||||
def main(i: Int) {}
|
||||
}
|
||||
""" :: """
|
||||
object MainC1 {
|
||||
def main(args: Array[String]) = 3
|
||||
}
|
||||
object MainC2 {
|
||||
def main1(args: Array[String]) {}
|
||||
}
|
||||
""" :: """
|
||||
object MainD1 {
|
||||
val main = ()
|
||||
}
|
||||
object MainD2 {
|
||||
val main = (args: Array[String]) => ()
|
||||
}
|
||||
""" :: """
|
||||
object MainE1 {
|
||||
type T = String
|
||||
def main(args: Array[T]) {}
|
||||
}
|
||||
object MainE2 {
|
||||
type AT = Array[String]
|
||||
def main(args: AT) {}
|
||||
}
|
||||
object MainE3 {
|
||||
type U = Unit
|
||||
type T = String
|
||||
def main(args: Array[T]): U = ()
|
||||
}
|
||||
object MainE4 {
|
||||
def main[T](args: Array[String]) {}
|
||||
}
|
||||
object MainE5 {
|
||||
type A[T] = Array[String]
|
||||
def main[T](args: A[T]) {}
|
||||
}
|
||||
""" ::
|
||||
Nil
|
||||
val sources = for((source, index) <- sourceContent.zipWithIndex) yield new File("Main" + (index+1) + ".scala") -> source
|
||||
|
||||
"Analysis plugin should detect applications" in {
|
||||
WithFiles(sources : _*) { case files @ Seq(main, main2, main3, main4, main5, main6, main7, main8, main9, mainA, mainB, mainC, mainD, mainE) =>
|
||||
CallbackTest(files, Nil) { (callback, file, log) =>
|
||||
val expected = Seq( main -> "Main", main4 -> "Main4", main8 -> "Main8", main9 -> "Main9", mainB -> "MainB",
|
||||
mainE -> "MainE1", mainE -> "MainE2", mainE -> "MainE3", mainE -> "MainE4", mainE -> "MainE5" )
|
||||
(callback.applications) must haveTheSameElementsAs(expected)
|
||||
val loader = new URLClassLoader(Array(file.toURI.toURL), getClass.getClassLoader)
|
||||
for( (_, className) <- expected) testRun(loader, className)
|
||||
}
|
||||
}
|
||||
}
|
||||
private def testRun(loader: ClassLoader, className: String)
|
||||
{
|
||||
val obj = Class.forName(className+"$", true, loader)
|
||||
val singletonField = obj.getField("MODULE$")
|
||||
val singleton = singletonField.get(null)
|
||||
singleton.asInstanceOf[{def main(args: Array[String]): Unit}].main(Array[String]())
|
||||
}
|
||||
}
|
||||
|
|
@ -7,60 +7,18 @@ object CheckBasic extends Specification
|
|||
{
|
||||
val basicName = new File("Basic.scala")
|
||||
val basicSource = "package org.example { object Basic }"
|
||||
|
||||
val mainName = new File("Main.scala")
|
||||
val mainSource = "object Main { def main(args: Array[String]) {} }"
|
||||
|
||||
val super1Name = new File("a/Super.scala")
|
||||
val super2Name = new File("a/Super2.scala")
|
||||
val midName = new File("b/Middle.scala")
|
||||
val sub1Name = new File("b/SubA.scala")
|
||||
val sub2Name = new File("b/SubB.scala")
|
||||
val sub3Name = new File("SubC.scala")
|
||||
val super1Source = "package a; trait Super"
|
||||
val super2Source = "class Super2"
|
||||
val midSource = "package y.w; trait Mid extends a.Super"
|
||||
val subSource1 = "package a; trait Sub1 extends y.w.Mid"
|
||||
val subSource2 = "trait Sub2 extends a.Super"
|
||||
val subSource3 = "private class F extends a.Super; package c { object Sub3 extends Super2 }"
|
||||
|
||||
|
||||
"Compiling basic file should succeed" in {
|
||||
WithFiles(basicName -> basicSource){ files =>
|
||||
TestCompile(files){ loader => Class.forName("org.example.Basic", false, loader) }
|
||||
true must be(true) // don't know how to just check that previous line completes without exception
|
||||
}
|
||||
}
|
||||
|
||||
"Analysis plugin" should {
|
||||
"send source begin and end" in {
|
||||
WithFiles(basicName -> basicSource) { files =>
|
||||
CallbackTest(files) { callback =>
|
||||
(callback.beganSources) must haveTheSameElementsAs(files)
|
||||
(callback.endedSources) must haveTheSameElementsAs(files)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"detect applications" in {
|
||||
WithFiles(mainName -> mainSource ) { files =>
|
||||
CallbackTest(files) { callback =>
|
||||
(callback.applications) must haveTheSameElementsAs(files.map(file => (file, "Main")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"detect subclasses" in {
|
||||
WithFiles(super1Name -> super1Source, midName -> midSource, sub1Name -> subSource1, sub2Name -> subSource2,
|
||||
super2Name -> super2Source, sub3Name -> subSource3)
|
||||
{
|
||||
case files @ Seq(supFile, midFile, sub1File, sub2File, sup2File, sub3File) =>
|
||||
CallbackTest(files,Seq( "a.Super", "Super2", "x.Super3")) { (callback, ignore, ignore2) =>
|
||||
val expected = (sub1File, "a.Super", "a.Sub1", false) :: (sub2File, "a.Super", "a.Sub2", false) ::
|
||||
(sub3File, "Super2", "Sub3", true) :: Nil
|
||||
//println(callback.foundSubclasses)
|
||||
//println(callback.invalidSuperclasses)
|
||||
(callback.foundSubclasses) must haveTheSameElementsAs(expected)
|
||||
(callback.invalidSuperclasses) must haveTheSameElementsAs(Seq("x.Super3"))
|
||||
}
|
||||
"Analyzer plugin should send source begin and end" in {
|
||||
WithFiles(basicName -> basicSource) { files =>
|
||||
CallbackTest(files) { callback =>
|
||||
(callback.beganSources) must haveTheSameElementsAs(files)
|
||||
(callback.endedSources) must haveTheSameElementsAs(files)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
package xsbt
|
||||
|
||||
import java.io.File
|
||||
import org.specs.Specification
|
||||
|
||||
object DetectSubclasses extends Specification
|
||||
{
|
||||
val sources =
|
||||
("a/Super.scala" -> "package a; trait Super") ::
|
||||
("a/Super2.scala" -> "class Super2") ::
|
||||
("b/Middle.scala" -> "package y.w; trait Mid extends a.Super") ::
|
||||
("b/Sub1.scala" -> "package a; class Sub1 extends y.w.Mid") ::
|
||||
("b/Sub2.scala" -> "final class Sub2 extends a.Super") ::
|
||||
("Sub3.scala" -> "private class F extends a.Super; package c { object Sub3 extends Super2 }") ::
|
||||
Nil
|
||||
|
||||
"Analysis plugin should detect subclasses" in {
|
||||
WithFiles(sources.map{case (file, content) => (new File(file), content)} : _*)
|
||||
{
|
||||
case files @ Seq(supFile, sup2File, midFile, sub1File, sub2File, sub3File) =>
|
||||
CallbackTest(files, Seq( "a.Super", "Super2", "x.Super3", "Super4") ) { (callback, x, xx) =>
|
||||
val expected =
|
||||
(sub1File, "a.Sub1", "a.Super", false) ::
|
||||
(sub2File, "Sub2", "a.Super", false) ::
|
||||
(sup2File, "Super2", "Super2", false) ::
|
||||
(sub3File, "c.Sub3", "Super2", true) ::
|
||||
Nil
|
||||
(callback.foundSubclasses) must haveTheSameElementsAs(expected)
|
||||
(callback.invalidSuperclasses) must haveTheSameElementsAs(Seq("x.Super3", "Super4"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,8 @@ import FileUtilities.{classLocationFile, withTemporaryDirectory, write}
|
|||
|
||||
object TestCompile
|
||||
{
|
||||
/** Tests running the compiler interface with the analyzer plugin with a test callback. The test callback saves all information
|
||||
* that the plugin sends it for post-compile analysis by the provided function.*/
|
||||
def apply[T](arguments: Seq[String], superclassNames: Seq[String])(f: (TestCallback, Logger) => T): T =
|
||||
{
|
||||
val pluginLocation = classLocationFile[Analyzer]
|
||||
|
|
@ -21,6 +23,8 @@ object TestCompile
|
|||
f(testCallback, log)
|
||||
}
|
||||
}
|
||||
/** Tests running the compiler interface with the analyzer plugin. The provided function is given a ClassLoader that can
|
||||
* load the compiled classes..*/
|
||||
def apply[T](sources: Seq[File])(f: ClassLoader => T): T =
|
||||
CallbackTest.apply(sources, Nil){ case (callback, outputDir, log) => f(new URLClassLoader(Array(outputDir.toURI.toURL))) }
|
||||
}
|
||||
|
|
@ -38,6 +42,9 @@ object CallbackTest
|
|||
}
|
||||
object WithFiles
|
||||
{
|
||||
/** Takes the relative path -> content pairs and writes the content to a file in a temporary directory. The written file
|
||||
* path is the relative path resolved against the temporary directory path. The provided function is called with the resolved file paths
|
||||
* in the same order as the inputs. */
|
||||
def apply[T](sources: (File, String)*)(f: Seq[File] => T): T =
|
||||
{
|
||||
withTemporaryDirectory { dir =>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@ public interface AnalysisCallback
|
|||
* discovered.*/
|
||||
public void foundSubclass(File source, String subclassName, String superclassName, boolean isModule);
|
||||
/** Called to indicate that the source file <code>source</code> depends on the source file
|
||||
* <code>dependsOn</code>.*/
|
||||
* <code>dependsOn</code>. Note that only source files included in the current compilation will
|
||||
* passed to this method. Dependencies on classes generated by sources not in the current compilation will
|
||||
* be passed as class dependencies to the classDependency method.*/
|
||||
public void sourceDependency(File dependsOn, File source);
|
||||
/** Called to indicate that the source file <code>source</code> depends on the jar
|
||||
* <code>jar</code>.*/
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class XSbt(info: ProjectInfo) extends ParentProject(info)
|
|||
|
||||
def utilPath = path("util")
|
||||
def compilePath = path("compile")
|
||||
|
||||
|
||||
class CommonDependencies(info: ProjectInfo) extends DefaultProject(info)
|
||||
{
|
||||
val sc = "org.scala-tools.testing" % "scalacheck" % "1.5" % "test->default"
|
||||
|
|
@ -35,7 +35,7 @@ class XSbt(info: ProjectInfo) extends ParentProject(info)
|
|||
{
|
||||
//override def compileOptions = super.compileOptions ++ List(Unchecked,ExplainTypes, CompileOption("-Xlog-implicits"))
|
||||
}
|
||||
class Base(info: ProjectInfo) extends DefaultProject(info) with AssemblyProject
|
||||
class Base(info: ProjectInfo) extends DefaultProject(info)
|
||||
{
|
||||
override def scratch = true
|
||||
}
|
||||
|
|
@ -53,7 +53,7 @@ class XSbt(info: ProjectInfo) extends ParentProject(info)
|
|||
// these set up the test so that the classes and resources are both in the output resource directory
|
||||
// the main output path is removed so that the plugin (xsbt.Analyzer) is found in the output resource directory so that
|
||||
// the tests can configure that directory as -Xpluginsdir (which requires the scalac-plugin.xml and the classes to be together)
|
||||
override def testCompileAction = super.testCompileAction dependsOn(packageForTest)
|
||||
override def testCompileAction = super.testCompileAction dependsOn(packageForTest, ioSub.testCompile)
|
||||
override def mainResources = super.mainResources +++ "scalac-plugin.xml"
|
||||
override def testClasspath = (super.testClasspath --- super.mainCompilePath) +++ ioSub.testClasspath +++ testPackagePath
|
||||
def testPackagePath = outputPath / "test.jar"
|
||||
|
|
|
|||
|
|
@ -58,12 +58,12 @@ private final class TaskScheduler[O](root: Task[O], strategy: ScheduleStrategy[W
|
|||
private val strategyRun = strategy.run
|
||||
private val failed = new mutable.HashSet[Task[_]]
|
||||
private val failureReports = new mutable.ArrayBuffer[WorkFailure[Task[_]]]
|
||||
|
||||
|
||||
{
|
||||
val initialized = addGraph(root, root) // TODO: replace second root with something better? (it is ignored here anyway)
|
||||
assert(initialized)
|
||||
}
|
||||
|
||||
|
||||
private def addReady[O](m: Task[O])
|
||||
{
|
||||
def add[I](m: ITask[I,O])
|
||||
|
|
@ -72,7 +72,7 @@ private final class TaskScheduler[O](root: Task[O], strategy: ScheduleStrategy[W
|
|||
strategyRun.workReady(new Work(m, input))
|
||||
listener.runnable(m)
|
||||
}
|
||||
|
||||
|
||||
assert(!forwardDeps.contains(m), m)
|
||||
assert(reverseDeps.contains(m), m)
|
||||
assert(!completed.contains(m), m)
|
||||
|
|
@ -172,20 +172,25 @@ private final class TaskScheduler[O](root: Task[O], strategy: ScheduleStrategy[W
|
|||
retire(dependsOnM, None)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private def success[O](task: Task[O], value: Result[O]): Unit =
|
||||
value match
|
||||
{
|
||||
case NewTask(t) =>
|
||||
if(t == task)
|
||||
if(t eq task)
|
||||
{
|
||||
failureReports += WorkFailure(t, CircularDependency(t, task))
|
||||
retire(task, None)
|
||||
}
|
||||
else if(addGraph(t, task))
|
||||
{
|
||||
calls(t) = task
|
||||
listener.calling(task, t)
|
||||
if(completed.contains(t))
|
||||
retire(task, Some(completed(t)))
|
||||
else
|
||||
{
|
||||
calls(t) = task
|
||||
listener.calling(task, t)
|
||||
}
|
||||
}
|
||||
else
|
||||
retire(task, None)
|
||||
|
|
|
|||
|
|
@ -8,12 +8,9 @@ object TaskRunnerCircularTest extends Properties("TaskRunner Circular")
|
|||
specify("Catches circular references", (intermediate: Int, workers: Int) =>
|
||||
(workers > 0 && intermediate >= 0) ==> checkCircularReferences(intermediate, workers)
|
||||
)
|
||||
/*specify("Check root complete", (intermediate: Int, workers: Int) =>
|
||||
(workers > 0 && intermediate >= 0) ==> checkRootComplete(intermediate, workers)
|
||||
)*/
|
||||
specify("Allows noncircular references", (intermediate: Int, workers: Int) =>
|
||||
specify("Allows references to completed tasks", forAllNoShrink(Arbitrary.arbitrary[(Int,Int)])(x=>x) { case (intermediate: Int, workers: Int) =>
|
||||
(workers > 0 && intermediate >= 0) ==> allowedReference(intermediate, workers)
|
||||
)
|
||||
})
|
||||
final def allowedReference(intermediate: Int, workers: Int) =
|
||||
{
|
||||
val top = Task(intermediate) named("top")
|
||||
|
|
@ -24,7 +21,7 @@ object TaskRunnerCircularTest extends Properties("TaskRunner Circular")
|
|||
else
|
||||
iterate(Task(t-1) named (t-1).toString)
|
||||
}
|
||||
try { checkResult(TaskRunner(iterate(top), workers), 0) }
|
||||
try { checkResult(TaskRunner(iterate(top), workers), intermediate) }
|
||||
catch { case e: CircularDependency => ("Unexpected exception: " + e) |: false }
|
||||
}
|
||||
final def checkCircularReferences(intermediate: Int, workers: Int) =
|
||||
|
|
@ -44,21 +41,4 @@ object TaskRunnerCircularTest extends Properties("TaskRunner Circular")
|
|||
try { TaskRunner(top, workers); false }
|
||||
catch { case TasksFailed(failures) => failures.exists(_.exception.isInstanceOf[CircularDependency]) }
|
||||
}
|
||||
final def checkRootComplete(intermediate: Int, workers: Int) =
|
||||
{
|
||||
val top = Task(intermediate)
|
||||
def iterate(task: Task[Int]): Task[Int] =
|
||||
{
|
||||
lazy val it: Task[Int] =
|
||||
task bind { t =>
|
||||
if(t <= 0)
|
||||
it
|
||||
else
|
||||
iterate(Task(t-1) named (t-1).toString)
|
||||
} named("it")
|
||||
it
|
||||
}
|
||||
try { TaskRunner(iterate(top), workers); false }
|
||||
catch { case e: CircularDependency => true }
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,12 @@ object TaskRunnerForkTest extends Properties("TaskRunner Fork")
|
|||
true
|
||||
}
|
||||
)
|
||||
specify("Fork and reduce 2", (m: Int, workers: Int) =>
|
||||
(workers > 0 && m > 1) ==> {
|
||||
val task = (0 to m) fork {_ * 10} reduce{_ + _}
|
||||
checkResult(TaskRunner(task, workers), 5*(m+1)*m)
|
||||
}
|
||||
)
|
||||
specify("Double join", (a: Int, b: Int, workers: Int) =>
|
||||
(workers > 0) ==> { runDoubleJoin(abs(a),abs(b),workers); true }
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue