1// Copyright 2011 Google Inc. All Rights Reserved.
2
3package com.google.common.testing;
4
5import com.google.common.testing.GcFinalization.FinalizationPredicate;
6import com.google.common.util.concurrent.SettableFuture;
7
8import junit.framework.TestCase;
9
10import java.lang.ref.WeakReference;
11import java.util.WeakHashMap;
12import java.util.concurrent.CountDownLatch;
13import java.util.concurrent.atomic.AtomicBoolean;
14
15/**
16 * Tests for {@link GcFinalization}.
17 *
18 * @author martinrb@google.com (Martin Buchholz)
19 * @author schmoe@google.com (mike nonemacher)
20 */
21
22public class GcFinalizationTest extends TestCase {
23
24  //----------------------------------------------------------------
25  // Ordinary tests of successful method execution
26  //----------------------------------------------------------------
27
28  public void testAwait_CountDownLatch() {
29    final CountDownLatch latch = new CountDownLatch(1);
30    Object x = new Object() {
31      protected void finalize() { latch.countDown(); }
32    };
33    x = null;  // Hint to the JIT that x is unreachable
34    GcFinalization.await(latch);
35    assertEquals(0, latch.getCount());
36  }
37
38  public void testAwaitDone_Future() {
39    final SettableFuture<Void> future = SettableFuture.create();
40    Object x = new Object() {
41      protected void finalize() { future.set(null); }
42    };
43    x = null;  // Hint to the JIT that x is unreachable
44    GcFinalization.awaitDone(future);
45    assertTrue(future.isDone());
46    assertFalse(future.isCancelled());
47  }
48
49  public void testAwaitDone_Future_Cancel() {
50    final SettableFuture<Void> future = SettableFuture.create();
51    Object x = new Object() {
52      protected void finalize() { future.cancel(false); }
53    };
54    x = null;  // Hint to the JIT that x is unreachable
55    GcFinalization.awaitDone(future);
56    assertTrue(future.isDone());
57    assertTrue(future.isCancelled());
58  }
59
60  public void testAwaitClear() {
61    final WeakReference<Object> ref = new WeakReference<Object>(new Object());
62    GcFinalization.awaitClear(ref);
63    assertNull(ref.get());
64  }
65
66  public void testAwaitDone_FinalizationPredicate() {
67    final WeakHashMap<Object, Object> map = new WeakHashMap<Object, Object>();
68    map.put(new Object(), Boolean.TRUE);
69    GcFinalization.awaitDone(new FinalizationPredicate() {
70      public boolean isDone() {
71        return map.isEmpty();
72      }
73    });
74    assertTrue(map.isEmpty());
75  }
76
77  //----------------------------------------------------------------
78  // Test that interrupts result in RuntimeException, not InterruptedException.
79  // Trickier than it looks, because runFinalization swallows interrupts.
80  //----------------------------------------------------------------
81
82  class Interruptenator extends Thread {
83    final AtomicBoolean shutdown;
84    Interruptenator(final Thread interruptee) {
85      this(interruptee, new AtomicBoolean(false));
86    }
87    Interruptenator(final Thread interruptee,
88                    final AtomicBoolean shutdown) {
89      super(new Runnable() {
90          public void run() {
91            while (!shutdown.get()) {
92              interruptee.interrupt();
93              Thread.yield();
94            }}});
95      this.shutdown = shutdown;
96      start();
97    }
98    void shutdown() {
99      shutdown.set(true);
100      while (this.isAlive()) {
101        Thread.yield();
102      }
103    }
104  }
105
106  void assertWrapsInterruptedException(RuntimeException e) {
107    assertTrue(e.getMessage().contains("Unexpected interrupt"));
108    assertTrue(e.getCause() instanceof InterruptedException);
109  }
110
111  public void testAwait_CountDownLatch_Interrupted() {
112    Interruptenator interruptenator = new Interruptenator(Thread.currentThread());
113    try {
114      final CountDownLatch latch = new CountDownLatch(1);
115      try {
116        GcFinalization.await(latch);
117        fail("should throw");
118      } catch (RuntimeException expected) {
119        assertWrapsInterruptedException(expected);
120      }
121    } finally {
122      interruptenator.shutdown();
123      Thread.interrupted();
124    }
125  }
126
127  public void testAwaitDone_Future_Interrupted_Interrupted() {
128    Interruptenator interruptenator = new Interruptenator(Thread.currentThread());
129    try {
130      final SettableFuture<Void> future = SettableFuture.create();
131      try {
132        GcFinalization.awaitDone(future);
133        fail("should throw");
134      } catch (RuntimeException expected) {
135        assertWrapsInterruptedException(expected);
136      }
137    } finally {
138      interruptenator.shutdown();
139      Thread.interrupted();
140    }
141  }
142
143  public void testAwaitClear_Interrupted() {
144    Interruptenator interruptenator = new Interruptenator(Thread.currentThread());
145    try {
146      final WeakReference<Object> ref = new WeakReference<Object>(Boolean.TRUE);
147      try {
148        GcFinalization.awaitClear(ref);
149        fail("should throw");
150      } catch (RuntimeException expected) {
151        assertWrapsInterruptedException(expected);
152      }
153    } finally {
154      interruptenator.shutdown();
155      Thread.interrupted();
156    }
157  }
158
159  public void testAwaitDone_FinalizationPredicate_Interrupted() {
160    Interruptenator interruptenator = new Interruptenator(Thread.currentThread());
161    try {
162      try {
163        GcFinalization.awaitDone(new FinalizationPredicate() {
164            public boolean isDone() {
165              return false;
166            }
167          });
168        fail("should throw");
169      } catch (RuntimeException expected) {
170        assertWrapsInterruptedException(expected);
171      }
172    } finally {
173      interruptenator.shutdown();
174      Thread.interrupted();
175    }
176  }
177
178}
179