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