ReferenceTest.java revision e978e33deb1f7fbf5f6e81ed9948b303b8493ed3
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 */
17package tests.api.java.lang.ref;
18
19import dalvik.annotation.TestTargets;
20import dalvik.annotation.TestLevel;
21import dalvik.annotation.TestTargetNew;
22import dalvik.annotation.TestTargetClass;
23
24import junit.framework.AssertionFailedError;
25
26import java.lang.ref.PhantomReference;
27import java.lang.ref.Reference;
28import java.lang.ref.ReferenceQueue;
29import java.lang.ref.SoftReference;
30import java.lang.ref.WeakReference;
31import java.util.Vector;
32
33@TestTargetClass(Reference.class)
34public class ReferenceTest extends junit.framework.TestCase {
35    Object tmpA, tmpB, tmpC, obj;
36
37    volatile Reference r;
38
39    /*
40     * For test_subclass().
41     */
42    static TestWeakReference twr;
43    static AssertionFailedError error;
44    static boolean testObjectFinalized;
45    static class TestWeakReference<T> extends WeakReference<T> {
46        public volatile boolean clearSeen = false;
47        public volatile boolean enqueueSeen = false;
48
49        public TestWeakReference(T referent) {
50            super(referent);
51        }
52
53        public TestWeakReference(T referent, ReferenceQueue<? super T> q) {
54            super(referent, q);
55        }
56
57        public void clear() {
58            clearSeen = true;
59            if (testObjectFinalized) {
60                error = new AssertionFailedError("Clear should happen " +
61                        "before finalization.");
62                throw error;
63            }
64            if (enqueueSeen) {
65                error = new AssertionFailedError("Clear should happen " +
66                        "before enqueue.");
67                throw error;
68            }
69            super.clear();
70        }
71
72        public boolean enqueue() {
73            enqueueSeen = true;
74            if (!clearSeen) {
75                error = new AssertionFailedError("Clear should happen " +
76                        "before enqueue.");
77                throw error;
78            }
79
80            /* Do this last;  it may notify the main test thread,
81             * and anything we'd do after it (e.g., setting clearSeen)
82             * wouldn't be seen.
83             */
84            return super.enqueue();
85        }
86    }
87
88    protected void doneSuite() {
89        tmpA = tmpB = obj = null;
90    }
91
92    /**
93     * @tests java.lang.ref.Reference#clear()
94     */
95    @TestTargetNew(
96        level = TestLevel.COMPLETE,
97        notes = "",
98        method = "clear",
99        args = {}
100    )
101    public void test_clear() {
102        tmpA = new Object();
103        tmpB = new Object();
104        tmpC = new Object();
105        SoftReference sr = new SoftReference(tmpA, new ReferenceQueue());
106        WeakReference wr = new WeakReference(tmpB, new ReferenceQueue());
107        PhantomReference pr = new PhantomReference(tmpC, new ReferenceQueue());
108        assertTrue("Start: Object not cleared.", (sr.get() != null)
109                && (wr.get() != null));
110        assertNull("Referent is not null.", pr.get());
111        sr.clear();
112        wr.clear();
113        pr.clear();
114        assertTrue("End: Object cleared.", (sr.get() == null)
115                && (wr.get() == null));
116        assertNull("Referent is not null.", pr.get());
117        // Must reference tmpA and tmpB so the jit does not optimize them away
118        assertTrue("should always pass", tmpA != sr.get() && tmpB != wr.get());
119    }
120
121    /**
122     * @tests java.lang.ref.Reference#enqueue()
123     */
124    @TestTargets({
125        @TestTargetNew(
126            level = TestLevel.COMPLETE,
127            notes = "",
128            method = "enqueue",
129            args = {}
130        ),
131        @TestTargetNew(
132            level = TestLevel.COMPLETE,
133            notes = "",
134            method = "isEnqueued",
135            args = {}
136        )
137    })
138    public void test_enqueue() {
139        ReferenceQueue rq = new ReferenceQueue();
140        obj = new Object();
141        Reference ref = new SoftReference(obj, rq);
142        assertTrue("Enqueue failed.", (!ref.isEnqueued())
143                && ((ref.enqueue()) && (ref.isEnqueued())));
144        assertTrue("Not properly enqueued.", rq.poll().get() == obj);
145        // This fails...
146        assertTrue("Should remain enqueued.", !ref.isEnqueued());
147        assertTrue("Can not enqueue twice.", (!ref.enqueue())
148                && (rq.poll() == null));
149
150        rq = new ReferenceQueue();
151        obj = new Object();
152
153        ref = new WeakReference(obj, rq);
154        assertTrue("Enqueue failed2.", (!ref.isEnqueued())
155                && ((ref.enqueue()) && (ref.isEnqueued())));
156        assertTrue("Not properly enqueued2.", rq.poll().get() == obj);
157        assertTrue("Should remain enqueued2.", !ref.isEnqueued()); // This
158        // fails.
159        assertTrue("Can not enqueue twice2.", (!ref.enqueue())
160                && (rq.poll() == null));
161
162        ref = new PhantomReference(obj, rq);
163        assertTrue("Enqueue failed3.", (!ref.isEnqueued())
164                && ((ref.enqueue()) && (ref.isEnqueued())));
165        assertNull("Not properly enqueued3.", rq.poll().get());
166        assertTrue("Should remain enqueued3.", !ref.isEnqueued()); // This
167        // fails.
168        assertTrue("Can not enqueue twice3.", (!ref.enqueue())
169                && (rq.poll() == null));
170    }
171
172    /**
173     * @tests java.lang.ref.Reference#enqueue()
174     */
175    @TestTargetNew(
176        level = TestLevel.PARTIAL_COMPLETE,
177        notes = "Verifies positive functionality for WeakReference.",
178        method = "get",
179        args = {}
180    )
181    public void test_get_WeakReference() {
182        // Test the general/overall functionality of Reference.
183
184        class TestObject {
185            public boolean finalized;
186
187            public TestObject() {
188                finalized = false;
189            }
190
191            protected void finalize() {
192                finalized = true;
193            }
194        }
195
196        final ReferenceQueue rq = new ReferenceQueue();
197
198        class TestThread extends Thread {
199
200            public void run() {
201                // Create the object in a separate thread to ensure it will be
202                // gc'ed
203                Object testObj = new TestObject();
204                r = new WeakReference(testObj, rq);
205                testObj = null;
206            }
207        }
208
209        Reference ref;
210
211        try {
212            TestThread t = new TestThread();
213            t.start();
214            t.join();
215            System.gc();
216            System.runFinalization();
217            ref = rq.remove();
218            assertNotNull("Object not garbage collected1.", ref);
219            assertTrue("Unexpected ref1", ref == r);
220            assertNull("Object could not be reclaimed1.", r.get());
221        } catch (InterruptedException e) {
222            fail("InterruptedException : " + e.getMessage());
223        }
224
225        try {
226            TestThread t = new TestThread();
227            t.start();
228            t.join();
229            System.gc();
230            System.runFinalization();
231            ref = rq.poll();
232            assertNotNull("Object not garbage collected.", ref);
233            assertTrue("Unexpected ref2", ref == r);
234            assertNull("Object could not be reclaimed.", ref.get());
235            // Reference wr so it does not get collected
236            assertNull("Object could not be reclaimed.", r.get());
237        } catch (Exception e) {
238            fail("Exception : " + e.getMessage());
239        }
240    }
241
242
243    /**
244     * Makes sure that overridden versions of clear() and enqueue()
245     * get called, and that clear/enqueue/finalize happen in the
246     * right order for WeakReferences.
247     *
248     * @tests java.lang.ref.Reference#clear()
249     * @tests java.lang.ref.Reference#enqueue()
250     * @tests java.lang.Object#finalize()
251     */
252    @TestTargets({
253        @TestTargetNew(
254            level = TestLevel.PARTIAL_COMPLETE,
255            notes = "Makes sure that overridden versions of clear() and enqueue()  " +
256                    "get called, and that clear/enqueue/finalize happen in the  " +
257                    "right order for WeakReferences.",
258            method = "clear",
259            args = {}
260        ),
261        @TestTargetNew(
262            level = TestLevel.PARTIAL_COMPLETE,
263            notes = "Makes sure that overridden versions of clear() and enqueue()  " +
264                    "get called, and that clear/enqueue/finalize happen in the  " +
265                    "right order for WeakReferences.",
266            method = "enqueue",
267            args = {}
268        )
269    })
270    public void test_subclass() {
271        error = null;
272        testObjectFinalized = false;
273        twr = null;
274
275        class TestObject {
276            public TestWeakReference testWeakReference = null;
277
278            public void setTestWeakReference(TestWeakReference twr) {
279                testWeakReference = twr;
280            }
281
282            protected void finalize() {
283                testObjectFinalized = true;
284                if (!testWeakReference.clearSeen) {
285                    error = new AssertionFailedError("Clear should happen " +
286                            "before finalize.");
287                    throw error;
288                }
289            }
290        }
291
292        final ReferenceQueue rq = new ReferenceQueue();
293
294        class TestThread extends Thread {
295            public void run() {
296                // Create the object in a separate thread to ensure it will be
297                // gc'ed
298                TestObject testObj = new TestObject();
299                twr = new TestWeakReference(testObj, rq);
300                testObj.setTestWeakReference(twr);
301                testObj = null;
302            }
303        }
304
305        Reference ref;
306
307        try {
308            Thread t = new TestThread();
309            t.start();
310            t.join();
311            System.gc();
312            System.runFinalization();
313            ref = rq.remove(5000L);    // Give up after five seconds.
314
315            assertNotNull("Object not garbage collected.", ref);
316            assertTrue("Unexpected reference.", ref == twr);
317            assertNull("Object could not be reclaimed.", twr.get());
318            //assertTrue("Overridden clear() should have been called.",
319            //       twr.clearSeen);
320            //assertTrue("Overridden enqueue() should have been called.",
321            //        twr.enqueueSeen);
322            assertTrue("finalize() should have been called.",
323                    testObjectFinalized);
324        } catch (InterruptedException e) {
325            fail("InterruptedException : " + e.getMessage());
326        }
327
328    }
329
330    /**
331     * @tests java.lang.ref.Reference#get()
332     */
333    @TestTargetNew(
334        level = TestLevel.PARTIAL_COMPLETE,
335        notes = "Doesn't check that get() can return null.",
336        method = "get",
337        args = {}
338    )
339    public void test_get() {
340
341        Vector<StringBuffer> vec = new Vector<StringBuffer>();
342        WeakReference ref = new WeakReference(vec, new ReferenceQueue());
343        assertTrue("Get succeeded.", ref.get() == vec);
344
345        Runtime rt =  Runtime.getRuntime();
346
347        long beforeTest = rt.freeMemory();
348        while(rt.freeMemory() < beforeTest * 2/3) {
349            vec.add(new StringBuffer(1000));
350        }
351        vec = null;
352
353        System.gc();
354        System.runFinalization();
355        assertNull("get() doesn't return null after gc for WeakReference",
356                    ref.get());
357
358        obj = new Object();
359        ref = new WeakReference(obj, new ReferenceQueue());
360        ref.clear();
361        assertNull("get() doesn't return null after clear for WeakReference",
362                ref.get());
363    }
364
365    /**
366     * @tests java.lang.ref.Reference#isEnqueued()
367     */
368    @TestTargetNew(
369        level = TestLevel.COMPLETE,
370        notes = "",
371        method = "isEnqueued",
372        args = {}
373    )
374    public void test_isEnqueued() {
375        ReferenceQueue rq = new ReferenceQueue();
376        obj = new Object();
377        Reference ref = new SoftReference(obj, rq);
378        assertTrue("Should start off not enqueued.", !ref.isEnqueued());
379        ref.enqueue();
380        assertTrue("Should now be enqueued.", ref.isEnqueued());
381        ref.enqueue();
382        assertTrue("Should still be enqueued.", ref.isEnqueued());
383        rq.poll();
384        // This fails ...
385        assertTrue("Should now be not enqueued.", !ref.isEnqueued());
386    }
387
388    /* Contrives a situation where the only reference to a string
389     * is a WeakReference from an object that is being finalized.
390     * Checks to make sure that the referent of the WeakReference
391     * is still pointing to a valid object.
392     */
393    @TestTargetNew(
394        level = TestLevel.PARTIAL_COMPLETE,
395        notes = "Contrives a situation where the only reference to a string  " +
396                "is a WeakReference from an object that is being finalized.  " +
397                "Checks to make sure that the referent of the WeakReference  " +
398                "is still pointing to a valid object.",
399        method = "get",
400        args = {}
401    )
402    public void test_finalizeReferenceInteraction() {
403        error = null;
404        testObjectFinalized = false;
405
406        class TestObject {
407            WeakReference<String> stringRef;
408
409            public TestObject(String referent) {
410                stringRef = new WeakReference<String>(referent);
411            }
412
413            protected void finalize() {
414                try {
415                    /* If a VM bug has caused the referent to get
416                     * freed without the reference getting cleared,
417                     * looking it up, assigning it to a local and
418                     * doing a GC should cause some sort of exception.
419                     */
420                    String s = stringRef.get();
421                    System.gc();
422                    testObjectFinalized = true;
423                } catch (Throwable t) {
424                    error = new AssertionFailedError("something threw '" + t +
425                            "' in finalize()");
426                }
427            }
428        }
429
430        class TestThread extends Thread {
431            public void run() {
432                // Create the object in a separate thread to ensure it will be
433                // gc'ed
434                TestObject testObj = new TestObject(new String("sup /b/"));
435            }
436        }
437
438        try {
439            Thread t = new TestThread();
440            t.start();
441            t.join();
442            System.gc();
443            System.runFinalization();
444            Thread.sleep(1000);
445            if (error != null) {
446                throw error;
447            }
448            assertTrue("finalize() should have been called.",
449                    testObjectFinalized);
450        } catch (InterruptedException e) {
451            fail("InterruptedException : " + e.getMessage());
452        }
453    }
454
455
456    protected void setUp() {
457    }
458
459    protected void tearDown() {
460    }
461}
462