From b2c9d3e2cfdaa8320aa8fa5204953a7f6e0a0c96 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Wed, 30 Jan 2019 17:25:00 -0800 Subject: [PATCH 1/3] Use custom classloader for scripted tests We had previously used reflection to load the bridge class, but continued using sbt's default classloader. This was problematic because the metabuild could have a different classpath from that required by the scripted tests. Bonus: scalafmt Fixes: #4514 --- project/Scripted.scala | 61 ++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/project/Scripted.scala b/project/Scripted.scala index e77749d63..cd7095799 100644 --- a/project/Scripted.scala +++ b/project/Scripted.scala @@ -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,40 @@ 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], ): 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 + } + import scala.language.reflectiveCalls + bridge.runInParallel( + sourcePath, + bufferLog, + args.toArray, + launcher, + launchOpts.toArray, + callback, + ) + } catch { case ite: InvocationTargetException => throw ite.getCause } + } finally { + Thread.currentThread.setContextClassLoader(initLoader) + } } } From 9023b58f830936d0183cee9442733ccf0107d394 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Wed, 30 Jan 2019 18:07:06 -0800 Subject: [PATCH 2/3] Remove debug commands in scripted These just create noise. --- .../dependency-management/cached-resolution-classifier/test | 2 -- .../dependency-management/cached-resolution-conflicts/test | 1 - .../dependency-management/cached-resolution-interproj/test | 2 -- sbt/src/sbt-test/dependency-management/exclude-transitive/test | 1 - sbt/src/sbt-test/dependency-management/ivy-settings-a/test | 1 - sbt/src/sbt-test/dependency-management/mvn-local/disabled | 1 - 6 files changed, 8 deletions(-) diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution-classifier/test b/sbt/src/sbt-test/dependency-management/cached-resolution-classifier/test index ccff5f24b..55cfc0713 100644 --- a/sbt/src/sbt-test/dependency-management/cached-resolution-classifier/test +++ b/sbt/src/sbt-test/dependency-management/cached-resolution-classifier/test @@ -1,5 +1,3 @@ -> debug - > a/update > a/updateClassifiers diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution-conflicts/test b/sbt/src/sbt-test/dependency-management/cached-resolution-conflicts/test index 8e266c833..1a1446f96 100644 --- a/sbt/src/sbt-test/dependency-management/cached-resolution-conflicts/test +++ b/sbt/src/sbt-test/dependency-management/cached-resolution-conflicts/test @@ -1,4 +1,3 @@ > y1/publishLocal > y2/publishLocal -> debug > check diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution-interproj/test b/sbt/src/sbt-test/dependency-management/cached-resolution-interproj/test index f3d872ac0..15886c51a 100644 --- a/sbt/src/sbt-test/dependency-management/cached-resolution-interproj/test +++ b/sbt/src/sbt-test/dependency-management/cached-resolution-interproj/test @@ -1,4 +1,2 @@ -> debug - > check diff --git a/sbt/src/sbt-test/dependency-management/exclude-transitive/test b/sbt/src/sbt-test/dependency-management/exclude-transitive/test index bf78e6c17..6204908fb 100644 --- a/sbt/src/sbt-test/dependency-management/exclude-transitive/test +++ b/sbt/src/sbt-test/dependency-management/exclude-transitive/test @@ -1,4 +1,3 @@ -> debug # load the project definition with transitive dependencies enabled # and check that they are not downloaded #$ pause diff --git a/sbt/src/sbt-test/dependency-management/ivy-settings-a/test b/sbt/src/sbt-test/dependency-management/ivy-settings-a/test index a8347e838..d7b5b2289 100644 --- a/sbt/src/sbt-test/dependency-management/ivy-settings-a/test +++ b/sbt/src/sbt-test/dependency-management/ivy-settings-a/test @@ -1,4 +1,3 @@ -> debug > update # works because scalaVersion is the same as sbtScalaVersion > compile diff --git a/sbt/src/sbt-test/dependency-management/mvn-local/disabled b/sbt/src/sbt-test/dependency-management/mvn-local/disabled index e6f5381a9..b7eb9f51f 100644 --- a/sbt/src/sbt-test/dependency-management/mvn-local/disabled +++ b/sbt/src/sbt-test/dependency-management/mvn-local/disabled @@ -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 From 7b8ed4d13f26bec33a46693ba84f0f45633a8190 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Wed, 30 Jan 2019 18:27:39 -0800 Subject: [PATCH 3/3] Allow sbt scripted tests to run in parallel I'm not sure if this is a huge benefit or not, but it's nice to have the option to run the scripted tests in parallel. The default behavior should be the same as before. --- project/Scripted.scala | 10 +++++++++- .../main/scala/sbt/scriptedtest/ScriptedTests.scala | 5 ++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/project/Scripted.scala b/project/Scripted.scala index cd7095799..2b4276a8d 100644 --- a/project/Scripted.scala +++ b/project/Scripted.scala @@ -115,6 +115,7 @@ object Scripted { bootProperties: File, launchOpts: Array[String], prescripted: java.util.List[File], + instances: Int ): Unit } @@ -123,7 +124,6 @@ object Scripted { 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] { @@ -131,6 +131,13 @@ object Scripted { 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, @@ -139,6 +146,7 @@ object Scripted { launcher, launchOpts.toArray, callback, + instances ) } catch { case ite: InvocationTargetException => throw ite.getCause } } finally { diff --git a/scripted-sbt-redux/src/main/scala/sbt/scriptedtest/ScriptedTests.scala b/scripted-sbt-redux/src/main/scala/sbt/scriptedtest/ScriptedTests.scala index 91373e1d0..8375bf835 100644 --- a/scripted-sbt-redux/src/main/scala/sbt/scriptedtest/ScriptedTests.scala +++ b/scripted-sbt-redux/src/main/scala/sbt/scriptedtest/ScriptedTests.scala @@ -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, _) =>