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