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