groovy git commit: add tests

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

groovy git commit: add tests

jwagenleitner-2
Repository: groovy
Updated Branches:
  refs/heads/master beab89aff -> d40a6400c


add tests


Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/d40a6400
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/d40a6400
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/d40a6400

Branch: refs/heads/master
Commit: d40a6400c3151ac4223c0312ee5a5c1f70460fc7
Parents: beab89a
Author: John Wagenleitner <[hidden email]>
Authored: Sun Feb 12 13:13:22 2017 -0800
Committer: John Wagenleitner <[hidden email]>
Committed: Sun Feb 12 13:13:22 2017 -0800

----------------------------------------------------------------------
 .../reflection/ClassInfoLeakStressTest.java     | 101 ++++++++++++++
 .../util/ManagedConcurrentMapStressTest.java    | 136 +++++++++++++++++++
 .../ManagedConcurrentValueMapStressTest.java    | 135 ++++++++++++++++++
 3 files changed, 372 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/d40a6400/subprojects/stress/src/test/java/org/codehaus/groovy/reflection/ClassInfoLeakStressTest.java
----------------------------------------------------------------------
diff --git a/subprojects/stress/src/test/java/org/codehaus/groovy/reflection/ClassInfoLeakStressTest.java b/subprojects/stress/src/test/java/org/codehaus/groovy/reflection/ClassInfoLeakStressTest.java
new file mode 100644
index 0000000..fa4f2ff
--- /dev/null
+++ b/subprojects/stress/src/test/java/org/codehaus/groovy/reflection/ClassInfoLeakStressTest.java
@@ -0,0 +1,101 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.reflection;
+
+import groovy.lang.GroovyClassLoader;
+import org.apache.groovy.stress.util.GCUtils;
+import org.codehaus.groovy.util.ReferenceBundle;
+
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.codehaus.groovy.util.ReferenceManager;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class ClassInfoLeakStressTest {
+
+    private static final int NUM_OBJECTS = 3101;
+    private static ReferenceBundle bundle = ReferenceBundle.getWeakBundle();
+
+    private ReferenceQueue<ClassLoader> classLoaderQueue = new ReferenceQueue<ClassLoader>();
+    private ReferenceQueue<Class<?>> classQueue = new ReferenceQueue<Class<?>>();
+    private ReferenceQueue<ClassInfo> classInfoQueue = new ReferenceQueue<ClassInfo>();
+
+    // Used to keep a hard reference to the References so they are not collected
+    private List<Reference<?>> refList = new ArrayList<Reference<?>>(NUM_OBJECTS * 3);
+
+    @Before
+    public void setUp() {
+        // Make sure we switch over to callback manager
+        ReferenceManager manager = bundle.getManager();
+        for (int i = 0; i < 1501; i++) {
+            manager.afterReferenceCreation(null);
+        }
+    }
+
+    @Test
+    public void testLeak() {
+        assertFalse(Boolean.getBoolean("groovy.use.classvalue"));
+        for (int i = 0; i < NUM_OBJECTS; i++) {
+            GroovyClassLoader gcl = new GroovyClassLoader();
+            Class scriptClass = gcl.parseClass("int myvar = " + i);
+            ClassInfo ci = ClassInfo.getClassInfo(scriptClass);
+            Reference<ClassLoader> classLoaderRef = new WeakReference<ClassLoader>(gcl, classLoaderQueue);
+            Reference<Class<?>> classRef = new WeakReference<Class<?>>(scriptClass, classQueue);
+            Reference<ClassInfo> classInfoRef = new WeakReference<ClassInfo>(ci, classInfoQueue);
+            refList.add(classLoaderRef);
+            refList.add(classRef);
+            refList.add(classInfoRef);
+            gcl = null;
+            scriptClass = null;
+            ci = null;
+            GCUtils.gc();
+        }
+
+        // Add new class to help evict the last collected entry
+        GroovyClassLoader gcl = new GroovyClassLoader();
+        Class scriptClass = gcl.parseClass("int myvar = 7777");
+        ClassInfo ci = ClassInfo.getClassInfo(scriptClass);
+
+        GCUtils.gc();
+
+        // All objects should have been collected
+        assertEquals("GroovyClassLoaders not collected by GC", NUM_OBJECTS, queueSize(classLoaderQueue));
+        assertEquals("Script Classes not collected by GC", NUM_OBJECTS, queueSize(classQueue));
+
+        int ciSize = queueSize(classInfoQueue);
+        assertEquals("ClassInfo objects [" + ciSize + "] collected by GC, expected [" + NUM_OBJECTS + "]",
+                NUM_OBJECTS, ciSize);
+    }
+
+    private int queueSize(ReferenceQueue<?> queue) {
+        int size = 0;
+        while (queue.poll() != null) {
+            ++size;
+        }
+        return size;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/d40a6400/subprojects/stress/src/test/java/org/codehaus/groovy/util/ManagedConcurrentMapStressTest.java
----------------------------------------------------------------------
diff --git a/subprojects/stress/src/test/java/org/codehaus/groovy/util/ManagedConcurrentMapStressTest.java b/subprojects/stress/src/test/java/org/codehaus/groovy/util/ManagedConcurrentMapStressTest.java
new file mode 100644
index 0000000..0fb936f
--- /dev/null
+++ b/subprojects/stress/src/test/java/org/codehaus/groovy/util/ManagedConcurrentMapStressTest.java
@@ -0,0 +1,136 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.util;
+
+import org.apache.groovy.stress.util.GCUtils;
+import org.apache.groovy.stress.util.ThreadUtils;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.*;
+
+public class ManagedConcurrentMapStressTest {
+
+    static final int ENTRY_COUNT = 10371;
+
+    static final ReferenceBundle bundle = ReferenceBundle.getWeakBundle();
+
+    @Test
+    public void testMapRemovesCollectedReferences() throws Exception {
+        ManagedConcurrentMap<Object, String> map = new ManagedConcurrentMap<Object, String>(bundle);
+
+        // Keep a hardref so we can test get later
+        List<Object> keyList = populate(map);
+        assertEquals(ENTRY_COUNT, map.size());
+
+        // Make sure we still have our entries, sample a few
+        Object key1337 = keyList.remove(1337);
+        assertEquals("value1337", map.get(key1337));
+
+        Object key77 = keyList.remove(77);
+        assertEquals("value77", map.get(key77));
+
+        key1337 = null;
+        key77 = null;
+
+        GCUtils.gc();
+        assertEquals(ENTRY_COUNT - 2, map.size());
+        for (Object o : map.values()) {
+            if (o instanceof AbstractConcurrentMapBase.Entry<?>) {
+                @SuppressWarnings("unchecked")
+                AbstractConcurrentMapBase.Entry<String> e = (AbstractConcurrentMapBase.Entry)o;
+                if ("value77".equals(e.getValue()) || "value1337".equals(e.getValue())) {
+                    fail("Entries not removed from map");
+                }
+            } else {
+                fail("No Entry found");
+            }
+        }
+
+        // Clear all refs and gc()
+        keyList.clear();
+        GCUtils.gc();
+
+        // Add an entries to force ReferenceManager.removeStaleEntries
+        map.put(new Object(), "last");
+        assertEquals("Map removed weak entries", 1, map.size());
+    }
+
+    /**
+     * This tests for deadlock which can happen if more than one thread is allowed
+     * to process entries from the same RefQ. We run multiple iterations because it
+     * wont always be detected one run.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testMultipleThreadsPutWhileRemovingRefs() throws Exception {
+        for (int i = 0; i < 10; i++) {
+            ManagedConcurrentMap<Object, String> map = new ManagedConcurrentMap<Object, String>(bundle);
+            multipleThreadsPutWhileRemovingRefs(map);
+        }
+    }
+
+    private void multipleThreadsPutWhileRemovingRefs(final ManagedConcurrentMap<Object, String> map) throws Exception {
+        List<Object> keyList1 = populate(map);
+        List<Object> keyList2 = populate(map);
+        assertEquals(keyList1.size() + keyList2.size(), map.size());
+
+        // Place some values on the ReferenceQueue
+        keyList1.clear();
+        GCUtils.gc();
+
+        final int threadCount = 16;
+        final CyclicBarrier barrier = new CyclicBarrier(threadCount + 1);
+        final Object[] threadKeys = new Object[threadCount];
+        for (int i = 0; i < threadCount; i++) {
+            final int idx = i;
+            Thread t = new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    Object k = new Object();
+                    threadKeys[idx] = k;
+                    String v = "thread-" + idx;
+                    ThreadUtils.await(barrier);
+                    map.put(k, v);
+                    ThreadUtils.await(barrier);
+                }
+            });
+            t.setDaemon(true);
+            t.start();
+        }
+        barrier.await(); // start threads
+        barrier.await(30L, TimeUnit.SECONDS); // wait for them to complete
+        assertEquals(keyList2.size() + threadCount, map.size());
+    }
+
+    private List<Object> populate(ManagedConcurrentMap<Object, String> map) {
+        List<Object> elements = new ArrayList<Object>(ENTRY_COUNT);
+        for (int i = 0; i < ENTRY_COUNT; i++) {
+            Object key = new Object();
+            elements.add(key);
+            map.put(key, "value" + i);
+        }
+        return elements;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/d40a6400/subprojects/stress/src/test/java/org/codehaus/groovy/util/ManagedConcurrentValueMapStressTest.java
----------------------------------------------------------------------
diff --git a/subprojects/stress/src/test/java/org/codehaus/groovy/util/ManagedConcurrentValueMapStressTest.java b/subprojects/stress/src/test/java/org/codehaus/groovy/util/ManagedConcurrentValueMapStressTest.java
new file mode 100644
index 0000000..d379f97
--- /dev/null
+++ b/subprojects/stress/src/test/java/org/codehaus/groovy/util/ManagedConcurrentValueMapStressTest.java
@@ -0,0 +1,135 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.util;
+
+import groovy.lang.MetaClass;
+import org.apache.groovy.stress.util.GCUtils;
+import org.apache.groovy.stress.util.ThreadUtils;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.*;
+
+public class ManagedConcurrentValueMapStressTest {
+
+    static final int ENTRY_COUNT = 10371;
+
+    static final ReferenceBundle bundle = ReferenceBundle.getWeakBundle();
+
+    @Test
+    public void testMapRemovesCollectedReferences() throws InterruptedException {
+        ManagedConcurrentValueMap<String, Object> map = new ManagedConcurrentValueMap<String, Object>(bundle);
+
+        // Keep a hardref so we can test get later
+        List<Object> valueList = populate(map);
+
+        // Make sure we still have our entries, sample a few
+        Object value77 = map.get("key77");
+        assertEquals(valueList.get(77), value77);
+
+        Object value1337 = map.get("key1337");
+        assertEquals(valueList.get(1337), value1337);
+
+        // Clear hardrefs and gc()
+        value77 = null;
+        value1337 = null;
+        valueList.clear();
+
+        GCUtils.gc();
+
+        // Add an entries to force ReferenceManager.removeStaleEntries
+        map.put("keyLast", new Object());
+
+        // No size() method, so let's just check a few keys we that should have been collected
+        assertEquals(null, map.get("key77"));
+        assertEquals(null, map.get("key1337"));
+        assertEquals(null, map.get("key3559"));
+
+        assertEquals(1, size(map));
+    }
+
+    /**
+     * This tests for deadlock which can happen if more than one thread is allowed
+     * to process entries from the same RefQ. We run multiple iterations because it
+     * wont always be detected one run.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testMultipleThreadsPutWhileRemovingRefs() throws Exception {
+        for (int i = 0; i < 10; i++) {
+            ManagedConcurrentValueMap<String, Object> map = new ManagedConcurrentValueMap<String, Object>(bundle);
+            multipleThreadsPutWhileRemovingRefs(map);
+        }
+    }
+
+    private void multipleThreadsPutWhileRemovingRefs(final ManagedConcurrentValueMap<String, Object> map) throws Exception {
+        List<Object> valueList1 = populate(map);
+        List<Object> valueList2 = populate(map);
+
+        // Place some values on the ReferenceQueue
+        valueList1.clear();
+        GCUtils.gc();
+
+        final int threadCount = 16;
+        final CyclicBarrier barrier = new CyclicBarrier(threadCount + 1);
+        final Object[] threadValues = new Object[threadCount];
+        for (int i = 0; i < threadCount; i++) {
+            final int idx = i;
+            Thread t = new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    Object v = new Object();
+                    threadValues[idx] = v;
+                    String k = "thread-" + idx;
+                    ThreadUtils.await(barrier);
+                    map.put(k, v);
+                    ThreadUtils.await(barrier);
+                }
+            });
+            t.setDaemon(true);
+            t.start();
+        }
+        barrier.await(); // start threads
+        barrier.await(30L, TimeUnit.SECONDS); // wait for them to complete
+        assertEquals(ENTRY_COUNT + threadValues.length, size(map));
+    }
+
+    private List<Object> populate(ManagedConcurrentValueMap<String, Object> map) {
+        List<Object> elements = new ArrayList<Object>(ENTRY_COUNT);
+        for (int i = 0; i < ENTRY_COUNT; i++) {
+            Object val = new Object();
+            elements.add(val);
+            map.put("key" + i, val);
+        }
+        return elements;
+    }
+
+    private static int size(ManagedConcurrentValueMap<String, Object> map) {
+        MetaClass metaClass = InvokerHelper.getMetaClass(map);
+        ConcurrentHashMap<String, Object> internalMap = (ConcurrentHashMap<String, Object>)metaClass.getProperty(map, "internalMap");
+        return internalMap.size();
+    }
+}