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