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