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