mirror of https://github.com/sbt/sbt.git
Fixed scripted test framework to test version of sbt being built. Trying out cross-classloader logger.
git-svn-id: https://simple-build-tool.googlecode.com/svn/trunk@884 d89573ee-9141-11dd-94d4-bdf5e562f29c
This commit is contained in:
parent
d0a9e63140
commit
6cbe27ed95
|
|
@ -4,7 +4,9 @@
|
|||
package sbt.test
|
||||
|
||||
import Scripted._
|
||||
import FileUtilities.{classLocation, sbtJar, scalaCompilerJar, scalaLibraryJar, wrapNull}
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
|
||||
trait ScalaScripted extends BasicScalaProject with Scripted with MavenStyleScalaPaths
|
||||
{
|
||||
|
|
@ -16,7 +18,7 @@ trait ScalaScripted extends BasicScalaProject with Scripted with MavenStyleScala
|
|||
|
||||
lazy val scriptedOnly = scriptedMethodTask(scriptedDependencies : _*)
|
||||
|
||||
override def scriptedClasspath = runClasspath +++ Path.lazyPathFinder { Path.fromFile(FileUtilities.sbtJar) :: Nil }
|
||||
override def scriptedClasspath = runClasspath +++ Path.lazyPathFinder { Path.fromFile(sbtJar) :: Nil }
|
||||
}
|
||||
trait SbtScripted extends ScalaScripted
|
||||
{
|
||||
|
|
@ -35,32 +37,39 @@ final case class ScriptedTest(group: String, name: String) extends NotNull
|
|||
trait Scripted extends Project with MultiTaskProject
|
||||
{
|
||||
def sbtTests: Path
|
||||
def scriptedTask(dependencies: ManagedTask*) = compoundTask(scriptedTests(listTests, dependencies : _*))
|
||||
def scriptedTask(dependencies: ManagedTask*) = dynamic(scriptedTests(listTests, dependencies : _*))
|
||||
def scriptedMethodTask(dependencies: ManagedTask*) = multiTask(listTests.map(_.toString).toList) { includeFunction =>
|
||||
scriptedTests(listTests.filter(test => includeFunction(test.toString)), dependencies : _*)
|
||||
}
|
||||
def listTests = (new ListTests(sbtTests.asFile, include _, log)).listTests
|
||||
def scriptedTests(tests: Seq[ScriptedTest], dependencies: ManagedTask*) =
|
||||
{
|
||||
// load ScriptedTests using a ClassLoader that loads from the project classpath so that the version
|
||||
// of sbt being built is tested, not the one doing the building.
|
||||
val loader = ScriptedLoader(_scriptedClasspath.toArray)
|
||||
val scriptedClass = Class.forName(ScriptedClassName, true, loader)
|
||||
val scriptedConstructor = scriptedClass.getConstructor(classOf[File], classOf[ClassLoader])
|
||||
val rawRunner = scriptedConstructor.newInstance(sbtTests.asFile, loader)
|
||||
val runner = rawRunner.asInstanceOf[{def scriptedTest(group: String, name: String, log: Logger): Option[String]}]
|
||||
val localLogger = new LocalLogger(log)
|
||||
lazy val runner =
|
||||
{
|
||||
// load ScriptedTests using a ClassLoader that loads from the project classpath so that the version
|
||||
// of sbt being built is tested, not the one doing the building.
|
||||
val filtered = new FilteredLoader(ClassLoader.getSystemClassLoader, Seq("sbt.", "scala.", "ch.epfl.", "org.apache.", "org.jsch."))
|
||||
val loader = new URLClassLoader(_scriptedClasspath.toArray, filtered)
|
||||
val scriptedClass = Class.forName(ScriptedClassName, true, loader)
|
||||
val scriptedConstructor = scriptedClass.getConstructor(classOf[File], classOf[ClassLoader])
|
||||
val rawRunner = scriptedConstructor.newInstance(sbtTests.asFile, loader)
|
||||
rawRunner.asInstanceOf[{def scriptedTest(group: String, name: String, log: Reflected.Logger): String}]
|
||||
}
|
||||
|
||||
val startTask = task { None } named("scripted-test-start") dependsOn(dependencies : _*)
|
||||
def scriptedTest(test: ScriptedTest) = task { runner.scriptedTest(test.group, test.name, log) } named test.toString dependsOn(startTask)
|
||||
def scriptedTest(test: ScriptedTest) =
|
||||
task { unwrapOption(runner.scriptedTest(test.group, test.name, localLogger)) } named test.toString dependsOn(startTask)
|
||||
val testTasks = tests.map(scriptedTest)
|
||||
task { None } named("scripted-test-complete") dependsOn(testTasks : _*)
|
||||
}
|
||||
private def unwrapOption[T](s: T): Option[T] = if(s == null) None else Some(s)
|
||||
/** The classpath to use for scripted tests. This ensures that the version of sbt being built is the one used for testing.*/
|
||||
private def _scriptedClasspath =
|
||||
{
|
||||
val buildClasspath = FileUtilities.classLocation[Scripted]
|
||||
val scalacJar = FileUtilities.scalaCompilerJar.toURI.toURL
|
||||
buildClasspath :: scalacJar :: scriptedClasspath.get.map(_.asURL).toList
|
||||
val buildClasspath = classLocation[Scripted]
|
||||
val scalaJars = List(scalaLibraryJar, scalaCompilerJar).map(_.toURI.toURL).toList
|
||||
buildClasspath :: scalaJars ::: scriptedClasspath.get.map(_.asURL).toList
|
||||
}
|
||||
def scriptedClasspath: PathFinder = Path.emptyPathFinder
|
||||
|
||||
|
|
@ -71,7 +80,7 @@ private[test] object Scripted
|
|||
{
|
||||
val ScriptedClassName = "sbt.test.ScriptedTests"
|
||||
val SbtTestDirectoryName = "sbt-test"
|
||||
def list(directory: File, filter: java.io.FileFilter) = FileUtilities.wrapNull(directory.listFiles(filter))
|
||||
def list(directory: File, filter: java.io.FileFilter) = wrapNull(directory.listFiles(filter))
|
||||
}
|
||||
private[test] final class ListTests(baseDirectory: File, accept: ScriptedTest => Boolean, log: Logger) extends NotNull
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2009 Mark Harrah
|
||||
*/
|
||||
package sbt.test
|
||||
|
||||
import java.net.URL
|
||||
|
||||
object ScriptedLoader
|
||||
{
|
||||
def apply(paths: Array[URL]): ClassLoader = new ScriptedLoader(paths)
|
||||
}
|
||||
private class ScriptedLoader(paths: Array[URL]) extends LoaderBase(paths, classOf[ScriptedLoader].getClassLoader)
|
||||
{
|
||||
private val delegateFor = List("sbt.Logger", "sbt.LogEvent", "sbt.SetLevel", "sbt.Success", "sbt.Log", "sbt.SetTrace", "sbt.Trace", "sbt.ControlEvent")
|
||||
def doLoadClass(className: String): Class[_] =
|
||||
{
|
||||
// Logger needs to be loaded from the version of sbt building the project because we need to pass
|
||||
// a Logger from that loader into ScriptedTests.
|
||||
// All other sbt classes should be loaded from the project classpath so that we test those classes with 'scripted'
|
||||
if(!shouldDelegate(className) && (className.startsWith("sbt.") || className.startsWith("scala.tools.")))
|
||||
findClass(className)
|
||||
else
|
||||
selfLoadClass(className)
|
||||
}
|
||||
|
||||
private def shouldDelegate(className: String) = delegateFor.exists(check => isNestedOrSelf(className, check))
|
||||
private def isNestedOrSelf(className: String, checkAgainst: String) =
|
||||
className == checkAgainst || className.startsWith(checkAgainst + "$")
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package sbt.test
|
||||
|
||||
object Reflected
|
||||
{
|
||||
type Logger =
|
||||
{
|
||||
def enableTrace(flag: Boolean): Unit
|
||||
def traceEnabled: Boolean
|
||||
def getLevel: Int
|
||||
def setLevel(level: Int): Unit
|
||||
|
||||
def trace(t: F0[Throwable]): Unit
|
||||
def success(message: F0[String]): Unit
|
||||
def log(level: Int, message: F0[String]): Unit
|
||||
def control(event: Int, message: F0[String]): Unit
|
||||
}
|
||||
type F0[T] =
|
||||
{
|
||||
def apply(): T
|
||||
}
|
||||
}
|
||||
|
||||
final class LocalLogger(logger: Logger) extends NotNull
|
||||
{
|
||||
import Reflected.F0
|
||||
def enableTrace(flag: Boolean) = logger.enableTrace(flag)
|
||||
def traceEnabled = logger.traceEnabled
|
||||
def getLevel = logger.getLevel.id
|
||||
def setLevel(level: Int) = logger.setLevel(Level(level))
|
||||
|
||||
def trace(t: F0[Throwable]) = logger.trace(t())
|
||||
def success(message: F0[String]) = logger.success(message())
|
||||
def log(level: Int, message: F0[String]) = logger.log(Level(level), message())
|
||||
def control(event: Int, message: F0[String]) = logger.control(ControlEvent(event), message())
|
||||
}
|
||||
|
||||
final class RemoteLogger(logger: Reflected.Logger) extends Logger
|
||||
{
|
||||
private final class F0[T](s: => T) extends NotNull { def apply(): T = s }
|
||||
def getLevel: Level.Value = Level(logger.getLevel)
|
||||
def setLevel(newLevel: Level.Value) = logger.setLevel(newLevel.id)
|
||||
def enableTrace(flag: Boolean) = logger.enableTrace(flag)
|
||||
def traceEnabled = logger.traceEnabled
|
||||
|
||||
def trace(t: => Throwable) = logger.trace(new F0(t))
|
||||
def success(message: => String) = logger.success(new F0(message))
|
||||
def log(level: Level.Value, message: => String) = logger.log(level.id, new F0(message))
|
||||
def control(event: ControlEvent.Value, message: => String) = logger.control(event.id, new F0(message))
|
||||
def logAll(events: Seq[LogEvent]) = events.foreach(log)
|
||||
}
|
||||
|
|
@ -14,12 +14,19 @@ final class ScriptedTests(testResources: Resources) extends NotNull
|
|||
val ScriptFilename = "test"
|
||||
import testResources._
|
||||
|
||||
def scriptedTest(group: String, name: String, log: Logger): Option[String] =
|
||||
readOnlyResourceDirectory(group, name).fold(err => Some(err), testDirectory => scriptedTest(testDirectory, log))
|
||||
def scriptedTest(testDirectory: File, log: Logger): Option[String] =
|
||||
private def printClass(c: Class[_]) = println(c.getName + " loader=" +c.getClassLoader + " location=" + FileUtilities.classLocationFile(c))
|
||||
|
||||
def scriptedTest(group: String, name: String, logger: Reflected.Logger): String =
|
||||
{
|
||||
val log = new RemoteLogger(logger)
|
||||
val result = readOnlyResourceDirectory(group, name).fold(err => Some(err), testDirectory => scriptedTest(testDirectory, log))
|
||||
wrapOption(result)
|
||||
}
|
||||
private def scriptedTest(testDirectory: File, log: Logger): Option[String] =
|
||||
{
|
||||
(for(script <- (new TestScriptParser(testDirectory, log)).parse(new File(testDirectory, ScriptFilename)).right;
|
||||
u <- withProject(testDirectory, log)(script).right )
|
||||
yield u).left.toOption
|
||||
}
|
||||
private[this] def wrapOption[T >: Null](s: Option[T]): T = s match { case Some(t) => t; case None => null }
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue