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