1 /*
2  * Copyright (C) 2005 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.base;
18 
19 import com.google.common.base.internal.Finalizer;
20 import com.google.common.testing.GcFinalization;
21 
22 import junit.framework.TestCase;
23 
24 import java.lang.ref.ReferenceQueue;
25 import java.lang.ref.WeakReference;
26 import java.net.URL;
27 import java.net.URLClassLoader;
28 import java.util.Arrays;
29 import java.util.Collections;
30 
31 /**
32  * Unit test for {@link FinalizableReferenceQueue}.
33  *
34  * @author Bob Lee
35  */
36 public class FinalizableReferenceQueueTest extends TestCase {
37 
38   private FinalizableReferenceQueue frq;
39 
40   @Override
tearDown()41   protected void tearDown() throws Exception {
42     frq = null;
43   }
44 
testFinalizeReferentCalled()45   public void testFinalizeReferentCalled() {
46     final MockReference reference = new MockReference(
47         frq = new FinalizableReferenceQueue());
48 
49     GcFinalization.awaitDone(new GcFinalization.FinalizationPredicate() {
50         public boolean isDone() {
51           return reference.finalizeReferentCalled;
52         }
53       });
54   }
55 
56   static class MockReference extends FinalizableWeakReference<Object> {
57 
58     volatile boolean finalizeReferentCalled;
59 
MockReference(FinalizableReferenceQueue frq)60     MockReference(FinalizableReferenceQueue frq) {
61       super(new Object(), frq);
62     }
63 
64     @Override
finalizeReferent()65     public void finalizeReferent() {
66       finalizeReferentCalled = true;
67     }
68   }
69 
70   /**
71    * Keeps a weak reference to the underlying reference queue. When this
72    * reference is cleared, we know that the background thread has stopped
73    * and released its strong reference.
74    */
75   private WeakReference<ReferenceQueue<Object>> queueReference;
76 
testThatFinalizerStops()77   public void testThatFinalizerStops() {
78     weaklyReferenceQueue();
79     GcFinalization.awaitClear(queueReference);
80   }
81 
82   /**
83    * If we don't keep a strong reference to the reference object, it won't
84    * be enqueued.
85    */
86   FinalizableWeakReference<Object> reference;
87 
88   /**
89    * Create the FRQ in a method that goes out of scope so that we're sure
90    * it will be reclaimed.
91    */
weaklyReferenceQueue()92   private void weaklyReferenceQueue() {
93     frq = new FinalizableReferenceQueue();
94     queueReference = new WeakReference<ReferenceQueue<Object>>(frq.queue);
95 
96     /*
97      * Queue and clear a reference for good measure. We test later on that
98      * the finalizer thread stopped, but we should test that it actually
99      * started first.
100      */
101     reference = new FinalizableWeakReference<Object>(new Object(), frq) {
102       @Override
103       public void finalizeReferent() {
104         reference = null;
105         frq = null;
106       }
107     };
108   }
109 
testDecoupledLoader()110   public void testDecoupledLoader() {
111     FinalizableReferenceQueue.DecoupledLoader decoupledLoader =
112         new FinalizableReferenceQueue.DecoupledLoader() {
113           @Override
114           URLClassLoader newLoader(URL base) {
115             return new DecoupledClassLoader(new URL[] { base });
116           }
117         };
118 
119     Class<?> finalizerCopy = decoupledLoader.loadFinalizer();
120 
121     assertNotNull(finalizerCopy);
122     assertNotSame(Finalizer.class, finalizerCopy);
123 
124     assertNotNull(FinalizableReferenceQueue.getStartFinalizer(finalizerCopy));
125   }
126 
127   static class DecoupledClassLoader extends URLClassLoader {
128 
DecoupledClassLoader(URL[] urls)129     public DecoupledClassLoader(URL[] urls) {
130       super(urls);
131     }
132 
133     @Override
loadClass(String name, boolean resolve)134     protected synchronized Class<?> loadClass(String name, boolean resolve)
135         throws ClassNotFoundException {
136       // Force Finalizer to load from this class loader, not its parent.
137       if (name.equals(Finalizer.class.getName())) {
138         Class<?> clazz = findClass(name);
139         if (resolve) {
140           resolveClass(clazz);
141         }
142         return clazz;
143       }
144 
145       return super.loadClass(name, resolve);
146     }
147   }
148 
testGetFinalizerUrl()149   public void testGetFinalizerUrl() {
150     assertNotNull(getClass().getResource("internal/Finalizer.class"));
151   }
152 
testFinalizeClassHasNoNestedClases()153   public void testFinalizeClassHasNoNestedClases() throws Exception {
154     // Ensure that the Finalizer class has no nested classes.
155     // See https://code.google.com/p/guava-libraries/issues/detail?id=1505
156     assertEquals(Collections.emptyList(), Arrays.asList(Finalizer.class.getDeclaredClasses()));
157   }
158 }
159