Merge pull request #4527 from eatkins/scripted-classloader

Use custom classloader for scripted tests
This commit is contained in:
Ethan Atkins 2019-02-01 13:21:28 -08:00 committed by GitHub
commit f14bc7a503
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 47 additions and 35 deletions

View File

@ -17,8 +17,10 @@ object LocalScriptedPlugin extends AutoPlugin {
trait ScriptedKeys {
val publishAll = taskKey[Unit]("")
val publishLocalBinAll = taskKey[Unit]("")
val scriptedUnpublished = inputKey[Unit]("Execute scripted without publishing sbt first. " +
"Saves you some time when only your test has changed")
val scriptedUnpublished = inputKey[Unit](
"Execute scripted without publishing sbt first. " +
"Saves you some time when only your test has changed"
)
val scriptedSource = settingKey[File]("")
val scriptedPrescripted = taskKey[File => Unit]("")
}
@ -107,33 +109,48 @@ object Scripted {
// Interface to cross class loader
type SbtScriptedRunner = {
def runInParallel(
resourceBaseDirectory: File,
bufferLog: Boolean,
tests: Array[String],
bootProperties: File,
launchOpts: Array[String],
prescripted: java.util.List[File],
resourceBaseDirectory: File,
bufferLog: Boolean,
tests: Array[String],
bootProperties: File,
launchOpts: Array[String],
prescripted: java.util.List[File],
instances: Int
): Unit
}
val bridge = bridgeClass.getDeclaredConstructor().newInstance().asInstanceOf[SbtScriptedRunner]
val initLoader = Thread.currentThread.getContextClassLoader
try {
// Using java.util.List to encode File => Unit.
val callback = new java.util.AbstractList[File] {
override def add(x: File): Boolean = { prescripted(x); false }
def get(x: Int): sbt.File = ???
def size(): Int = 0
}
import scala.language.reflectiveCalls
bridge.runInParallel(
sourcePath,
bufferLog,
args.toArray,
launcher,
launchOpts.toArray,
callback,
)
} catch { case ite: InvocationTargetException => throw ite.getCause }
Thread.currentThread.setContextClassLoader(loader)
val bridge =
bridgeClass.getDeclaredConstructor().newInstance().asInstanceOf[SbtScriptedRunner]
try {
// Using java.util.List to encode File => Unit.
val callback = new java.util.AbstractList[File] {
override def add(x: File): Boolean = { prescripted(x); false }
def get(x: Int): sbt.File = ???
def size(): Int = 0
}
val instances: Int = (System.getProperty("sbt.scripted.parallel.instances") match {
case null => 1
case i => scala.util.Try(i.toInt).getOrElse(1)
}) match {
case i if i > 0 => i
case _ => 1
}
import scala.language.reflectiveCalls
bridge.runInParallel(
sourcePath,
bufferLog,
args.toArray,
launcher,
launchOpts.toArray,
callback,
instances
)
} catch { case ite: InvocationTargetException => throw ite.getCause }
} finally {
Thread.currentThread.setContextClassLoader(initLoader)
}
}
}

View File

@ -1,5 +1,3 @@
> debug
> a/update
> a/updateClassifiers

View File

@ -1,4 +1,3 @@
> y1/publishLocal
> y2/publishLocal
> debug
> check

View File

@ -1,4 +1,3 @@
> debug
# load the project definition with transitive dependencies enabled
# and check that they are not downloaded
#$ pause

View File

@ -1,4 +1,3 @@
> debug
> update
# works because scalaVersion is the same as sbtScalaVersion
> compile

View File

@ -19,7 +19,6 @@ $ copy-file changes/mainB1.scala main/B.scala
-> main/compile
$ copy-file changes/libA.scala library/A.scala
> debug
> library/publishM2
# should succeed even without 'update' because Ivy should use the jar from the origin and not copy it to its cache
> main/compile

View File

@ -125,7 +125,10 @@ final class ScriptedTests(
if (labelsAndDirs.isEmpty) List()
else {
val totalSize = labelsAndDirs.size
val batchSize = totalSize / sbtInstances
val batchSize = totalSize / sbtInstances match {
case 0 => 1
case s => s
}
val (launcherBasedTests, runFromSourceBasedTests) = labelsAndDirs.partition {
case (testName, _) =>