• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 import java.lang.ref.ReferenceQueue;
22 import java.lang.ref.WeakReference;
23 import java.net.URL;
24 import java.net.URLClassLoader;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import junit.framework.TestCase;
28 
29 /**
30  * Unit test for {@link FinalizableReferenceQueue}.
31  *
32  * @author Bob Lee
33  */
34 public class FinalizableReferenceQueueTest extends TestCase {
35 
36   private FinalizableReferenceQueue frq;
37 
38   @Override
tearDown()39   protected void tearDown() throws Exception {
40     frq = null;
41   }
42 
43 
testFinalizeReferentCalled()44   public void testFinalizeReferentCalled() {
45     final MockReference reference = new MockReference(frq = new FinalizableReferenceQueue());
46 
47     GcFinalization.awaitDone(
48         new GcFinalization.FinalizationPredicate() {
49           @Override
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 reference is cleared, we
72    * know that the background thread has stopped and released its strong reference.
73    */
74   private WeakReference<ReferenceQueue<Object>> queueReference;
75 
76 
testThatFinalizerStops()77   public void testThatFinalizerStops() {
78     weaklyReferenceQueue();
79     GcFinalization.awaitClear(queueReference);
80   }
81 
82   /** If we don't keep a strong reference to the reference object, it won't be enqueued. */
83   FinalizableWeakReference<Object> reference;
84 
85   /** Create the FRQ in a method that goes out of scope so that we're sure it will be reclaimed. */
weaklyReferenceQueue()86   private void weaklyReferenceQueue() {
87     frq = new FinalizableReferenceQueue();
88     queueReference = new WeakReference<>(frq.queue);
89 
90     /*
91      * Queue and clear a reference for good measure. We test later on that
92      * the finalizer thread stopped, but we should test that it actually
93      * started first.
94      */
95     reference =
96         new FinalizableWeakReference<Object>(new Object(), frq) {
97           @Override
98           public void finalizeReferent() {
99             reference = null;
100             frq = null;
101           }
102         };
103   }
104 
105   @AndroidIncompatible // no concept of separate ClassLoaders
testDecoupledLoader()106   public void testDecoupledLoader() {
107     FinalizableReferenceQueue.DecoupledLoader decoupledLoader =
108         new FinalizableReferenceQueue.DecoupledLoader() {
109           @Override
110           URLClassLoader newLoader(URL base) {
111             return new DecoupledClassLoader(new URL[] {base});
112           }
113         };
114 
115     Class<?> finalizerCopy = decoupledLoader.loadFinalizer();
116 
117     assertNotNull(finalizerCopy);
118     assertNotSame(Finalizer.class, finalizerCopy);
119 
120     assertNotNull(FinalizableReferenceQueue.getStartFinalizer(finalizerCopy));
121   }
122 
123   static class DecoupledClassLoader extends URLClassLoader {
124 
DecoupledClassLoader(URL[] urls)125     public DecoupledClassLoader(URL[] urls) {
126       super(urls);
127     }
128 
129     @Override
loadClass(String name, boolean resolve)130     protected synchronized Class<?> loadClass(String name, boolean resolve)
131         throws ClassNotFoundException {
132       // Force Finalizer to load from this class loader, not its parent.
133       if (name.equals(Finalizer.class.getName())) {
134         Class<?> clazz = findClass(name);
135         if (resolve) {
136           resolveClass(clazz);
137         }
138         return clazz;
139       }
140 
141       return super.loadClass(name, resolve);
142     }
143   }
144 
145   @AndroidIncompatible // TODO(cpovirk): How significant is this failure?
testGetFinalizerUrl()146   public void testGetFinalizerUrl() {
147     assertNotNull(getClass().getResource("internal/Finalizer.class"));
148   }
149 
testFinalizeClassHasNoNestedClasses()150   public void testFinalizeClassHasNoNestedClasses() throws Exception {
151     // Ensure that the Finalizer class has no nested classes.
152     // See https://code.google.com/p/guava-libraries/issues/detail?id=1505
153     assertEquals(Collections.emptyList(), Arrays.asList(Finalizer.class.getDeclaredClasses()));
154   }
155 }
156