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 21 import junit.framework.TestCase; 22 23 import java.lang.ref.ReferenceQueue; 24 import java.lang.ref.WeakReference; 25 import java.net.URL; 26 import java.net.URLClassLoader; 27 28 /** 29 * Unit test for {@link FinalizableReferenceQueue}. 30 * 31 * @author Bob Lee 32 */ 33 public class FinalizableReferenceQueueTest extends TestCase { 34 35 private FinalizableReferenceQueue frq; 36 37 @Override tearDown()38 protected void tearDown() throws Exception { 39 frq = null; 40 } 41 testFinalizeReferentCalled()42 public void testFinalizeReferentCalled() { 43 MockReference reference = new MockReference( 44 frq = new FinalizableReferenceQueue()); 45 // wait up to 5s 46 for (int i = 0; i < 500; i++) { 47 if (reference.finalizeReferentCalled) { 48 return; 49 } 50 try { 51 System.gc(); 52 Thread.sleep(10); 53 } catch (InterruptedException e) { /* ignore */ } 54 } 55 fail(); 56 } 57 58 static class MockReference extends FinalizableWeakReference<Object> { 59 60 volatile boolean finalizeReferentCalled; 61 MockReference(FinalizableReferenceQueue frq)62 MockReference(FinalizableReferenceQueue frq) { 63 super(new Object(), frq); 64 } 65 66 @Override finalizeReferent()67 public void finalizeReferent() { 68 finalizeReferentCalled = true; 69 } 70 } 71 72 /** 73 * Keeps a weak reference to the underlying reference queue. When this 74 * reference is cleared, we know that the background thread has stopped 75 * and released its strong reference. 76 */ 77 private WeakReference<ReferenceQueue<Object>> queueReference; 78 testThatFinalizerStops()79 public void testThatFinalizerStops() { 80 weaklyReferenceQueue(); 81 82 // wait up to 5s 83 for (int i = 0; i < 500; i++) { 84 if (queueReference.get() == null) { 85 return; 86 } 87 try { 88 System.gc(); 89 Thread.sleep(10); 90 } catch (InterruptedException e) { /* ignore */ } 91 } 92 fail(); 93 } 94 95 /** 96 * If we don't keep a strong reference to the reference object, it won't 97 * be enqueued. 98 */ 99 FinalizableWeakReference<Object> reference; 100 101 /** 102 * Create the FRQ in a method that goes out of scope so that we're sure 103 * it will be reclaimed. 104 */ weaklyReferenceQueue()105 private void weaklyReferenceQueue() { 106 frq = new FinalizableReferenceQueue(); 107 queueReference = new WeakReference<ReferenceQueue<Object>>(frq.queue); 108 109 /* 110 * Queue and clear a reference for good measure. We test later on that 111 * the finalizer thread stopped, but we should test that it actually 112 * started first. 113 */ 114 reference = new FinalizableWeakReference<Object>(new Object(), frq) { 115 @Override 116 public void finalizeReferent() { 117 reference = null; 118 frq = null; 119 } 120 }; 121 } 122 testDecoupledLoader()123 public void testDecoupledLoader() { 124 FinalizableReferenceQueue.DecoupledLoader decoupledLoader = 125 new FinalizableReferenceQueue.DecoupledLoader() { 126 @Override 127 URLClassLoader newLoader(URL base) { 128 return new DecoupledClassLoader(new URL[] { base }); 129 } 130 }; 131 132 Class<?> finalizerCopy = decoupledLoader.loadFinalizer(); 133 134 assertNotNull(finalizerCopy); 135 assertNotSame(Finalizer.class, finalizerCopy); 136 137 assertNotNull(FinalizableReferenceQueue.getStartFinalizer(finalizerCopy)); 138 } 139 140 static class DecoupledClassLoader extends URLClassLoader { 141 DecoupledClassLoader(URL[] urls)142 public DecoupledClassLoader(URL[] urls) { 143 super(urls); 144 } 145 146 @Override loadClass(String name, boolean resolve)147 protected synchronized Class<?> loadClass(String name, boolean resolve) 148 throws ClassNotFoundException { 149 // Force Finalizer to load from this class loader, not its parent. 150 if (name.equals(Finalizer.class.getName())) { 151 Class<?> clazz = findClass(name); 152 if (resolve) { 153 resolveClass(clazz); 154 } 155 return clazz; 156 } 157 158 return super.loadClass(name, resolve); 159 } 160 } 161 testGetFinalizerUrl()162 public void testGetFinalizerUrl() { 163 assertNotNull(getClass().getResource("internal/Finalizer.class")); 164 } 165 } 166