From eac1af364e99a6712c5e54e257216027b2ab127e Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 16 May 2016 16:04:17 +1000 Subject: Reduce boilerplate in compiler JUnit tests (#5158) Many JUnit tests share a compiler instance between all test cases in a class to reduce overhead. This commit refactors the mechanism to reduce the boilerplate. In the new scheme: - Using the `@ClassRule` hook in JUnit, we create a per-class map for each test class. - Per-class values are registered from the test class itself by calling `cached("someKey", () => mkExpensiveThing)` - At the end of the test, the entries in this map are `close()`-ed (if they implement `Closable`), and are released for garbage collection.) --- .../tools/nsc/backend/jvm/opt/AnalyzerTest.scala | 8 +----- .../tools/nsc/backend/jvm/opt/CallGraphTest.scala | 26 +++++++----------- .../nsc/backend/jvm/opt/ClosureOptimizerTest.scala | 9 +----- .../jvm/opt/EmptyExceptionHandlersTest.scala | 14 ++-------- .../tools/nsc/backend/jvm/opt/InlineInfoTest.scala | 22 ++++++--------- .../nsc/backend/jvm/opt/InlineWarningTest.scala | 20 ++++---------- .../backend/jvm/opt/InlinerIllegalAccessTest.scala | 9 +----- .../tools/nsc/backend/jvm/opt/InlinerTest.scala | 32 ++++++++-------------- .../nsc/backend/jvm/opt/MethodLevelOptsTest.scala | 9 +----- .../nsc/backend/jvm/opt/ScalaInlineInfoTest.scala | 8 +----- .../nsc/backend/jvm/opt/UnreachableCodeTest.scala | 24 ++++------------ .../backend/jvm/opt/UnusedLocalVariablesTest.scala | 9 +----- 12 files changed, 49 insertions(+), 141 deletions(-) (limited to 'test/junit/scala/tools/nsc/backend/jvm/opt') diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/AnalyzerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/AnalyzerTest.scala index 930f7f2f10..09675870f0 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/AnalyzerTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/AnalyzerTest.scala @@ -21,15 +21,9 @@ import BytecodeUtils._ import scala.collection.JavaConverters._ import scala.tools.testing.ClearAfterClass -object AnalyzerTest extends ClearAfterClass.Clearable { - var noOptCompiler = newCompiler(extraArgs = "-Yopt:l:none") - def clear(): Unit = { noOptCompiler = null } -} - @RunWith(classOf[JUnit4]) class AnalyzerTest extends ClearAfterClass { - ClearAfterClass.stateToClear = AnalyzerTest - val noOptCompiler = AnalyzerTest.noOptCompiler + val noOptCompiler = cached("compiler", () => newCompiler(extraArgs = "-Yopt:l:none")) @Test def aliasingOfPrimitives(): Unit = { diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/CallGraphTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/CallGraphTest.scala index 1d30e42e3c..9a27c42cac 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/CallGraphTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/CallGraphTest.scala @@ -24,29 +24,23 @@ import BackendReporting._ import scala.collection.JavaConverters._ import scala.tools.testing.ClearAfterClass -object CallGraphTest extends ClearAfterClass.Clearable { - var compiler = newCompiler(extraArgs = "-Yopt:inline-global -Yopt-warnings") - def clear(): Unit = { compiler = null } - - // allows inspecting the caches after a compilation run - val notPerRun: List[Clearable] = List( - compiler.genBCode.bTypes.classBTypeFromInternalName, - compiler.genBCode.bTypes.byteCodeRepository.compilingClasses, - compiler.genBCode.bTypes.byteCodeRepository.parsedClasses, - compiler.genBCode.bTypes.callGraph.callsites) - notPerRun foreach compiler.perRunCaches.unrecordCache -} - @RunWith(classOf[JUnit4]) class CallGraphTest extends ClearAfterClass { - ClearAfterClass.stateToClear = CallGraphTest + val compiler = cached("compiler", () => newCompiler(extraArgs = "-Yopt:inline-global -Yopt-warnings") + ) + import compiler.genBCode.bTypes + val notPerRun: List[Clearable] = List( + bTypes.classBTypeFromInternalName, + bTypes.byteCodeRepository.compilingClasses, + bTypes.byteCodeRepository.parsedClasses, + bTypes.callGraph.callsites) + notPerRun foreach compiler.perRunCaches.unrecordCache - val compiler = CallGraphTest.compiler import compiler.genBCode.bTypes._ import callGraph._ def compile(code: String, allowMessage: StoreReporter#Info => Boolean = _ => false): List[ClassNode] = { - CallGraphTest.notPerRun.foreach(_.clear()) + notPerRun.foreach(_.clear()) compileClasses(compiler)(code, allowMessage = allowMessage).map(c => byteCodeRepository.classNode(c.name).get) } diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/ClosureOptimizerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/ClosureOptimizerTest.scala index 12bfba71a8..e8530af4e0 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/ClosureOptimizerTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/ClosureOptimizerTest.scala @@ -27,16 +27,9 @@ import BackendReporting._ import scala.collection.JavaConverters._ import scala.tools.testing.ClearAfterClass -object ClosureOptimizerTest extends ClearAfterClass.Clearable { - var compiler = newCompiler(extraArgs = "-Yopt:l:classpath -Yopt-warnings:_") - def clear(): Unit = { compiler = null } -} - @RunWith(classOf[JUnit4]) class ClosureOptimizerTest extends ClearAfterClass { - ClearAfterClass.stateToClear = ClosureOptimizerTest - - val compiler = ClosureOptimizerTest.compiler + val compiler = cached("compiler", () => newCompiler(extraArgs = "-Yopt:l:classpath -Yopt-warnings:_")) @Test def nothingTypedClosureBody(): Unit = { 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 22aed4207f..6d566c722f 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala @@ -13,21 +13,11 @@ import scala.tools.partest.ASMConverters import ASMConverters._ import scala.tools.testing.ClearAfterClass -object EmptyExceptionHandlersTest extends ClearAfterClass.Clearable { - var noOptCompiler = newCompiler(extraArgs = "-Yopt:l:none") - var dceCompiler = newCompiler(extraArgs = "-Yopt:unreachable-code") - def clear(): Unit = { - noOptCompiler = null - dceCompiler = null - } -} @RunWith(classOf[JUnit4]) class EmptyExceptionHandlersTest extends ClearAfterClass { - ClearAfterClass.stateToClear = EmptyExceptionHandlersTest - - val noOptCompiler = EmptyExceptionHandlersTest.noOptCompiler - val dceCompiler = EmptyExceptionHandlersTest.dceCompiler + val noOptCompiler = cached("noOptCompiler", () => newCompiler(extraArgs = "-Yopt:l:none")) + val dceCompiler = cached("dceCompiler", () => newCompiler(extraArgs = "-Yopt:unreachable-code")) val exceptionDescriptor = "java/lang/Exception" diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala index 23386bb5ae..5cb1aab4a9 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala @@ -18,25 +18,19 @@ import BackendReporting._ import scala.collection.JavaConverters._ -object InlineInfoTest extends ClearAfterClass.Clearable { - var compiler = newCompiler(extraArgs = "-Yopt:l:classpath") - def clear(): Unit = { compiler = null } - - def notPerRun: List[Clearable] = List( - compiler.genBCode.bTypes.classBTypeFromInternalName, - compiler.genBCode.bTypes.byteCodeRepository.compilingClasses, - compiler.genBCode.bTypes.byteCodeRepository.parsedClasses) - notPerRun foreach compiler.perRunCaches.unrecordCache -} - @RunWith(classOf[JUnit4]) class InlineInfoTest extends ClearAfterClass { - ClearAfterClass.stateToClear = InlineInfoTest + val compiler = cached("compiler", () => newCompiler(extraArgs = "-Yopt:l:classpath")) - val compiler = InlineInfoTest.compiler + import compiler.genBCode.bTypes + def notPerRun: List[Clearable] = List( + bTypes.classBTypeFromInternalName, + bTypes.byteCodeRepository.compilingClasses, + bTypes.byteCodeRepository.parsedClasses) + notPerRun foreach compiler.perRunCaches.unrecordCache def compile(code: String) = { - InlineInfoTest.notPerRun.foreach(_.clear()) + notPerRun.foreach(_.clear()) compileClasses(compiler)(code) } diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala index 1597c75a7e..6dd0a33289 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala @@ -27,20 +27,12 @@ import BackendReporting._ import scala.collection.JavaConverters._ import scala.tools.testing.ClearAfterClass -object InlineWarningTest extends ClearAfterClass.Clearable { - val argsNoWarn = "-Yopt:l:classpath" - val args = argsNoWarn + " -Yopt-warnings" - var compiler = newCompiler(extraArgs = args) - var compilerWarnAll = newCompiler(extraArgs = argsNoWarn + " -Yopt-warnings:_") - def clear(): Unit = { compiler = null; compilerWarnAll = null } -} - @RunWith(classOf[JUnit4]) class InlineWarningTest extends ClearAfterClass { - ClearAfterClass.stateToClear = InlineWarningTest - - val compiler = InlineWarningTest.compiler - val compilerWarnAll = InlineWarningTest.compilerWarnAll + val argsNoWarn = "-Yopt:l:classpath" + val args = argsNoWarn + " -Yopt-warnings" + val compiler = cached("compiler", () => newCompiler(extraArgs = args)) + val compilerWarnAll = cached("compilerWarnAll", () => newCompiler(extraArgs = argsNoWarn + " -Yopt-warnings:_")) def compile(scalaCode: String, javaCode: List[(String, String)] = Nil, allowMessage: StoreReporter#Info => Boolean = _ => false, compiler: Global = compiler): List[ClassNode] = { compileClasses(compiler)(scalaCode, javaCode, allowMessage) @@ -115,10 +107,10 @@ class InlineWarningTest extends ClearAfterClass { assert(c == 1, c) // no warnings here - compileClasses(newCompiler(extraArgs = InlineWarningTest.argsNoWarn + " -Yopt-warnings:none"))(scalaCode, List((javaCode, "A.java"))) + compileClasses(newCompiler(extraArgs = argsNoWarn + " -Yopt-warnings:none"))(scalaCode, List((javaCode, "A.java"))) c = 0 - compileClasses(newCompiler(extraArgs = InlineWarningTest.argsNoWarn + " -Yopt-warnings:no-inline-mixed"))(scalaCode, List((javaCode, "A.java")), allowMessage = i => {c += 1; warns.exists(i.msg contains _)}) + compileClasses(newCompiler(extraArgs = argsNoWarn + " -Yopt-warnings:no-inline-mixed"))(scalaCode, List((javaCode, "A.java")), allowMessage = i => {c += 1; warns.exists(i.msg contains _)}) assert(c == 2, c) } 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 6460158e71..ab1aef47cd 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala @@ -19,16 +19,9 @@ import AsmUtils._ import scala.collection.JavaConverters._ import scala.tools.testing.ClearAfterClass -object InlinerIllegalAccessTest extends ClearAfterClass.Clearable { - var compiler = newCompiler(extraArgs = "-Yopt:l:none") - def clear(): Unit = { compiler = null } -} - @RunWith(classOf[JUnit4]) class InlinerIllegalAccessTest extends ClearAfterClass { - ClearAfterClass.stateToClear = InlinerIllegalAccessTest - - val compiler = InlinerIllegalAccessTest.compiler + val compiler = cached("compiler", () => newCompiler(extraArgs = "-Yopt:l:none")) import compiler.genBCode.bTypes._ def addToRepo(cls: List[ClassNode]): Unit = for (c <- cls) byteCodeRepository.add(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 e2a495fb2b..b7641b5ec7 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala @@ -22,35 +22,27 @@ import BackendReporting._ import scala.collection.JavaConverters._ import scala.tools.testing.ClearAfterClass -object InlinerTest extends ClearAfterClass.Clearable { +@RunWith(classOf[JUnit4]) +class InlinerTest extends ClearAfterClass { val args = "-Yopt:l:classpath -Yopt-warnings" - var compiler = newCompiler(extraArgs = args) - var inlineOnlyCompiler = newCompiler(extraArgs = "-Yopt:inline-project") - + val compiler = cached("compiler", () => newCompiler(extraArgs = args)) + val inlineOnlyCompiler = cached("inlineOnlyCompiler", () => newCompiler(extraArgs = "-Yopt:inline-project")) + import compiler.genBCode.bTypes // allows inspecting the caches after a compilation run def notPerRun: List[Clearable] = List( - compiler.genBCode.bTypes.classBTypeFromInternalName, - compiler.genBCode.bTypes.byteCodeRepository.compilingClasses, - compiler.genBCode.bTypes.byteCodeRepository.parsedClasses, - compiler.genBCode.bTypes.callGraph.callsites) + bTypes.classBTypeFromInternalName, + bTypes.byteCodeRepository.compilingClasses, + bTypes.byteCodeRepository.parsedClasses, + bTypes.callGraph.callsites) notPerRun foreach compiler.perRunCaches.unrecordCache - def clear(): Unit = { compiler = null; inlineOnlyCompiler = null } -} - -@RunWith(classOf[JUnit4]) -class InlinerTest extends ClearAfterClass { - ClearAfterClass.stateToClear = InlinerTest - - val compiler = InlinerTest.compiler import compiler.genBCode.bTypes._ import compiler.genBCode.bTypes.backendUtils._ import inlinerHeuristics._ - val inlineOnlyCompiler = InlinerTest.inlineOnlyCompiler def compile(scalaCode: String, javaCode: List[(String, String)] = Nil, allowMessage: StoreReporter#Info => Boolean = _ => false): List[ClassNode] = { - InlinerTest.notPerRun.foreach(_.clear()) + notPerRun.foreach(_.clear()) compileClasses(compiler)(scalaCode, javaCode, allowMessage) // Use the class nodes stored in the byteCodeRepository. The ones returned by compileClasses are not the same, // these are created new from the classfile byte array. They are completely separate instances which cannot @@ -837,7 +829,7 @@ class InlinerTest extends ClearAfterClass { var c = 0 - compileClasses(newCompiler(extraArgs = InlinerTest.args + " -Yopt-warnings:_"))( + compileClasses(newCompiler(extraArgs = args + " -Yopt-warnings:_"))( scalaCode, List((javaCode, "A.java")), allowMessage = i => {c += 1; i.msg contains warn}) @@ -899,7 +891,7 @@ class InlinerTest extends ClearAfterClass { | def t = System.arraycopy(null, 0, null, 0, 0) |} """.stripMargin - val List(c) = compileClasses(newCompiler(extraArgs = InlinerTest.args + " -Yopt-inline-heuristics:everything"))(code) + val List(c) = compileClasses(newCompiler(extraArgs = args + " -Yopt-inline-heuristics:everything"))(code) assertInvoke(getSingleMethod(c, "t"), "java/lang/System", "arraycopy") } diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOptsTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOptsTest.scala index dd7fbd9977..003b2d4880 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOptsTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOptsTest.scala @@ -18,16 +18,9 @@ import ASMConverters._ import scala.tools.testing.ClearAfterClass import scala.collection.JavaConverters._ -object MethodLevelOptsTest extends ClearAfterClass.Clearable { - var methodOptCompiler = newCompiler(extraArgs = "-Yopt:l:method") - def clear(): Unit = { methodOptCompiler = null } -} - @RunWith(classOf[JUnit4]) class MethodLevelOptsTest extends ClearAfterClass { - ClearAfterClass.stateToClear = MethodLevelOptsTest - - val methodOptCompiler = MethodLevelOptsTest.methodOptCompiler + val methodOptCompiler = cached("methodOptCompiler", () => newCompiler(extraArgs = "-Yopt:l:method")) 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/ScalaInlineInfoTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala index 8dd23ec3ce..6cb3fd3bba 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala @@ -16,15 +16,9 @@ import ASMConverters._ import scala.collection.JavaConverters._ import scala.tools.testing.ClearAfterClass -object ScalaInlineInfoTest extends ClearAfterClass.Clearable { - var compiler = newCompiler(extraArgs = "-Yopt:l:none") - def clear(): Unit = { compiler = null } -} - @RunWith(classOf[JUnit4]) class ScalaInlineInfoTest extends ClearAfterClass { - ClearAfterClass.stateToClear = ScalaInlineInfoTest - val compiler = ScalaInlineInfoTest.compiler + val compiler = cached("compiler", () => newCompiler(extraArgs = "-Yopt:l:none")) def inlineInfo(c: ClassNode): InlineInfo = c.attrs.asScala.collect({ case a: InlineInfoAttribute => a.inlineInfo }).head 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 0021a1784d..46f06d1d39 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala @@ -15,27 +15,13 @@ 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 = "-Yopt:l:method") - var dceCompiler = newCompiler(extraArgs = "-Yopt:unreachable-code") - var noOptCompiler = newCompiler(extraArgs = "-Yopt:l:none") - - def clear(): Unit = { - methodOptCompiler = null - dceCompiler = null - noOptCompiler = null - } -} - @RunWith(classOf[JUnit4]) class UnreachableCodeTest extends ClearAfterClass { - ClearAfterClass.stateToClear = UnreachableCodeTest - - val methodOptCompiler = UnreachableCodeTest.methodOptCompiler - val dceCompiler = UnreachableCodeTest.dceCompiler - val noOptCompiler = UnreachableCodeTest.noOptCompiler + // jvm-1.6 enables emitting stack map frames, which impacts the code generation wrt dead basic blocks, + // see comment in BCodeBodyBuilder + val methodOptCompiler = cached("methodOptCompiler", () => newCompiler(extraArgs = "-Yopt:l:method")) + val dceCompiler = cached("dceCompiler", () => newCompiler(extraArgs = "-Yopt:unreachable-code")) + val noOptCompiler = cached("noOptCompiler", () => newCompiler(extraArgs = "-Yopt:l:none")) def assertEliminateDead(code: (Instruction, Boolean)*): Unit = { val method = genMethod()(code.map(_._1): _*) 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 4f71df1822..77e73e64b9 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/UnusedLocalVariablesTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/UnusedLocalVariablesTest.scala @@ -14,16 +14,9 @@ import scala.tools.partest.ASMConverters import ASMConverters._ import scala.tools.testing.ClearAfterClass -object UnusedLocalVariablesTest extends ClearAfterClass.Clearable { - var dceCompiler = newCompiler(extraArgs = "-Yopt:unreachable-code") - def clear(): Unit = { dceCompiler = null } -} - @RunWith(classOf[JUnit4]) class UnusedLocalVariablesTest extends ClearAfterClass { - ClearAfterClass.stateToClear = UnusedLocalVariablesTest - - val dceCompiler = UnusedLocalVariablesTest.dceCompiler + val dceCompiler = cached("dceCompiler", () => newCompiler(extraArgs = "-Yopt:unreachable-code")) @Test def removeUnusedVar(): Unit = { -- cgit v1.2.3