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 org.apache.harmony.tests.java.lang.ref;
18
19import java.lang.ref.PhantomReference;
20import java.lang.ref.Reference;
21import java.lang.ref.ReferenceQueue;
22import java.lang.ref.SoftReference;
23import java.lang.ref.WeakReference;
24import junit.framework.AssertionFailedError;
25import libcore.java.lang.ref.FinalizationTester;
26
27public class ReferenceTest extends junit.framework.TestCase {
28    Object tmpA, tmpB, tmpC, obj;
29
30    volatile Reference r;
31
32    /*
33     * For test_subclass().
34     */
35    static TestWeakReference twr;
36    static AssertionFailedError error;
37    static boolean testObjectFinalized;
38
39    static class TestWeakReference<T> extends WeakReference<T> {
40        public volatile boolean clearSeen = false;
41        public volatile boolean enqueueSeen = false;
42
43        public TestWeakReference(T referent, ReferenceQueue<? super T> q) {
44            super(referent, q);
45        }
46
47        public void clear() {
48            clearSeen = true;
49            super.clear();
50        }
51
52        public boolean enqueue() {
53            enqueueSeen = true;
54            return super.enqueue();
55        }
56    }
57
58    protected void doneSuite() {
59        tmpA = tmpB = obj = null;
60    }
61
62    /**
63     * java.lang.ref.Reference#clear()
64     */
65    public void test_clear() {
66        tmpA = new Object();
67        tmpB = new Object();
68        tmpC = new Object();
69        SoftReference sr = new SoftReference(tmpA, new ReferenceQueue());
70        WeakReference wr = new WeakReference(tmpB, new ReferenceQueue());
71        PhantomReference pr = new PhantomReference(tmpC, new ReferenceQueue());
72        assertTrue("Start: Object not cleared.", (sr.get() != null)
73                && (wr.get() != null));
74        assertNull("Referent is not null.", pr.get());
75        sr.clear();
76        wr.clear();
77        pr.clear();
78        assertTrue("End: Object cleared.", (sr.get() == null)
79                && (wr.get() == null));
80        assertNull("Referent is not null.", pr.get());
81        // Must reference tmpA and tmpB so the jit does not optimize them away
82        assertTrue("should always pass", tmpA != sr.get() && tmpB != wr.get());
83    }
84
85    /**
86     * java.lang.ref.Reference#enqueue()
87     */
88    public void test_enqueue() {
89        ReferenceQueue rq = new ReferenceQueue();
90        obj = new Object();
91        Reference ref = new SoftReference(obj, rq);
92        assertTrue("Enqueue failed.", (!ref.isEnqueued())
93                && ((ref.enqueue()) && (ref.isEnqueued())));
94        assertTrue("Not properly enqueued.", rq.poll().get() == obj);
95        // This fails...
96        assertTrue("Should remain enqueued.", !ref.isEnqueued());
97        assertTrue("Can not enqueue twice.", (!ref.enqueue())
98                && (rq.poll() == null));
99
100        rq = new ReferenceQueue();
101        obj = new Object();
102
103        ref = new WeakReference(obj, rq);
104        assertTrue("Enqueue failed2.", (!ref.isEnqueued())
105                && ((ref.enqueue()) && (ref.isEnqueued())));
106        assertTrue("Not properly enqueued2.", rq.poll().get() == obj);
107        assertTrue("Should remain enqueued2.", !ref.isEnqueued()); // This
108        // fails.
109        assertTrue("Can not enqueue twice2.", (!ref.enqueue())
110                && (rq.poll() == null));
111
112        ref = new PhantomReference(obj, rq);
113        assertTrue("Enqueue failed3.", (!ref.isEnqueued())
114                && ((ref.enqueue()) && (ref.isEnqueued())));
115        assertNull("Not properly enqueued3.", rq.poll().get());
116        assertTrue("Should remain enqueued3.", !ref.isEnqueued()); // This
117        // fails.
118        assertTrue("Can not enqueue twice3.", (!ref.enqueue())
119                && (rq.poll() == null));
120    }
121
122    public void test_get_WeakReference() throws Exception {
123        // Test the general/overall functionality of Reference.
124        ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
125
126        r = newWeakReference(queue);
127        FinalizationTester.induceFinalization();
128        Reference ref = queue.remove();
129        assertNotNull("Object not enqueued.", ref);
130        assertSame("Unexpected ref1", ref, r);
131        assertNull("Object could not be reclaimed1.", r.get());
132
133        r = newWeakReference(queue);
134        FinalizationTester.induceFinalization();
135
136        // wait for the reference queue thread to enqueue the newly-finalized object
137        Thread.yield();
138        Thread.sleep(200);
139
140        ref = queue.poll();
141        assertNotNull("Object not enqueued.", ref);
142        assertSame("Unexpected ref2", ref, r);
143        assertNull("Object could not be reclaimed.", ref.get());
144        assertNull("Object could not be reclaimed.", r.get());
145    }
146
147    /**
148     * Makes sure that overridden versions of clear() and enqueue()
149     * get called, and that clear/enqueue/finalize happen in the
150     * right order for WeakReferences.
151     *
152     * java.lang.ref.Reference#clear()
153     * java.lang.ref.Reference#enqueue()
154     * java.lang.Object#finalize()
155     */
156    public void test_subclass() {
157        error = null;
158        testObjectFinalized = false;
159        twr = null;
160
161        class TestObject {
162            public TestWeakReference testWeakReference = null;
163
164            public void setTestWeakReference(TestWeakReference twr) {
165                testWeakReference = twr;
166            }
167
168            protected void finalize() {
169                testObjectFinalized = true;
170            }
171        }
172
173        final ReferenceQueue rq = new ReferenceQueue();
174
175        class TestThread extends Thread {
176            public void run() {
177                // Create the object in a separate thread to ensure it will be
178                // gc'ed
179                TestObject testObj = new TestObject();
180                twr = new TestWeakReference(testObj, rq);
181                testObj.setTestWeakReference(twr);
182                testObj = null;
183            }
184        }
185
186        Reference ref;
187
188        try {
189            Thread t = new TestThread();
190            t.start();
191            t.join();
192            FinalizationTester.induceFinalization();
193            ref = rq.remove(5000L);    // Give up after five seconds.
194
195            assertNotNull("Object not garbage collected.", ref);
196            assertTrue("Unexpected reference.", ref == twr);
197            assertNull("Object could not be reclaimed.", twr.get());
198
199            // enqueue() and clear() will not be called by the garbage collector. The GC
200            // will perform the equivalent operations directly.
201            assertFalse(twr.clearSeen);
202            assertFalse(twr.enqueueSeen);
203
204            assertTrue(testObjectFinalized);
205        } catch (InterruptedException e) {
206            fail("InterruptedException : " + e.getMessage());
207        }
208
209    }
210
211    /**
212     * java.lang.ref.Reference#get()
213     */
214    public void test_get() {
215        WeakReference ref = newWeakReference(null);
216
217        FinalizationTester.induceFinalization();
218        assertNull("get() doesn't return null after gc for WeakReference", ref.get());
219
220        obj = new Object();
221        ref = new WeakReference<Object>(obj, new ReferenceQueue<Object>());
222        ref.clear();
223        assertNull("get() doesn't return null after clear for WeakReference", ref.get());
224    }
225
226    /**
227     * Helper method to prevent live-precise bugs from interfering with analysis
228     * of what is reachable. Do not inline this method; otherwise tests may fail
229     * on VMs that are not live-precise. http://b/4191345
230     */
231    private WeakReference<Object> newWeakReference(ReferenceQueue<Object> queue) {
232        Object o = new Object();
233        WeakReference<Object> ref = new WeakReference<Object>(o, queue);
234        assertSame(o, ref.get());
235        return ref;
236    }
237
238    /**
239     * java.lang.ref.Reference#isEnqueued()
240     */
241    public void test_isEnqueued() {
242        ReferenceQueue rq = new ReferenceQueue();
243        obj = new Object();
244        Reference ref = new SoftReference(obj, rq);
245        assertTrue("Should start off not enqueued.", !ref.isEnqueued());
246        ref.enqueue();
247        assertTrue("Should now be enqueued.", ref.isEnqueued());
248        ref.enqueue();
249        assertTrue("Should still be enqueued.", ref.isEnqueued());
250        rq.poll();
251        // This fails ...
252        assertTrue("Should now be not enqueued.", !ref.isEnqueued());
253    }
254
255    /* Contrives a situation where the only reference to a string
256     * is a WeakReference from an object that is being finalized.
257     * Checks to make sure that the referent of the WeakReference
258     * is still pointing to a valid object.
259     */
260    public void test_finalizeReferenceInteraction() {
261        error = null;
262        testObjectFinalized = false;
263
264        class TestObject {
265            WeakReference<String> stringRef;
266
267            public TestObject(String referent) {
268                stringRef = new WeakReference<String>(referent);
269            }
270
271            protected void finalize() {
272                try {
273                    /* If a VM bug has caused the referent to get
274                     * freed without the reference getting cleared,
275                     * looking it up, assigning it to a local and
276                     * doing a GC should cause some sort of exception.
277                     */
278                    String s = stringRef.get();
279                    System.gc();
280                    testObjectFinalized = true;
281                } catch (Throwable t) {
282                    error = new AssertionFailedError("something threw '" + t +
283                            "' in finalize()");
284                }
285            }
286        }
287
288        class TestThread extends Thread {
289            public void run() {
290                // Create the object in a separate thread to ensure it will be
291                // gc'ed
292                TestObject testObj = new TestObject(new String("sup /b/"));
293            }
294        }
295
296        try {
297            Thread t = new TestThread();
298            t.start();
299            t.join();
300            FinalizationTester.induceFinalization();
301            Thread.sleep(1000);
302            if (error != null) {
303                throw error;
304            }
305            assertTrue("finalize() should have been called.",
306                    testObjectFinalized);
307        } catch (InterruptedException e) {
308            fail("InterruptedException : " + e.getMessage());
309        }
310    }
311
312
313    protected void setUp() {
314    }
315
316    protected void tearDown() {
317    }
318}
319