Don't leak the sbt boot scala library into tests

It was reported in https://github.com/sbt/sbt/issues/4608 that there was
a regression that tests run against scala 2.11 would fail. This was
because the interface loader incorrectly contained the scala library. To
fix this, I needed to find the xsbt.boot.BootFilteredLoader in the
classloading hierarchy and put the sbt testing interface library in
between that loader and the scala library loader.
This commit is contained in:
Ethan Atkins 2019-04-07 14:56:19 -07:00
parent c9aec02d05
commit fc715cab44
5 changed files with 29 additions and 2 deletions

View File

@ -21,6 +21,8 @@ import sbt.internal.util.Attributed.data
import sbt.io.IO
import sbt.librarymanagement.Configurations.{ Runtime, Test }
import scala.annotation.tailrec
private[sbt] object ClassLoaders {
private[this] val interfaceLoader = classOf[sbt.testing.Framework].getClassLoader
/*
@ -214,9 +216,15 @@ private[sbt] object SbtMetaBuildClassLoader {
}
}
def apply(libraryLoader: ClassLoader, fullLoader: ClassLoader): ClassLoader = {
@tailrec
def bootLoader(classLoader: ClassLoader): ClassLoader = classLoader.getParent match {
case null => classLoader
case c if c.getClass.getCanonicalName == "xsbt.boot.BootFilteredLoader" => c
case c => bootLoader(c)
}
val interfaceFilter: URL => Boolean = _.getFile.endsWith("test-interface-1.0.jar")
val (interfaceURL, rest) = fullLoader.urls.partition(interfaceFilter)
val interfaceLoader = new URLClassLoader(interfaceURL, libraryLoader.getParent) {
val interfaceLoader = new URLClassLoader(interfaceURL, bootLoader(libraryLoader)) {
override def toString: String = s"SbtTestInterfaceClassLoader(${getURLs.head})"
}
val updatedLibraryLoader = new URLClassLoader(libraryLoader.urls, interfaceLoader) {

View File

@ -0,0 +1,8 @@
val scalatest = "org.scalatest" %% "scalatest" % "3.0.5"
lazy val root = (project in file("."))
.settings(
// Verifies that a different scala library version still works in test
scalaVersion := "2.11.12",
libraryDependencies += scalatest % Test
)

View File

@ -0,0 +1,8 @@
import java.io.File
import org.scalatest.FlatSpec
class Test extends FlatSpec {
"a test" should "pass" in {
assert(true)
}
}

View File

@ -0,0 +1 @@
> test

View File

@ -255,7 +255,9 @@ final class ScriptedTests(
case "source-dependencies/linearization" => LauncherBased // sbt/Package$
case "source-dependencies/named" => LauncherBased // sbt/Package$
case "source-dependencies/specialized" => LauncherBased // sbt/Package$
case _ => RunFromSourceBased
case "tests/test-cross" =>
LauncherBased // the sbt metabuild classpath leaks into the test interface classloader in older versions of sbt
case _ => RunFromSourceBased
}
// sbt/Package$ means:
// java.lang.NoClassDefFoundError: sbt/Package$ (wrong name: sbt/package$)