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