FinalizableReferenceQueueTest.java revision 1d580d0f6ee4f21eb309ba7b509d2c6d671c4044
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
17package com.google.common.base;
18
19import com.google.common.base.internal.Finalizer;
20
21import junit.framework.TestCase;
22
23import java.lang.ref.ReferenceQueue;
24import java.lang.ref.WeakReference;
25import java.net.URL;
26import java.net.URLClassLoader;
27
28/**
29 * Unit test for {@link FinalizableReferenceQueue}.
30 *
31 * @author Bob Lee
32 */
33public class FinalizableReferenceQueueTest extends TestCase {
34
35  private FinalizableReferenceQueue frq;
36
37  @Override
38  protected void tearDown() throws Exception {
39    frq = null;
40  }
41
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
62    MockReference(FinalizableReferenceQueue frq) {
63      super(new Object(), frq);
64    }
65
66    @Override
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
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   */
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
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
142    public DecoupledClassLoader(URL[] urls) {
143      super(urls);
144    }
145
146    @Override
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
162  public void testGetFinalizerUrl() {
163    assertNotNull(getClass().getResource("internal/Finalizer.class"));
164  }
165}
166