1 /* 2 * Copyright (C) 2011 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.testing; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import com.google.common.testing.GcFinalization.FinalizationPredicate; 22 import com.google.common.util.concurrent.SettableFuture; 23 import java.lang.ref.WeakReference; 24 import java.util.WeakHashMap; 25 import java.util.concurrent.CountDownLatch; 26 import java.util.concurrent.atomic.AtomicBoolean; 27 import junit.framework.TestCase; 28 29 /** 30 * Tests for {@link GcFinalization}. 31 * 32 * @author Martin Buchholz 33 * @author mike nonemacher 34 */ 35 36 public class GcFinalizationTest extends TestCase { 37 38 // ---------------------------------------------------------------- 39 // Ordinary tests of successful method execution 40 // ---------------------------------------------------------------- 41 testAwait_CountDownLatch()42 public void testAwait_CountDownLatch() { 43 final CountDownLatch latch = new CountDownLatch(1); 44 Object x = 45 new Object() { 46 @Override 47 protected void finalize() { 48 latch.countDown(); 49 } 50 }; 51 x = null; // Hint to the JIT that x is unreachable 52 GcFinalization.await(latch); 53 assertEquals(0, latch.getCount()); 54 } 55 testAwaitDone_Future()56 public void testAwaitDone_Future() { 57 final SettableFuture<Void> future = SettableFuture.create(); 58 Object x = 59 new Object() { 60 @Override 61 protected void finalize() { 62 future.set(null); 63 } 64 }; 65 x = null; // Hint to the JIT that x is unreachable 66 GcFinalization.awaitDone(future); 67 assertTrue(future.isDone()); 68 assertFalse(future.isCancelled()); 69 } 70 testAwaitDone_Future_Cancel()71 public void testAwaitDone_Future_Cancel() { 72 final SettableFuture<Void> future = SettableFuture.create(); 73 Object x = 74 new Object() { 75 @Override 76 protected void finalize() { 77 future.cancel(false); 78 } 79 }; 80 x = null; // Hint to the JIT that x is unreachable 81 GcFinalization.awaitDone(future); 82 assertTrue(future.isDone()); 83 assertTrue(future.isCancelled()); 84 } 85 testAwaitClear()86 public void testAwaitClear() { 87 final WeakReference<Object> ref = new WeakReference<>(new Object()); 88 GcFinalization.awaitClear(ref); 89 assertNull(ref.get()); 90 } 91 testAwaitDone_FinalizationPredicate()92 public void testAwaitDone_FinalizationPredicate() { 93 final WeakHashMap<Object, Object> map = new WeakHashMap<>(); 94 map.put(new Object(), Boolean.TRUE); 95 GcFinalization.awaitDone( 96 new FinalizationPredicate() { 97 public boolean isDone() { 98 return map.isEmpty(); 99 } 100 }); 101 assertTrue(map.isEmpty()); 102 } 103 104 // ---------------------------------------------------------------- 105 // Test that interrupts result in RuntimeException, not InterruptedException. 106 // Trickier than it looks, because runFinalization swallows interrupts. 107 // ---------------------------------------------------------------- 108 109 class Interruptenator extends Thread { 110 final AtomicBoolean shutdown; 111 Interruptenator(final Thread interruptee)112 Interruptenator(final Thread interruptee) { 113 this(interruptee, new AtomicBoolean(false)); 114 } 115 Interruptenator(final Thread interruptee, final AtomicBoolean shutdown)116 Interruptenator(final Thread interruptee, final AtomicBoolean shutdown) { 117 super( 118 new Runnable() { 119 public void run() { 120 while (!shutdown.get()) { 121 interruptee.interrupt(); 122 Thread.yield(); 123 } 124 } 125 }); 126 this.shutdown = shutdown; 127 start(); 128 } 129 shutdown()130 void shutdown() { 131 shutdown.set(true); 132 while (this.isAlive()) { 133 Thread.yield(); 134 } 135 } 136 } 137 assertWrapsInterruptedException(RuntimeException e)138 void assertWrapsInterruptedException(RuntimeException e) { 139 assertThat(e).hasMessageThat().contains("Unexpected interrupt"); 140 assertThat(e).hasCauseThat().isInstanceOf(InterruptedException.class); 141 } 142 testAwait_CountDownLatch_Interrupted()143 public void testAwait_CountDownLatch_Interrupted() { 144 Interruptenator interruptenator = new Interruptenator(Thread.currentThread()); 145 try { 146 final CountDownLatch latch = new CountDownLatch(1); 147 try { 148 GcFinalization.await(latch); 149 fail("should throw"); 150 } catch (RuntimeException expected) { 151 assertWrapsInterruptedException(expected); 152 } 153 } finally { 154 interruptenator.shutdown(); 155 Thread.interrupted(); 156 } 157 } 158 testAwaitDone_Future_Interrupted_Interrupted()159 public void testAwaitDone_Future_Interrupted_Interrupted() { 160 Interruptenator interruptenator = new Interruptenator(Thread.currentThread()); 161 try { 162 final SettableFuture<Void> future = SettableFuture.create(); 163 try { 164 GcFinalization.awaitDone(future); 165 fail("should throw"); 166 } catch (RuntimeException expected) { 167 assertWrapsInterruptedException(expected); 168 } 169 } finally { 170 interruptenator.shutdown(); 171 Thread.interrupted(); 172 } 173 } 174 testAwaitClear_Interrupted()175 public void testAwaitClear_Interrupted() { 176 Interruptenator interruptenator = new Interruptenator(Thread.currentThread()); 177 try { 178 final WeakReference<Object> ref = new WeakReference<Object>(Boolean.TRUE); 179 try { 180 GcFinalization.awaitClear(ref); 181 fail("should throw"); 182 } catch (RuntimeException expected) { 183 assertWrapsInterruptedException(expected); 184 } 185 } finally { 186 interruptenator.shutdown(); 187 Thread.interrupted(); 188 } 189 } 190 testAwaitDone_FinalizationPredicate_Interrupted()191 public void testAwaitDone_FinalizationPredicate_Interrupted() { 192 Interruptenator interruptenator = new Interruptenator(Thread.currentThread()); 193 try { 194 try { 195 GcFinalization.awaitDone( 196 new FinalizationPredicate() { 197 public boolean isDone() { 198 return false; 199 } 200 }); 201 fail("should throw"); 202 } catch (RuntimeException expected) { 203 assertWrapsInterruptedException(expected); 204 } 205 } finally { 206 interruptenator.shutdown(); 207 Thread.interrupted(); 208 } 209 } 210 211 /** 212 * awaitFullGc() is not quite as reliable a way to ensure calling of a specific finalize method as 213 * the more direct await* methods, but should be reliable enough in practice to avoid flakiness of 214 * this test. (And if it isn't, we'd like to know about it first!) 215 */ testAwaitFullGc()216 public void testAwaitFullGc() { 217 final CountDownLatch finalizerRan = new CountDownLatch(1); 218 final WeakReference<Object> ref = 219 new WeakReference<Object>( 220 new Object() { 221 @Override 222 protected void finalize() { 223 finalizerRan.countDown(); 224 } 225 }); 226 227 // Don't copy this into your own test! 228 // Use e.g. awaitClear or await(CountDownLatch) instead. 229 GcFinalization.awaitFullGc(); 230 231 // If this test turns out to be flaky, add a second call to awaitFullGc() 232 // GcFinalization.awaitFullGc(); 233 234 assertEquals(0, finalizerRan.getCount()); 235 assertNull(ref.get()); 236 } 237 } 238