summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@gmail.com>2015-01-18 23:22:25 +0100
committerLukas Rytz <lukas.rytz@gmail.com>2015-03-11 12:53:34 -0700
commit4ffe9345ca6e611ff5a83a3fdabeb7658a2fce50 (patch)
tree324b0ec35f2adc76e97548f90a1ba9e3351271f6
parentb34a452c0683d260ffb1644575a0e970559cae87 (diff)
downloadscala-4ffe9345ca6e611ff5a83a3fdabeb7658a2fce50.tar.gz
scala-4ffe9345ca6e611ff5a83a3fdabeb7658a2fce50.tar.bz2
scala-4ffe9345ca6e611ff5a83a3fdabeb7658a2fce50.zip
Reuse the same compiler instance for all tests in a JUnit class
Note that JUnit creates a new instance of the test class for running each test method. So the compiler instance is added to the companion. However, the JVM would quickly run out of memory when running multiple tests, as the compilers cannot be GCd. So we make it a `var`, and set it to null when a class is done. For that we use JUnit's `@AfterClass` which is required to be on a static method. Therefore we add a Java class with such a static method that we can extend from Scala.
-rwxr-xr-xbuild.xml9
-rw-r--r--src/reflect/scala/reflect/internal/SymbolTable.scala8
-rw-r--r--test/junit/scala/collection/IterableViewLikeTest.scala1
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala52
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala12
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala4
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala19
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala12
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala29
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOpts.scala12
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala36
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/UnusedLocalVariablesTest.scala12
-rw-r--r--test/junit/scala/tools/testing/ClearAfterClass.java20
13 files changed, 175 insertions, 51 deletions
diff --git a/build.xml b/build.xml
index 5f6b04b8e4..ee6a045bda 100755
--- a/build.xml
+++ b/build.xml
@@ -1448,6 +1448,15 @@ TODO:
<target name="test.junit.comp" depends="pack.done">
<stopwatch name="test.junit.compiler.timer"/>
<mkdir dir="${test.junit.classes}"/>
+ <javac
+ debug="true"
+ srcdir="${test.junit.src}"
+ destdir="${test.junit.classes}"
+ classpathref="test.junit.compiler.build.path"
+ target="1.6"
+ source="1.5"
+ compiler="javac1.6"
+ includes="**/*.java"/>
<scalacfork
destdir="${test.junit.classes}"
compilerpathref="quick.compiler.path"
diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala
index 01e4cdf367..ef63078f90 100644
--- a/src/reflect/scala/reflect/internal/SymbolTable.scala
+++ b/src/reflect/scala/reflect/internal/SymbolTable.scala
@@ -355,6 +355,14 @@ abstract class SymbolTable extends macros.Universe
cache
}
+ /**
+ * Removes a cache from the per-run caches. This is useful for testing: it allows running the
+ * compiler and then inspect the state of a cache.
+ */
+ def unrecordCache[T <: Clearable](cache: T): Unit = {
+ caches = caches.filterNot(_.get eq cache)
+ }
+
def clearAll() = {
debuglog("Clearing " + caches.size + " caches.")
caches foreach (ref => Option(ref.get).foreach(_.clear))
diff --git a/test/junit/scala/collection/IterableViewLikeTest.scala b/test/junit/scala/collection/IterableViewLikeTest.scala
index ab09c4930b..435a43c215 100644
--- a/test/junit/scala/collection/IterableViewLikeTest.scala
+++ b/test/junit/scala/collection/IterableViewLikeTest.scala
@@ -13,6 +13,7 @@ class IterableViewLikeTest {
def hasCorrectDropAndTakeMethods() {
val iter = Iterable(1, 2, 3)
+ import scala.language.postfixOps
assertEquals(Iterable.empty[Int], iter.view take Int.MinValue force)
assertEquals(Iterable.empty[Int], iter.view takeRight Int.MinValue force)
assertEquals(iter, iter.view drop Int.MinValue force)
diff --git a/test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala b/test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala
index 2347e8288e..6ada0e20fb 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala
@@ -7,35 +7,41 @@ import org.junit.Test
import scala.tools.asm.Opcodes
import org.junit.Assert._
-@RunWith(classOf[JUnit4])
-class BTypesTest {
- val settings = new Settings()
- settings.processArgumentString("-usejavacp")
- val g: Global = new Global(settings)
- val run = new g.Run() // initializes some compiler internals
- import g.{definitions => d, Symbol}
+import scala.tools.nsc.backend.jvm.CodeGenTools._
+import scala.tools.testing.ClearAfterClass
- def duringBackend[T](f: => T) = g.exitingDelambdafy(f)
+object BTypesTest extends ClearAfterClass.Clearable {
+ var compiler = {
+ val comp = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:l:none")
+ new comp.Run() // initializes some of the compiler
+ comp.exitingDelambdafy(comp.scalaPrimitives.init()) // needed: it's only done when running the backend, and we don't actually run the compiler
+ comp.exitingDelambdafy(comp.genBCode.bTypes.initializeCoreBTypes())
+ comp
+ }
+ def clear(): Unit = { compiler = null }
+}
- val btypes = new BTypesFromSymbols[g.type](g)
- import btypes._
- duringBackend(btypes.initializeCoreBTypes())
+@RunWith(classOf[JUnit4])
+class BTypesTest extends ClearAfterClass {
+ ClearAfterClass.stateToClear = BTypesTest
- def classBTypeFromSymbol(sym: Symbol) = duringBackend(btypes.classBTypeFromSymbol(sym))
+ val compiler = BTypesTest.compiler
+ import compiler.genBCode.bTypes._
- val jlo = d.ObjectClass
- val jls = d.StringClass
+ def classBTFS(sym: compiler.Symbol) = compiler.exitingDelambdafy(classBTypeFromSymbol(sym))
- val o = classBTypeFromSymbol(jlo)
- val s = classBTypeFromSymbol(jls)
- val oArr = ArrayBType(o)
- val method = MethodBType(List(oArr, INT, DOUBLE, s), UNIT)
+ def jlo = compiler.definitions.ObjectClass
+ def jls = compiler.definitions.StringClass
+ def o = classBTFS(jlo)
+ def s = classBTFS(jls)
+ def oArr = ArrayBType(o)
+ def method = MethodBType(List(oArr, INT, DOUBLE, s), UNIT)
@Test
def classBTypesEquality() {
- val s1 = classBTypeFromSymbol(jls)
- val s2 = classBTypeFromSymbol(jls)
- val o = classBTypeFromSymbol(jlo)
+ val s1 = classBTFS(jls)
+ val s2 = classBTFS(jls)
+ val o = classBTFS(jlo)
assertEquals(s1, s2)
assertEquals(s1.hashCode, s2.hashCode)
assert(s1 != o)
@@ -53,7 +59,7 @@ class BTypesTest {
assert(FLOAT.typedOpcode(Opcodes.IALOAD) == Opcodes.FALOAD)
assert(LONG.typedOpcode(Opcodes.IALOAD) == Opcodes.LALOAD)
assert(DOUBLE.typedOpcode(Opcodes.IALOAD) == Opcodes.DALOAD)
- assert(classBTypeFromSymbol(jls).typedOpcode(Opcodes.IALOAD) == Opcodes.AALOAD)
+ assert(classBTFS(jls).typedOpcode(Opcodes.IALOAD) == Opcodes.AALOAD)
assert(UNIT.typedOpcode(Opcodes.IRETURN) == Opcodes.RETURN)
assert(BOOL.typedOpcode(Opcodes.IRETURN) == Opcodes.IRETURN)
@@ -64,7 +70,7 @@ class BTypesTest {
assert(FLOAT.typedOpcode(Opcodes.IRETURN) == Opcodes.FRETURN)
assert(LONG.typedOpcode(Opcodes.IRETURN) == Opcodes.LRETURN)
assert(DOUBLE.typedOpcode(Opcodes.IRETURN) == Opcodes.DRETURN)
- assert(classBTypeFromSymbol(jls).typedOpcode(Opcodes.IRETURN) == Opcodes.ARETURN)
+ assert(classBTFS(jls).typedOpcode(Opcodes.IRETURN) == Opcodes.ARETURN)
}
@Test
diff --git a/test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala b/test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala
index 89900291ca..3b1b009037 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala
@@ -7,10 +7,18 @@ import org.junit.Assert._
import CodeGenTools._
import scala.tools.asm.Opcodes._
import scala.tools.partest.ASMConverters._
+import scala.tools.testing.ClearAfterClass
+
+object DirectCompileTest extends ClearAfterClass.Clearable {
+ var compiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:l:method")
+ def clear(): Unit = { compiler = null }
+}
@RunWith(classOf[JUnit4])
-class DirectCompileTest {
- val compiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:l:method")
+class DirectCompileTest extends ClearAfterClass {
+ ClearAfterClass.stateToClear = DirectCompileTest
+
+ val compiler = DirectCompileTest.compiler
@Test
def testCompile(): Unit = {
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala
index 2975bd060d..4481fcd6be 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala
@@ -19,7 +19,8 @@ import scala.collection.convert.decorateAsScala._
@RunWith(classOf[JUnit4])
class BTypesFromClassfileTest {
- val compiler = newCompiler(extraArgs = "-Ybackend:GenBCode")
+ // inliner enabled -> inlineInfos are collected (and compared) in ClassBTypes
+ val compiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:inline-global")
import compiler._
import definitions._
@@ -29,6 +30,7 @@ class BTypesFromClassfileTest {
def duringBackend[T](f: => T) = compiler.exitingDelambdafy(f)
val run = new compiler.Run() // initializes some of the compiler
+ duringBackend(compiler.scalaPrimitives.init()) // needed: it's only done when running the backend, and we don't actually run the compiler
duringBackend(bTypes.initializeCoreBTypes())
def clearCache() = bTypes.classBTypeFromInternalName.clear()
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala
index 7d83c54b5b..7b0504fec0 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala
@@ -11,9 +11,23 @@ import org.junit.Assert._
import CodeGenTools._
import scala.tools.partest.ASMConverters
import ASMConverters._
+import scala.tools.testing.ClearAfterClass
+
+object EmptyExceptionHandlersTest extends ClearAfterClass.Clearable {
+ var noOptCompiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:l:none")
+ var dceCompiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:unreachable-code")
+ def clear(): Unit = {
+ noOptCompiler = null
+ dceCompiler = null
+ }
+}
@RunWith(classOf[JUnit4])
-class EmptyExceptionHandlersTest {
+class EmptyExceptionHandlersTest extends ClearAfterClass {
+ ClearAfterClass.stateToClear = EmptyExceptionHandlersTest
+
+ val noOptCompiler = EmptyExceptionHandlersTest.noOptCompiler
+ val dceCompiler = EmptyExceptionHandlersTest.dceCompiler
val exceptionDescriptor = "java/lang/Exception"
@@ -51,9 +65,6 @@ class EmptyExceptionHandlersTest {
assertTrue(convertMethod(asmMethod).handlers.isEmpty)
}
- val noOptCompiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:l:none")
- val dceCompiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:unreachable-code")
-
@Test
def eliminateUnreachableHandler(): Unit = {
val code = "def f: Unit = try { } catch { case _: Exception => println(0) }; println(1)"
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala
index 406b8da570..36f297767e 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala
@@ -17,10 +17,18 @@ import ASMConverters._
import AsmUtils._
import scala.collection.convert.decorateAsScala._
+import scala.tools.testing.ClearAfterClass
+
+object InlinerIllegalAccessTest extends ClearAfterClass.Clearable {
+ var compiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:l:none")
+ def clear(): Unit = { compiler = null }
+}
@RunWith(classOf[JUnit4])
-class InlinerIllegalAccessTest {
- val compiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:l:none")
+class InlinerIllegalAccessTest extends ClearAfterClass {
+ ClearAfterClass.stateToClear = InlinerIllegalAccessTest
+
+ val compiler = InlinerIllegalAccessTest.compiler
import compiler.genBCode.bTypes._
def addToRepo(cls: List[ClassNode]): Unit = for (c <- cls) byteCodeRepository.classes(c.name) = (c, ByteCodeRepository.Classfile)
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
index 1aa7bd1391..2ec6853f13 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
@@ -5,6 +5,7 @@ package opt
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.junit.Test
+import scala.collection.generic.Clearable
import scala.tools.asm.Opcodes._
import org.junit.Assert._
@@ -19,19 +20,37 @@ import ASMConverters._
import AsmUtils._
import scala.collection.convert.decorateAsScala._
+import scala.tools.testing.ClearAfterClass
+
+object InlinerTest extends ClearAfterClass.Clearable {
+ var compiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:l:project")
+
+ // allows inspecting the caches after a compilation run
+ def notPerRun: List[Clearable] = List(compiler.genBCode.bTypes.classBTypeFromInternalName, compiler.genBCode.bTypes.byteCodeRepository.classes)
+ notPerRun foreach compiler.perRunCaches.unrecordCache
+
+ def clear(): Unit = { compiler = null }
+}
@RunWith(classOf[JUnit4])
-class InlinerTest {
- val compiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:l:none")
+class InlinerTest extends ClearAfterClass {
+ ClearAfterClass.stateToClear = InlinerTest
+
+ val compiler = InlinerTest.compiler
import compiler.genBCode.bTypes._
- def addToRepo(cls: List[ClassNode]): List[ClassNode] = {
+
+ def compile(code: String): List[ClassNode] = {
+ InlinerTest.notPerRun.foreach(_.clear())
+ val cls = compileClasses(compiler)(code)
+ // the compiler doesn't add classes being compiled to the code repo yet, so we do it manually.
+ // this line is removed in the next commit.
for (c <- cls) byteCodeRepository.classes(c.name) = (c, ByteCodeRepository.Classfile)
cls
}
// inline first invocation of f into g in class C
def inlineTest(code: String, mod: ClassNode => Unit = _ => ()): (MethodNode, Option[String]) = {
- val List(cls) = addToRepo(compileClasses(compiler)(code))
+ val List(cls) = compile(code)
mod(cls)
val clsBType = classBTypeFromParsedClassfile(cls.name)
@@ -165,7 +184,7 @@ class InlinerTest {
|}
""".stripMargin
- val List(c, d) = addToRepo(compileClasses(compiler)(code))
+ val List(c, d) = compile(code)
val cTp = classBTypeFromParsedClassfile(c.name)
val dTp = classBTypeFromParsedClassfile(d.name)
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOpts.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOpts.scala
index 5430e33d6c..2c71e9d533 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOpts.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOpts.scala
@@ -13,10 +13,18 @@ import scala.tools.testing.AssertUtil._
import CodeGenTools._
import scala.tools.partest.ASMConverters
import ASMConverters._
+import scala.tools.testing.ClearAfterClass
+
+object MethodLevelOpts extends ClearAfterClass.Clearable {
+ var methodOptCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:l:method")
+ def clear(): Unit = { methodOptCompiler = null }
+}
@RunWith(classOf[JUnit4])
-class MethodLevelOpts {
- val methodOptCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:l:method")
+class MethodLevelOpts extends ClearAfterClass {
+ ClearAfterClass.stateToClear = MethodLevelOpts
+
+ val methodOptCompiler = MethodLevelOpts.methodOptCompiler
def wrapInDefault(code: Instruction*) = List(Label(0), LineNumber(1, Label(0))) ::: code.toList ::: List(Label(1))
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala
index 4a45dd9138..7c9636b8b7 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala
@@ -13,9 +13,34 @@ import scala.tools.testing.AssertUtil._
import CodeGenTools._
import scala.tools.partest.ASMConverters
import ASMConverters._
+import scala.tools.testing.ClearAfterClass
+
+object UnreachableCodeTest extends ClearAfterClass.Clearable {
+ // jvm-1.6 enables emitting stack map frames, which impacts the code generation wrt dead basic blocks,
+ // see comment in BCodeBodyBuilder
+ var methodOptCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:l:method")
+ var dceCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:unreachable-code")
+ var noOptCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:l:none")
+
+ // jvm-1.5 disables computing stack map frames, and it emits dead code as-is.
+ var noOptNoFramesCompiler = newCompiler(extraArgs = "-target:jvm-1.5 -Ybackend:GenBCode -Yopt:l:none")
+
+ def clear(): Unit = {
+ methodOptCompiler = null
+ dceCompiler = null
+ noOptCompiler = null
+ noOptNoFramesCompiler = null
+ }
+}
@RunWith(classOf[JUnit4])
-class UnreachableCodeTest {
+class UnreachableCodeTest extends ClearAfterClass {
+ ClearAfterClass.stateToClear = UnreachableCodeTest
+
+ val methodOptCompiler = UnreachableCodeTest.methodOptCompiler
+ val dceCompiler = UnreachableCodeTest.dceCompiler
+ val noOptCompiler = UnreachableCodeTest.noOptCompiler
+ val noOptNoFramesCompiler = UnreachableCodeTest.noOptNoFramesCompiler
def assertEliminateDead(code: (Instruction, Boolean)*): Unit = {
val method = genMethod()(code.map(_._1): _*)
@@ -25,15 +50,6 @@ class UnreachableCodeTest {
assertSameCode(nonEliminated, expectedLive)
}
- // jvm-1.6 enables emitting stack map frames, which impacts the code generation wrt dead basic blocks,
- // see comment in BCodeBodyBuilder
- val methodOptCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:l:method")
- val dceCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:unreachable-code")
- val noOptCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:l:none")
-
- // jvm-1.5 disables computing stack map frames, and it emits dead code as-is.
- val noOptNoFramesCompiler = newCompiler(extraArgs = "-target:jvm-1.5 -Ybackend:GenBCode -Yopt:l:none")
-
@Test
def basicElimination(): Unit = {
assertEliminateDead(
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/UnusedLocalVariablesTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/UnusedLocalVariablesTest.scala
index 24a1f9d1c1..769736669b 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/UnusedLocalVariablesTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/UnusedLocalVariablesTest.scala
@@ -12,10 +12,18 @@ import scala.collection.JavaConverters._
import CodeGenTools._
import scala.tools.partest.ASMConverters
import ASMConverters._
+import scala.tools.testing.ClearAfterClass
+
+object UnusedLocalVariablesTest extends ClearAfterClass.Clearable {
+ var dceCompiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:unreachable-code")
+ def clear(): Unit = { dceCompiler = null }
+}
@RunWith(classOf[JUnit4])
-class UnusedLocalVariablesTest {
- val dceCompiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:unreachable-code")
+class UnusedLocalVariablesTest extends ClearAfterClass {
+ ClearAfterClass.stateToClear = UnusedLocalVariablesTest
+
+ val dceCompiler = UnusedLocalVariablesTest.dceCompiler
@Test
def removeUnusedVar(): Unit = {
diff --git a/test/junit/scala/tools/testing/ClearAfterClass.java b/test/junit/scala/tools/testing/ClearAfterClass.java
new file mode 100644
index 0000000000..232d459c4e
--- /dev/null
+++ b/test/junit/scala/tools/testing/ClearAfterClass.java
@@ -0,0 +1,20 @@
+package scala.tools.testing;
+
+import org.junit.AfterClass;
+
+/**
+ * Extend this class to use JUnit's @AfterClass. This annotation only works on static methods,
+ * which cannot be written in Scala.
+ *
+ * Example: {@link scala.tools.nsc.backend.jvm.opt.InlinerTest}
+ */
+public class ClearAfterClass {
+ public static interface Clearable {
+ void clear();
+ }
+
+ public static Clearable stateToClear;
+
+ @AfterClass
+ public static void clearState() { stateToClear.clear(); }
+}