aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--project/Build.scala8
-rw-r--r--test/test/ContextEscapeDetection.java40
-rw-r--r--test/test/ContextEscapeDetector.java109
-rw-r--r--test/test/DottyTest.scala8
4 files changed, 160 insertions, 5 deletions
diff --git a/project/Build.scala b/project/Build.scala
index 8442ce5de..310a7d05e 100644
--- a/project/Build.scala
+++ b/project/Build.scala
@@ -15,7 +15,7 @@ object DottyBuild extends Build {
resourceDirectory in Compile := baseDirectory.value / "resources",
unmanagedSourceDirectories in Compile := Seq((scalaSource in Compile).value),
unmanagedSourceDirectories in Test := Seq((scalaSource in Test).value),
-
+
// include sources in eclipse (downloads source code for all dependencies)
//http://stackoverflow.com/questions/10472840/how-to-attach-sources-to-sbt-managed-dependencies-in-scala-ide#answer-11683728
com.typesafe.sbteclipse.plugin.EclipsePlugin.EclipseKeys.withSource := true,
@@ -28,17 +28,19 @@ object DottyBuild extends Build {
"org.scala-lang.modules" %% "scala-xml" % "1.0.1"),
// get junit onboard
- libraryDependencies += "com.novocode" % "junit-interface" % "0.9" % "test",
+ libraryDependencies += "com.novocode" % "junit-interface" % "0.11-RC1" % "test",
// scalac options
scalacOptions in Global ++= Seq("-feature", "-deprecation", "-language:_"),
// enable verbose exception messages for JUnit
- testOptions += Tests.Argument(TestFrameworks.JUnit, "-a", "-v"),
+ testOptions += Tests.Argument(TestFrameworks.JUnit, "-a", "-v", "--run-listener=test.ContextEscapeDetector"),
// Adjust classpath for running dotty
mainClass in (Compile, run) := Some("dotty.tools.dotc.Main"),
fork in run := true,
fork in Test := true,
+ parallelExecution in Test := false,
+
// http://grokbase.com/t/gg/simple-build-tool/135ke5y90p/sbt-setting-jvm-boot-paramaters-for-scala
javaOptions <++= (managedClasspath in Runtime, packageBin in Compile) map { (attList, bin) =>
// put the Scala {library, reflect, compiler} in the classpath
diff --git a/test/test/ContextEscapeDetection.java b/test/test/ContextEscapeDetection.java
new file mode 100644
index 000000000..233630eb2
--- /dev/null
+++ b/test/test/ContextEscapeDetection.java
@@ -0,0 +1,40 @@
+package test;
+
+import dotty.tools.dotc.core.Contexts;
+import org.junit.*;
+
+import java.lang.ref.WeakReference;
+import java.util.LinkedList;
+import java.util.List;
+
+
+public abstract class ContextEscapeDetection {
+ public static class TestContext{
+ public TestContext(WeakReference<Contexts.Context> context, String testName) {
+ this.context = context;
+ this.testName = testName;
+ }
+
+ public final WeakReference<Contexts.Context> context;
+ public final String testName;
+
+ }
+ public static final List<TestContext> contexts = new LinkedList<>();
+
+ public abstract Contexts.Context getCtx();
+
+ public abstract void clearCtx();
+
+ @Before
+ public synchronized void stealContext() {
+ contexts.add(new TestContext(new WeakReference<>(this.getCtx()), this.getClass().getName()));
+ }
+
+ @After
+ public synchronized void clearContext() {
+ this.clearCtx();
+ }
+
+
+}
+
diff --git a/test/test/ContextEscapeDetector.java b/test/test/ContextEscapeDetector.java
new file mode 100644
index 000000000..c7768fd57
--- /dev/null
+++ b/test/test/ContextEscapeDetector.java
@@ -0,0 +1,109 @@
+package test;
+
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+import org.junit.runner.notification.RunListener;
+import org.junit.Assert;
+
+import java.lang.ref.WeakReference;
+
+public class ContextEscapeDetector extends RunListener {
+
+ //context can be captured by objects, eg NoDenotation
+ public static final int CONTEXTS_ALLOWED = 1;
+
+ @Override
+ public void testRunFinished(Result result) throws Exception {
+ if (contextsAlive() > CONTEXTS_ALLOWED) {
+ forceGCHeuristic0();
+ if (contextsAlive() > CONTEXTS_ALLOWED) {
+ forceGCHeuristic1();
+ if (contextsAlive() > CONTEXTS_ALLOWED) {
+ forceGCHeuristic2();
+ forceGCHeuristic1();
+ int contextAlive = contextsAlive();
+ if (contextAlive > CONTEXTS_ALLOWED) {
+ StringBuilder names = new StringBuilder();
+ for (ContextEscapeDetection.TestContext ref : ContextEscapeDetection.contexts) {
+ if (ref.context.get() != null) names.append(ref.testName).append(' ');
+ }
+ Assert.fail("Multiple contexts survived test suite: " + names.toString());
+ }
+ }
+ }
+ }
+ super.testRunFinished(result);
+ }
+
+ private static synchronized int contextsAlive() {
+ int count = 0;
+ for (ContextEscapeDetection.TestContext ref : ContextEscapeDetection.contexts) {
+ if (ref.context.get() != null) count++;
+ }
+ return count;
+ }
+
+ private static volatile Object o = null;
+
+ private static synchronized void forceGCHeuristic0() {
+ System.gc();
+ Runtime.getRuntime().gc();
+ System.gc();
+ Runtime.getRuntime().gc();
+ System.gc();
+ Runtime.getRuntime().gc();
+ System.gc();
+ Runtime.getRuntime().gc();
+ System.gc();
+ }
+
+ private static synchronized void forceGCHeuristic1() {
+ Object obj = new Object();
+ WeakReference ref = new WeakReference<Object>(obj);
+ obj = null;
+ while (ref.get() != null) {
+ System.gc();
+ }
+ }
+
+ private static synchronized void forceGCHeuristic2() {
+ try {
+ Object[] arr = new Object[1024]; // upto 8 GB
+ WeakReference ref = new WeakReference<Object>(arr);
+ o = arr; // make sure array isn't optimized away
+
+ Runtime runtime = Runtime.getRuntime();
+ // allocate memory until no more that 64MB is left
+ for (int i = 0; i < 1024 &&
+ runtime.totalMemory() != runtime.maxMemory() ||
+ runtime.freeMemory() < 1024 * 1024 * 64; i++) {
+ int[] data = new int[1024 * 1024]; // 8MB
+ for (int j = 0; j < 1024 * 1024; j++) {
+ data[j] = j; // force actual pages allocation
+ }
+ arr[i] = data;
+ }
+ o = null;
+ arr = new Object[128];
+ o = arr;
+ // allocate 1 more GB
+ for (int i = 0; i < 128; i++) {
+ int[] data = new int[1024 * 1024]; // 8MB
+ for (int j = 0; j < 1024 * 1024; j++) {
+ data[j] = j; // force actual pages allocation
+ }
+ arr[i] = data;
+ }
+ o = null;
+ arr = null;
+
+ forceGCHeuristic0();
+ while (ref.get() != null) {
+ System.gc();
+ }
+ } catch (OutOfMemoryError e) {
+ o = null;
+ // just swallow
+ }
+ }
+}
diff --git a/test/test/DottyTest.scala b/test/test/DottyTest.scala
index 3c308e230..4efb1e615 100644
--- a/test/test/DottyTest.scala
+++ b/test/test/DottyTest.scala
@@ -14,11 +14,11 @@ import dotty.tools.dotc.Compiler
import dotty.tools.dotc
import dotty.tools.dotc.core.Phases.Phase
-class DottyTest {
+class DottyTest extends ContextEscapeDetection{
dotty.tools.dotc.parsing.Scanners // initialize keywords
- implicit val ctx: Context = {
+ implicit var ctx: Contexts.Context = {
val base = new ContextBase
import base.settings._
val ctx = base.initialCtx.fresh
@@ -37,6 +37,10 @@ class DottyTest {
ctx
}
+ override def getCtx: Context = ctx
+ override def clearCtx() = {
+ ctx = null
+ }
private def compilerWithChecker(phase: String)(assertion:(tpd.Tree, Context) => Unit) = new Compiler {
override def phases = {
val allPhases = super.phases