From 57fcb8ff15f5fc3f4e1bcc8800e49d083140ec0b Mon Sep 17 00:00:00 2001 From: Ruslan Shevchenko Date: Fri, 23 Mar 2012 16:50:32 +0200 Subject: [PATCH] added infrastruture for running grouped tests. --- .../managedfixture/FeatureSpec.scala | 4 +- .../scalatest/managedfixture/FlatSpec.scala | 17 ++++- .../managedfixture/FlatSpecGroup.scala | 21 ++++++ .../scalatest/managedfixture/Grouped.scala | 35 ++++++++++ .../scalatest/managedfixture/SpecGroup.scala | 39 +++++++++++ .../managedfixture/ReflectionUtils.scala | 67 +++++++++++++++++++ 6 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 src/main/scala/org/scalatest/managedfixture/FlatSpecGroup.scala create mode 100644 src/main/scala/org/scalatest/managedfixture/Grouped.scala create mode 100644 src/main/scala/org/scalatest/managedfixture/SpecGroup.scala create mode 100644 src/main/scala/ua/gradsoft/managedfixture/ReflectionUtils.scala diff --git a/src/main/scala/org/scalatest/managedfixture/FeatureSpec.scala b/src/main/scala/org/scalatest/managedfixture/FeatureSpec.scala index 7e425da..30243b0 100644 --- a/src/main/scala/org/scalatest/managedfixture/FeatureSpec.scala +++ b/src/main/scala/org/scalatest/managedfixture/FeatureSpec.scala @@ -108,7 +108,9 @@ trait FeatureSpec[T <: FixtureStateTypes] extends fixture.Suite **/ def fixtureStateTypes: T - protected override lazy val internalSpec = new InternalFeatureSpec[T](this); + + protected override lazy val internalSpec: InternalFeatureSpec[T] = new InternalFeatureSpec[T](this); + override def withFixture(test: OneArgTest): Unit = throw new IllegalStateException("You can't call withFixture diretly in managedfixture"); diff --git a/src/main/scala/org/scalatest/managedfixture/FlatSpec.scala b/src/main/scala/org/scalatest/managedfixture/FlatSpec.scala index a91bfd9..ef83c32 100644 --- a/src/main/scala/org/scalatest/managedfixture/FlatSpec.scala +++ b/src/main/scala/org/scalatest/managedfixture/FlatSpec.scala @@ -202,7 +202,17 @@ trait FlatSpec[T <: FixtureStateTypes] extends Suite with ShouldVerb with MustVe // here we recreate internal suite and will be pass to one all 'real' functionality. - private[scalatest] lazy val internalSpec = new InternalFlatSpec(this); + private[scalatest] lazy val internalSpec: InternalFlatSpec[T] = + if (this.isInstanceOf[Grouped]) { + if (GroupSpecConstructorKluge.currentOwner!=None) { + GroupSpecConstructorKluge.currentOwner.value.get.asInstanceOf[FlatSpecGroup[T]].internalSpec; + } else { + // it was called outside group, create internal constructor + new InternalFlatSpec(this); + } + } else { + new InternalFlatSpec(this); + } implicit protected def info: Informer = internalSpec._info; @@ -380,10 +390,15 @@ trait FlatSpec[T <: FixtureStateTypes] extends Suite with ShouldVerb with MustVe override def run(testName: Option[String], reporter: Reporter, stopper: Stopper, filter: Filter, configMap: Map[String, Any], distributor: Option[Distributor], tracker: Tracker) { + if (isGrouped) { + this.asInstanceOf[Grouped].checkGroupExists( classOf[FlatSpecGroup[_]] ); + } internalSpec.run(testName, reporter, stopper, filter, configMap, distributor, tracker); } protected val behave = new BehaveWord + + private def isGrouped: Boolean = this.isInstanceOf[Grouped]; } private[scalatest] object FlatSpecConstructorKluge diff --git a/src/main/scala/org/scalatest/managedfixture/FlatSpecGroup.scala b/src/main/scala/org/scalatest/managedfixture/FlatSpecGroup.scala new file mode 100644 index 0000000..2d8737b --- /dev/null +++ b/src/main/scala/org/scalatest/managedfixture/FlatSpecGroup.scala @@ -0,0 +1,21 @@ +package org.scalatest.managedfixture + + +import scala.util.DynamicVariable +import org.scalatest._ +import ua.gradsoft.managedfixture._ + + +abstract class FlatSpecGroup[T <: FixtureStateTypes] extends FlatSpec[T] + with SpecGroup +{ + + override def run(testName: Option[String], reporter: Reporter, stopper: Stopper, filter: Filter, + configMap: Map[String, Any], distributor: Option[Distributor], tracker: Tracker) { + collectGrouped(classOf[FlatSpec[T]]); + internalSpec.run(testName, reporter, stopper, filter, configMap, distributor, tracker); + } + +} + + diff --git a/src/main/scala/org/scalatest/managedfixture/Grouped.scala b/src/main/scala/org/scalatest/managedfixture/Grouped.scala new file mode 100644 index 0000000..a2a9ff6 --- /dev/null +++ b/src/main/scala/org/scalatest/managedfixture/Grouped.scala @@ -0,0 +1,35 @@ +package org.scalatest.managedfixture + +import ua.gradsoft.managedfixture._ + +trait Grouped { + + + + /** + * check - are we have instance of clazz somewhere in our package structure + * upper to current class. + * [todo - check same fixture state types (?)] + */ + def checkGroupExists(clazz:Class[_]):Boolean = + { + val pkg = clazz.getPackage(); + val components = pkg.getName().split('.'); + if (components.length > 1) { + var i=components.length; + var isFound = false; + while( i > 0 && !isFound ) { + isFound = ! ( ReflectionUtils.findClasses(components.take(i).mkString("."), + { (x: Class[_]) => + !(classOf[FlatSpecGroup[_]].isAssignableFrom(x)) + }, false) ); + } + isFound; + }else{ + false + } + } + + def mark(owner:SpecGroup):Unit = {} + +} \ No newline at end of file diff --git a/src/main/scala/org/scalatest/managedfixture/SpecGroup.scala b/src/main/scala/org/scalatest/managedfixture/SpecGroup.scala new file mode 100644 index 0000000..e3462f8 --- /dev/null +++ b/src/main/scala/org/scalatest/managedfixture/SpecGroup.scala @@ -0,0 +1,39 @@ +package org.scalatest.managedfixture + +import ua.gradsoft.managedfixture._ +import scala.util.DynamicVariable + +trait SpecGroup +{ + + def checkClass[T](cl:Class[T]): Boolean + = true; + + def checkObject(x:AnyRef): Boolean + = true; + + private[scalatest] def collectGrouped[T](cl:Class[T]):Boolean = + { + ReflectionUtils.findClasses(this.getClass().getPackage().getName, + { + (x:Class[_]) => + if (x.isInstanceOf[Grouped] && x.isInstanceOf[T]) { + if (checkClass(x)) { + GroupSpecConstructorKluge.currentOwner.withValue(Some(this)) { + val obj = x.newInstance().asInstanceOf[Grouped]; + obj.mark(this); + } + } + } + true + }, + true); + } + + +} + +private[scalatest] object GroupSpecConstructorKluge +{ + val currentOwner = new DynamicVariable[Option[SpecGroup]](None); +} diff --git a/src/main/scala/ua/gradsoft/managedfixture/ReflectionUtils.scala b/src/main/scala/ua/gradsoft/managedfixture/ReflectionUtils.scala new file mode 100644 index 0000000..bbd2fc6 --- /dev/null +++ b/src/main/scala/ua/gradsoft/managedfixture/ReflectionUtils.scala @@ -0,0 +1,67 @@ +package ua.gradsoft.managedfixture + +import java.io._ +import java.util.zip._ + +/** + * helper class, which simplicify work with reflection + */ +object ReflectionUtils { + + + def findClasses(packageName:String, testFun: Class[_]=>Boolean, recursive:Boolean):Boolean = + { + val e = classLoader.getResources(packageName); + var more = true; + while(e.hasMoreElements && more) { + val url = e.nextElement(); + more = forClassesInDir(packageName,url.getFile, testFun, recursive); + } + more + } + + private[this] def forClassesInDir(pkgName:String, dir:String, fun: Class[_]=>Boolean, recursive: Boolean):Boolean = + { + var more = true; + if (dir.startsWith("file:") && dir.contains("!") ) { + val jar = new java.net.URL(dir.split("!")(0)); + val zip = new ZipInputStream(jar.openStream()); + var zipEntry = zip.getNextEntry; + while(zipEntry!=null && more) { + val className = zipEntry.getName.replaceAll("[$].*", "").replaceAll("[.]class","").replace('/', '.'); + val c = Class.forName(className); + more = fun(c) + zipEntry = zip.getNextEntry; + } + } else { + var fd = new File(dir); + if (fd.exists) { + for(f <- fd.listFiles) { + var fname = f.getName; + if (fname.endsWith(".class")) { + val className = pkgName+"."+ fname.substring(0, fname.length-6); + val c = Class.forName(className); + more = fun(c); + } else if (f.isDirectory() && recursive) { + more = forClassesInDir(pkgName+"."+f.getName(),dir+"/"+f.getName,fun,recursive) + } + if (!more) { + return false; + } + } + } + } + more; + } + + private[this] def classLoader: ClassLoader = + { + Option(Thread.currentThread().getContextClassLoader()) match { + case Some(x) => x + case None => this.getClass.getClassLoader(); + } + } + + + +} \ No newline at end of file