/* * 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.apache.commons.logging.impl; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import junit.framework.TestCase; public class WeakHashtableTestCase extends TestCase { private static final int WAIT_FOR_THREAD_COMPLETION = 5000; // 5 seconds private static final int RUN_LOOPS = 3000; private static final int OUTER_LOOP = 400; private static final int THREAD_COUNT = 10; private static WeakHashtable hashtable; /** Maximum number of iterations before our test fails */ private static final int MAX_GC_ITERATIONS = 50; private WeakHashtable weakHashtable; private Long keyOne; private Long keyTwo; private Long keyThree; private Long valueOne; private Long valueTwo; private Long valueThree; public WeakHashtableTestCase(String testName) { super(testName); } protected void setUp() throws Exception { super.setUp(); weakHashtable = new WeakHashtable(); keyOne = new Long(1); keyTwo = new Long(2); keyThree = new Long(3); valueOne = new Long(100); valueTwo = new Long(200); valueThree = new Long(300); weakHashtable.put(keyOne, valueOne); weakHashtable.put(keyTwo, valueTwo); weakHashtable.put(keyThree, valueThree); } /** Tests public boolean contains(Object value) */ public void testContains() throws Exception { assertFalse(weakHashtable.contains(new Long(1))); assertFalse(weakHashtable.contains(new Long(2))); assertFalse(weakHashtable.contains(new Long(3))); assertTrue(weakHashtable.contains(new Long(100))); assertTrue(weakHashtable.contains(new Long(200))); assertTrue(weakHashtable.contains(new Long(300))); assertFalse(weakHashtable.contains(new Long(400))); } /** Tests public boolean containsKey(Object key) */ public void testContainsKey() throws Exception { assertTrue(weakHashtable.containsKey(new Long(1))); assertTrue(weakHashtable.containsKey(new Long(2))); assertTrue(weakHashtable.containsKey(new Long(3))); assertFalse(weakHashtable.containsKey(new Long(100))); assertFalse(weakHashtable.containsKey(new Long(200))); assertFalse(weakHashtable.containsKey(new Long(300))); assertFalse(weakHashtable.containsKey(new Long(400))); } /** Tests public boolean containsValue(Object value) */ public void testContainsValue() throws Exception { assertFalse(weakHashtable.containsValue(new Long(1))); assertFalse(weakHashtable.containsValue(new Long(2))); assertFalse(weakHashtable.containsValue(new Long(3))); assertTrue(weakHashtable.containsValue(new Long(100))); assertTrue(weakHashtable.containsValue(new Long(200))); assertTrue(weakHashtable.containsValue(new Long(300))); assertFalse(weakHashtable.containsValue(new Long(400))); } /** Tests public Enumeration elements() */ public void testElements() throws Exception { ArrayList elements = new ArrayList(); for (Enumeration e = weakHashtable.elements(); e.hasMoreElements();) { elements.add(e.nextElement()); } assertEquals(3, elements.size()); assertTrue(elements.contains(valueOne)); assertTrue(elements.contains(valueTwo)); assertTrue(elements.contains(valueThree)); } /** Tests public Set entrySet() */ public void testEntrySet() throws Exception { Set entrySet = weakHashtable.entrySet(); for (Iterator it = entrySet.iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry) it.next(); Object key = entry.getKey(); if (keyOne.equals(key)) { assertEquals(valueOne, entry.getValue()); } else if (keyTwo.equals(key)) { assertEquals(valueTwo, entry.getValue()); } else if (keyThree.equals(key)) { assertEquals(valueThree, entry.getValue()); } else { fail("Unexpected key"); } } } /** Tests public Object get(Object key) */ public void testGet() throws Exception { assertEquals(valueOne, weakHashtable.get(keyOne)); assertEquals(valueTwo, weakHashtable.get(keyTwo)); assertEquals(valueThree, weakHashtable.get(keyThree)); assertNull(weakHashtable.get(new Long(50))); } /** Tests public Enumeration keys() */ public void testKeys() throws Exception { ArrayList keys = new ArrayList(); for (Enumeration e = weakHashtable.keys(); e.hasMoreElements();) { keys.add(e.nextElement()); } assertEquals(3, keys.size()); assertTrue(keys.contains(keyOne)); assertTrue(keys.contains(keyTwo)); assertTrue(keys.contains(keyThree)); } /** Tests public Set keySet() */ public void testKeySet() throws Exception { Set keySet = weakHashtable.keySet(); assertEquals(3, keySet.size()); assertTrue(keySet.contains(keyOne)); assertTrue(keySet.contains(keyTwo)); assertTrue(keySet.contains(keyThree)); } /** Tests public Object put(Object key, Object value) */ public void testPut() throws Exception { Long anotherKey = new Long(2004); weakHashtable.put(anotherKey, new Long(1066)); assertEquals(new Long(1066), weakHashtable.get(anotherKey)); // Test compliance with the hashtable API re nulls Exception caught = null; try { weakHashtable.put(null, new Object()); } catch (Exception e) { caught = e; } assertNotNull("did not throw an exception adding a null key", caught); caught = null; try { weakHashtable.put(new Object(), null); } catch (Exception e) { caught = e; } assertNotNull("did not throw an exception adding a null value", caught); } /** Tests public void putAll(Map t) */ public void testPutAll() throws Exception { Map newValues = new HashMap(); Long newKey = new Long(1066); Long newValue = new Long(1415); newValues.put(newKey, newValue); Long anotherNewKey = new Long(1645); Long anotherNewValue = new Long(1815); newValues.put(anotherNewKey, anotherNewValue); weakHashtable.putAll(newValues); assertEquals(5, weakHashtable.size()); assertEquals(newValue, weakHashtable.get(newKey)); assertEquals(anotherNewValue, weakHashtable.get(anotherNewKey)); } /** Tests public Object remove(Object key) */ public void testRemove() throws Exception { weakHashtable.remove(keyOne); assertEquals(2, weakHashtable.size()); assertNull(weakHashtable.get(keyOne)); } /** Tests public Collection values() */ public void testValues() throws Exception { Collection values = weakHashtable.values(); assertEquals(3, values.size()); assertTrue(values.contains(valueOne)); assertTrue(values.contains(valueTwo)); assertTrue(values.contains(valueThree)); } /** * Disabled this test as it makes wrong assumptions wrt the GC. * This test especially fails with: * * Java(TM) SE Runtime Environment (build pxi3260sr12-20121025_01(SR12)) * IBM J9 VM (build 2.4, JRE 1.6.0 IBM J9 2.4 Linux x86-32 jvmxi3260sr12-20121024_1 */ public void xxxIgnoretestRelease() throws Exception { assertNotNull(weakHashtable.get(new Long(1))); ReferenceQueue testQueue = new ReferenceQueue(); WeakReference weakKeyOne = new WeakReference(keyOne, testQueue); // lose our references keyOne = null; keyTwo = null; keyThree = null; valueOne = null; valueTwo = null; valueThree = null; int iterations = 0; int bytz = 2; while(true) { System.gc(); if(iterations++ > MAX_GC_ITERATIONS){ fail("Max iterations reached before resource released."); } if(weakHashtable.get(new Long(1)) == null) { break; } else { // create garbage: byte[] b = new byte[bytz]; bytz = bytz * 2; } } // some JVMs seem to take a little time to put references on // the reference queue once the reference has been collected // need to think about whether this is enough to justify // stepping through the collection each time... while(testQueue.poll() == null) {} // Test that the released objects are not taking space in the table assertEquals("underlying table not emptied", 0, weakHashtable.size()); } public static class StupidThread extends Thread { public StupidThread(String name) { super(name); } public void run() { for (int i = 0; i < RUN_LOOPS; i++) { hashtable.put("key" + ":" + i%10, Boolean.TRUE); if(i%50 == 0) { yield(); } } } } public void testLOGGING_119() throws Exception { Thread [] t = new Thread[THREAD_COUNT]; for (int j=1; j <= OUTER_LOOP; j++) { hashtable = new WeakHashtable(); for (int i = 0; i < t.length; i++) { t[i] = new StupidThread("Thread:" + i); t[i].setDaemon(true); // Otherwise we cannot exit t[i].start(); } for (int i = 0; i < t.length; i++) { t[i].join(WAIT_FOR_THREAD_COMPLETION); if (t[i].isAlive()) { break; // at least one thread is stuck } } int active=0; for (int i = 0; i < t.length; i++) { if (t[i].isAlive()) { active++; } } if (active > 0) { fail("Attempt: " + j + " Stuck threads: " + active); } } } }