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