aboutsummaryrefslogtreecommitdiff
path: root/test/test
diff options
context:
space:
mode:
authorDmitry Petrashko <dmitry.petrashko@gmail.com>2014-03-12 16:02:45 +0100
committerDmitry Petrashko <dmitry.petrashko@gmail.com>2014-04-09 16:35:30 +0200
commitd010cef9d0f62b727a35140d470472ab8f8355f5 (patch)
tree00df7ddb33ca0be4028a43a1adc2fe1b908e2c24 /test/test
parent6bc463df6a6ea8312390915a65024b11cfdd2b77 (diff)
downloaddotty-d010cef9d0f62b727a35140d470472ab8f8355f5.tar.gz
dotty-d010cef9d0f62b727a35140d470472ab8f8355f5.tar.bz2
dotty-d010cef9d0f62b727a35140d470472ab8f8355f5.zip
Context escape detection.
During creation of each of DottyTests context is stolen from test and a WeakReference for it is created. After running all tests references are examined to detect if any of them has leaked.
Diffstat (limited to 'test/test')
-rw-r--r--test/test/ContextEscapeDetection.java40
-rw-r--r--test/test/ContextEscapeDetector.java109
-rw-r--r--test/test/DottyTest.scala8
3 files changed, 155 insertions, 2 deletions
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