@@ -30,7 +30,11 @@ def commonSettings: Seq[Setting[_]] = Def.settings(
30
30
inCompileAndTest(scalacOptions in console --= Vector (" -Ywarn-unused-import" , " -Ywarn-unused" , " -Xlint" )),
31
31
publishArtifact in Compile := true ,
32
32
publishArtifact in Test := false ,
33
- parallelExecution in Test := false
33
+ parallelExecution in Test := false ,
34
+ testOptions in Test += {
35
+ val log = streams.value.log
36
+ Tests .Cleanup { loader => cleanupTests(loader, log) }
37
+ }
34
38
)
35
39
36
40
val mimaSettings = Def settings (
@@ -275,3 +279,22 @@ inThisBuild(Seq(
275
279
276
280
def inCompileAndTest (ss : SettingsDefinition * ): Seq [Setting [_]] =
277
281
Seq (Compile , Test ) flatMap (inConfig(_)(Def .settings(ss : _* )))
282
+
283
+
284
+ // TODO move into sbt-house-rules?
285
+ def cleanupTests (loader : ClassLoader , log : sbt.internal.util.ManagedLogger ): Unit = {
286
+ // shutdown Log4J to avoid classloader leaks
287
+ try {
288
+ val logManager = Class .forName(" org.apache.logging.log4j.LogManager" )
289
+ logManager.getMethod(" shutdown" ).invoke(null )
290
+ } catch {
291
+ case _ : Throwable =>
292
+ log.warn(" Could not shut down Log4J" )
293
+ }
294
+ // Scala Test loads property bundles, let's eagerly clear then from the internal cache
295
+ // TODO move into SBT itself?
296
+ java.util.ResourceBundle .clearCache(loader)
297
+ // Scala Test also starts TimerThreads that it doesn't eagerly cancel. This can weakly retain
298
+ // metaspace until a full GC.
299
+ System .gc()
300
+ }
0 commit comments