1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 
18 package org.apache.harmony.tests.java.util;
19 
20 import java.util.AbstractMap;
21 import java.util.Arrays;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Comparator;
25 import java.util.ConcurrentModificationException;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32 import java.util.Spliterator;
33 import java.util.WeakHashMap;
34 import libcore.java.lang.ref.FinalizationTester;
35 
36 import libcore.java.util.SpliteratorTester;
37 import tests.support.Support_MapTest2;
38 
39 public class WeakHashMapTest extends junit.framework.TestCase {
40     class MockMap extends AbstractMap {
entrySet()41         public Set entrySet() {
42             return null;
43         }
44 
size()45         public int size() {
46             return 0;
47         }
48     }
49 
50     Object[] keyArray = new Object[100];
51 
52     Object[] valueArray = new Object[100];
53 
54     WeakHashMap whm;
55 
56     /**
57      * java.util.WeakHashMap#WeakHashMap()
58      */
test_Constructor()59     public void test_Constructor() {
60         // Test for method java.util.WeakHashMap()
61         new Support_MapTest2(new WeakHashMap()).runTest();
62 
63         whm = new WeakHashMap();
64         for (int i = 0; i < 100; i++)
65             whm.put(keyArray[i], valueArray[i]);
66         for (int i = 0; i < 100; i++)
67             assertTrue("Incorrect value retrieved", whm.get(keyArray[i]) == valueArray[i]);
68 
69     }
70 
71     /**
72      * java.util.WeakHashMap#WeakHashMap(int)
73      */
test_ConstructorI()74     public void test_ConstructorI() {
75         // Test for method java.util.WeakHashMap(int)
76         whm = new WeakHashMap(50);
77         for (int i = 0; i < 100; i++)
78             whm.put(keyArray[i], valueArray[i]);
79         for (int i = 0; i < 100; i++)
80             assertTrue("Incorrect value retrieved", whm.get(keyArray[i]) == valueArray[i]);
81 
82         WeakHashMap empty = new WeakHashMap(0);
83         assertNull("Empty weakhashmap access", empty.get("nothing"));
84         empty.put("something", "here");
85         assertTrue("cannot get element", empty.get("something") == "here");
86 
87         try {
88             new WeakHashMap(-50);
89             fail("IllegalArgumentException expected");
90         } catch (IllegalArgumentException e) {
91             //expected
92         }
93     }
94 
95     /**
96      * java.util.WeakHashMap#WeakHashMap(int, float)
97      */
test_ConstructorIF()98     public void test_ConstructorIF() {
99         // Test for method java.util.WeakHashMap(int, float)
100         whm = new WeakHashMap(50, 0.5f);
101         for (int i = 0; i < 100; i++)
102             whm.put(keyArray[i], valueArray[i]);
103         for (int i = 0; i < 100; i++)
104             assertTrue("Incorrect value retrieved", whm.get(keyArray[i]) == valueArray[i]);
105 
106         WeakHashMap empty = new WeakHashMap(0, 0.75f);
107         assertNull("Empty hashtable access", empty.get("nothing"));
108         empty.put("something", "here");
109         assertTrue("cannot get element", empty.get("something") == "here");
110 
111         try {
112             new WeakHashMap(50, -0.5f);
113             fail("IllegalArgumentException expected");
114         } catch (IllegalArgumentException e) {
115             //expected
116         }
117     }
118 
119     /**
120      * java.util.WeakHashMap#WeakHashMap(java.util.Map)
121      */
test_ConstructorLjava_util_Map()122     public void test_ConstructorLjava_util_Map() {
123         Map mockMap = new MockMap();
124         WeakHashMap map = new WeakHashMap(mockMap);
125         assertEquals("Size should be 0", 0, map.size());
126 
127         try {
128             new WeakHashMap(null);
129             fail("NullPointerException expected");
130         } catch (NullPointerException e) {
131             //expected
132         }
133     }
134 
135     /**
136      * java.util.WeakHashMap#clear()
137      */
test_clear()138     public void test_clear() {
139         // Test for method boolean java.util.WeakHashMap.clear()
140         whm = new WeakHashMap();
141         for (int i = 0; i < 100; i++)
142             whm.put(keyArray[i], valueArray[i]);
143         whm.clear();
144         assertTrue("Cleared map should be empty", whm.isEmpty());
145         for (int i = 0; i < 100; i++)
146             assertNull("Cleared map should only return null", whm.get(keyArray[i]));
147 
148     }
149 
150     /**
151      * java.util.WeakHashMap#containsKey(java.lang.Object)
152      */
test_containsKeyLjava_lang_Object()153     public void test_containsKeyLjava_lang_Object() {
154         // Test for method boolean java.util.WeakHashMap.containsKey()
155         whm = new WeakHashMap();
156         for (int i = 0; i < 100; i++)
157             whm.put(keyArray[i], valueArray[i]);
158         for (int i = 0; i < 100; i++)
159             assertTrue("Should contain referenced key", whm.containsKey(keyArray[i]));
160         keyArray[25] = null;
161         keyArray[50] = null;
162     }
163 
164     /**
165      * java.util.WeakHashMap#containsValue(java.lang.Object)
166      */
test_containsValueLjava_lang_Object()167     public void test_containsValueLjava_lang_Object() {
168         // Test for method boolean java.util.WeakHashMap.containsValue()
169         whm = new WeakHashMap();
170         for (int i = 0; i < 100; i++)
171             whm.put(keyArray[i], valueArray[i]);
172         for (int i = 0; i < 100; i++)
173             assertTrue("Should contain referenced value", whm.containsValue(valueArray[i]));
174         keyArray[25] = null;
175         keyArray[50] = null;
176     }
177 
178     /**
179      * java.util.WeakHashMap#entrySet()
180      */
test_entrySet()181     public void test_entrySet() {
182         // Test for method java.util.Set java.util.WeakHashMap.entrySet()
183         whm = new WeakHashMap();
184         for (int i = 0; i < 100; i++)
185             whm.put(keyArray[i], valueArray[i]);
186         List keys = Arrays.asList(keyArray);
187         List values = Arrays.asList(valueArray);
188         Set entrySet = whm.entrySet();
189         assertTrue("Incorrect number of entries returned--wanted 100, got: " + entrySet.size(),
190             entrySet.size() == 100);
191         Iterator it = entrySet.iterator();
192         while (it.hasNext()) {
193             Map.Entry entry = (Map.Entry) it.next();
194             assertTrue("Invalid map entry returned--bad key", keys.contains(entry.getKey()));
195             assertTrue("Invalid map entry returned--bad key", values.contains(entry.getValue()));
196         }
197         keys = null;
198         values = null;
199         keyArray[50] = null;
200 
201         FinalizationTester.induceFinalization();
202         long startTime = System.currentTimeMillis();
203         // We use a busy wait loop here since we cannot know when the ReferenceQueue
204         // daemon will enqueue the cleared references on their internal reference
205         // queues. The current timeout is 5 seconds.
206         do {
207             try {
208                 Thread.sleep(100);
209             } catch (InterruptedException e) {
210             }
211         } while (entrySet.size() != 99 &&
212                  System.currentTimeMillis() - startTime < 5000);
213 
214         assertEquals("Incorrect number of keys returned after gc,", 99, entrySet.size());
215     }
216 
217     /**
218      * java.util.WeakHashMap#isEmpty()
219      */
test_isEmpty()220     public void test_isEmpty() {
221         // Test for method boolean java.util.WeakHashMap.isEmpty()
222         whm = new WeakHashMap();
223         assertTrue("New map should be empty", whm.isEmpty());
224         Object myObject = new Object();
225         whm.put(myObject, myObject);
226         assertTrue("Map should not be empty", !whm.isEmpty());
227         whm.remove(myObject);
228         assertTrue("Map with elements removed should be empty", whm.isEmpty());
229     }
230 
231     /**
232      * java.util.WeakHashMap#put(java.lang.Object, java.lang.Object)
233      */
test_putLjava_lang_ObjectLjava_lang_Object()234     public void test_putLjava_lang_ObjectLjava_lang_Object() {
235         // Test for method java.lang.Object
236         // java.util.WeakHashMap.put(java.lang.Object, java.lang.Object)
237         WeakHashMap map = new WeakHashMap();
238         map.put(null, "value"); // add null key
239         System.gc();
240         System.gc();
241         FinalizationTester.induceFinalization();
242         map.remove("nothing"); // Cause objects in queue to be removed
243         assertEquals("null key was removed", 1, map.size());
244     }
245 
246     /**
247      * java.util.WeakHashMap#putAll(java.util.Map)
248      */
test_putAllLjava_util_Map()249     public void test_putAllLjava_util_Map() {
250         Map mockMap = new MockMap();
251         WeakHashMap map = new WeakHashMap();
252         map.putAll(mockMap);
253         assertEquals("Size should be 0", 0, map.size());
254 
255         try {
256             map.putAll(null);
257             fail("NullPointerException exected");
258         } catch (NullPointerException e) {
259             //expected
260         }
261     }
262 
263     /**
264      * java.util.WeakHashMap#remove(java.lang.Object)
265      */
test_removeLjava_lang_Object()266     public void test_removeLjava_lang_Object() {
267         // Test for method java.lang.Object
268         // java.util.WeakHashMap.remove(java.lang.Object)
269         whm = new WeakHashMap();
270         for (int i = 0; i < 100; i++)
271             whm.put(keyArray[i], valueArray[i]);
272 
273         assertTrue("Remove returned incorrect value", whm.remove(keyArray[25]) == valueArray[25]);
274         assertNull("Remove returned incorrect value", whm.remove(keyArray[25]));
275         assertEquals("Size should be 99 after remove", 99, whm.size());
276     }
277 
278     /**
279      * java.util.WeakHashMap#size()
280      */
test_size()281     public void test_size() {
282         whm = new WeakHashMap();
283         assertEquals(0, whm.size());
284     }
285 
286     /**
287      * java.util.WeakHashMap#keySet()
288      */
test_keySet()289     public void test_keySet() {
290         // Test for method java.util.Set java.util.WeakHashMap.keySet()
291         whm = new WeakHashMap();
292         for (int i = 0; i < 100; i++)
293             whm.put(keyArray[i], valueArray[i]);
294 
295         List keys = Arrays.asList(keyArray);
296         List values = Arrays.asList(valueArray);
297 
298         Set keySet = whm.keySet();
299         assertEquals("Incorrect number of keys returned,", 100, keySet.size());
300         Iterator it = keySet.iterator();
301         while (it.hasNext()) {
302             Object key = it.next();
303             assertTrue("Invalid map entry returned--bad key", keys.contains(key));
304         }
305         keys = null;
306         values = null;
307         keyArray[50] = null;
308 
309         FinalizationTester.induceFinalization();
310         long startTime = System.currentTimeMillis();
311         // We use a busy wait loop here since we cannot know when the ReferenceQueue
312         // daemon will enqueue the cleared references on their internal reference
313         // queues.
314         // The timeout after which the reference should be cleared. This test used to
315         // be flaky when it was set to 5 seconds. Daemons.MAX_FINALIZE_NANOS is
316         // currently 10 seconds so that seems like the correct value.
317         // We allow an extra 500msec buffer to minimize races between finalizer,
318         // keySet.size() evaluation and time check.
319         long timeout = 10000 + 500;
320         do {
321             try {
322                 Thread.sleep(100);
323             } catch (InterruptedException e) {
324             }
325         } while (keySet.size() != 99 &&
326                  System.currentTimeMillis() - startTime < timeout);
327 
328         assertEquals("Incorrect number of keys returned after gc,", 99, keySet.size());
329     }
330 
331     /**
332      * Regression test for HARMONY-3883
333      *
334      * java.util.WeakHashMap#keySet()
335      */
test_keySet_hasNext()336     public void test_keySet_hasNext() {
337         WeakHashMap map = new WeakHashMap();
338         ConstantHashClass cl = new ConstantHashClass(2);
339         map.put(new ConstantHashClass(1), null);
340         map.put(cl, null);
341         map.put(new ConstantHashClass(3), null);
342         Iterator iter = map.keySet().iterator();
343         iter.next();
344         iter.next();
345         int count = 0;
346         do {
347             System.gc();
348             System.gc();
349             FinalizationTester.induceFinalization();
350             count++;
351         } while (count <= 5);
352         assertFalse("Wrong hasNext() value", iter.hasNext());
353     }
354 
355     static class ConstantHashClass {
356         private int id = 0;
357 
ConstantHashClass(int id)358         public ConstantHashClass(int id) {
359             this.id = id;
360         }
361 
hashCode()362         public int hashCode() {
363             return 0;
364         }
365 
toString()366         public String toString() {
367             return "ConstantHashClass[id=" + id + "]";
368         }
369     }
370 
371 
372     /**
373      * java.util.WeakHashMap#values()
374      */
test_values()375     public void test_values() {
376         // Test for method java.util.Set java.util.WeakHashMap.values()
377         whm = new WeakHashMap();
378         for (int i = 0; i < 100; i++)
379             whm.put(keyArray[i], valueArray[i]);
380 
381         List keys = Arrays.asList(keyArray);
382         List values = Arrays.asList(valueArray);
383 
384         Collection valuesCollection = whm.values();
385         assertEquals("Incorrect number of keys returned,", 100, valuesCollection.size());
386         Iterator it = valuesCollection.iterator();
387         while (it.hasNext()) {
388             Object value = it.next();
389             assertTrue("Invalid map entry returned--bad value", values.contains(value));
390         }
391         keys = null;
392         values = null;
393         keyArray[50] = null;
394 
395         FinalizationTester.induceFinalization();
396         long startTime = System.currentTimeMillis();
397         // We use a busy wait loop here since we cannot know when the ReferenceQueue
398         // daemon will enqueue the cleared references on their internal reference
399         // queues. The current timeout is 5 seconds.
400         do {
401             try {
402                 Thread.sleep(100);
403             } catch (InterruptedException e) {
404             }
405         } while (valuesCollection.size() != 99 &&
406                  System.currentTimeMillis() - startTime < 5000);
407 
408         assertEquals("Incorrect number of keys returned after gc,", 99, valuesCollection.size());
409     }
410 
test_forEach()411     public void test_forEach() throws Exception {
412         WeakHashMap map = new WeakHashMap();
413         for (int i = 0; i < 100; i++)
414             map.put(keyArray[i], valueArray[i]);
415 
416         WeakHashMap output = new WeakHashMap();
417         map.forEach((k, v) -> output.put(k,v));
418         assertEquals(map, output);
419 
420         HashSet setOutput = new HashSet();
421         map.keySet().forEach((k) -> setOutput.add(k));
422         assertEquals(map.keySet(), setOutput);
423 
424         setOutput.clear();
425         map.values().forEach((v) -> setOutput.add(v));
426         assertEquals(new HashSet(map.values()), setOutput);
427 
428         HashSet entrySetOutput = new HashSet();
429         map.entrySet().forEach((v) -> entrySetOutput.add(v));
430         assertEquals(map.entrySet(), entrySetOutput);
431     }
432 
test_forEach_NPE()433     public void test_forEach_NPE() throws Exception {
434         WeakHashMap map = new WeakHashMap();
435         try {
436             map.forEach(null);
437             fail();
438         } catch(NullPointerException expected) {}
439 
440         try {
441             map.keySet().forEach(null);
442             fail();
443         } catch(NullPointerException expected) {}
444 
445         try {
446             map.values().forEach(null);
447             fail();
448         } catch(NullPointerException expected) {}
449 
450         try {
451             map.entrySet().forEach(null);
452             fail();
453         } catch(NullPointerException expected) {}
454 
455     }
456 
test_forEach_CME()457     public void test_forEach_CME() throws Exception {
458         WeakHashMap map = new WeakHashMap();
459         for (int i = 0; i < 100; i++)
460             map.put(keyArray[i], valueArray[i]);
461         ArrayList<Object> processed = new ArrayList<>();
462         try {
463             map.forEach(new java.util.function.BiConsumer<Object, Object>() {
464                     @Override
465                     public void accept(Object k, Object v) {
466                         processed.add(k);
467                         map.put("foo", v);
468                     }
469                 });
470             fail();
471         } catch(ConcurrentModificationException expected) {}
472         // We should get a CME and DO NOT continue forEach evaluation
473         assertEquals(1, processed.size());
474 
475         processed.clear();
476         try {
477             map.keySet().forEach(new java.util.function.Consumer<Object>() {
478                     @Override
479                     public void accept(Object k) {
480                         processed.add(k);
481                         map.put("foo2", "boo");
482                     }
483                 });
484             fail();
485         } catch(ConcurrentModificationException expected) {}
486         // We should get a CME and DO NOT continue forEach evaluation
487         assertEquals(1, processed.size());
488 
489         processed.clear();
490         try {
491             map.values().forEach(new java.util.function.Consumer<Object>() {
492                     @Override
493                     public void accept(Object k) {
494                         processed.add(k);
495                         map.put("foo3", "boo");
496                     }
497                 });
498             fail();
499         } catch(ConcurrentModificationException expected) {}
500         // We should get a CME and DO NOT continue forEach evaluation
501         assertEquals(1, processed.size());
502 
503         processed.clear();
504         try {
505             map.entrySet().forEach(new java.util.function.Consumer<Map.Entry<Object, Object>>() {
506                     @Override
507                     public void accept(Map.Entry<Object, Object> k) {
508                         processed.add(k.getKey());
509                         map.put("foo4", "boo");
510                     }
511                 });
512             fail();
513         } catch(ConcurrentModificationException expected) {}
514         // We should get a CME and DO NOT continue forEach evaluation
515         assertEquals(1, processed.size());
516     }
517 
test_spliterator_keySet()518     public void test_spliterator_keySet() {
519         WeakHashMap<String, String> hashMap = new WeakHashMap<>();
520         hashMap.put("a", "1");
521         hashMap.put("b", "2");
522         hashMap.put("c", "3");
523         hashMap.put("d", "4");
524         hashMap.put("e", "5");
525         hashMap.put("f", "6");
526         hashMap.put("g", "7");
527         hashMap.put("h", "8");
528         hashMap.put("i", "9");
529         hashMap.put("j", "10");
530         hashMap.put("k", "11");
531         hashMap.put("l", "12");
532         hashMap.put("m", "13");
533         hashMap.put("n", "14");
534         hashMap.put("o", "15");
535         hashMap.put("p", "16");
536 
537         Set<String> keys = hashMap.keySet();
538         ArrayList<String> expectedKeys = new ArrayList<>(keys);
539 
540         SpliteratorTester.runBasicIterationTests_unordered(keys.spliterator(), expectedKeys,
541                 String::compareTo);
542         SpliteratorTester.runBasicSplitTests(keys, expectedKeys);
543         SpliteratorTester.testSpliteratorNPE(keys.spliterator());
544 
545         assertTrue(keys.spliterator().hasCharacteristics(Spliterator.DISTINCT));
546 
547         SpliteratorTester.runDistinctTests(keys);
548         SpliteratorTester.assertSupportsTrySplit(keys);
549     }
550 
test_spliterator_valueSet()551     public void test_spliterator_valueSet() {
552         WeakHashMap<String, String> hashMap = new WeakHashMap<>();
553         hashMap.put("a", "1");
554         hashMap.put("b", "2");
555         hashMap.put("c", "3");
556         hashMap.put("d", "4");
557         hashMap.put("e", "5");
558         hashMap.put("f", "6");
559         hashMap.put("g", "7");
560         hashMap.put("h", "8");
561         hashMap.put("i", "9");
562         hashMap.put("j", "10");
563         hashMap.put("k", "11");
564         hashMap.put("l", "12");
565         hashMap.put("m", "13");
566         hashMap.put("n", "14");
567         hashMap.put("o", "15");
568         hashMap.put("p", "16");
569 
570         Collection<String> values = hashMap.values();
571         ArrayList<String> expectedValues = new ArrayList<>(values);
572 
573         SpliteratorTester.runBasicIterationTests_unordered(
574                 values.spliterator(), expectedValues, String::compareTo);
575         SpliteratorTester.runBasicSplitTests(values, expectedValues);
576         SpliteratorTester.testSpliteratorNPE(values.spliterator());
577     }
578 
test_spliterator_entrySet()579     public void test_spliterator_entrySet() {
580         WeakHashMap<String, String> hashMap = new WeakHashMap<>();
581         hashMap.put("a", "1");
582         hashMap.put("b", "2");
583         hashMap.put("c", "3");
584         hashMap.put("d", "4");
585         hashMap.put("e", "5");
586         hashMap.put("f", "6");
587         hashMap.put("g", "7");
588         hashMap.put("h", "8");
589         hashMap.put("i", "9");
590         hashMap.put("j", "10");
591         hashMap.put("k", "11");
592         hashMap.put("l", "12");
593         hashMap.put("m", "13");
594         hashMap.put("n", "14");
595         hashMap.put("o", "15");
596         hashMap.put("p", "16");
597 
598         Set<Map.Entry<String, String>> values = hashMap.entrySet();
599         ArrayList<Map.Entry<String, String>> expectedValues = new ArrayList<>(values);
600 
601         Comparator<Map.Entry<String, String>> comparator =
602                 (a, b) -> (a.getKey().compareTo(b.getKey()));
603 
604         SpliteratorTester.runBasicIterationTests_unordered(values.spliterator(), expectedValues,
605                 (a, b) -> (a.getKey().compareTo(b.getKey())));
606         SpliteratorTester.runBasicSplitTests(values, expectedValues, comparator);
607         SpliteratorTester.testSpliteratorNPE(values.spliterator());
608 
609         assertTrue(values.spliterator().hasCharacteristics(Spliterator.DISTINCT));
610 
611         SpliteratorTester.runDistinctTests(values);
612         SpliteratorTester.assertSupportsTrySplit(values);
613     }
614 
615     /**
616      * Sets up the fixture, for example, open a network connection. This method
617      * is called before a test is executed.
618      */
setUp()619     protected void setUp() {
620         for (int i = 0; i < 100; i++) {
621             keyArray[i] = new Object();
622             valueArray[i] = new Object();
623         }
624 
625     }
626 
627     /**
628      * Tears down the fixture, for example, close a network connection. This
629      * method is called after a test is executed.
630      */
tearDown()631     protected void tearDown() {
632     }
633 }
634