Home | History | Annotate | Download | only in concurrent
      1 /*
      2  * Copyright (C) 2010 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.util.concurrent;
     18 
     19 import com.google.common.testing.NullPointerTester;
     20 import com.google.common.testing.TearDownStack;
     21 
     22 import junit.framework.TestCase;
     23 
     24 import java.util.Random;
     25 import java.util.concurrent.TimeUnit;
     26 
     27 /**
     28  * Tests for {@link Monitor}, either interruptible or uninterruptible.
     29  *
     30  * @author Justin T. Sampson
     31  */
     32 
     33 public abstract class MonitorTestCase extends TestCase {
     34 
     35   public class TestGuard extends Monitor.Guard {
     36     private volatile boolean satisfied;
     37 
     38     public TestGuard(boolean satisfied) {
     39       super(MonitorTestCase.this.monitor);
     40       this.satisfied = satisfied;
     41     }
     42 
     43     @Override public boolean isSatisfied() {
     44       return this.satisfied;
     45     }
     46 
     47     public void setSatisfied(boolean satisfied) {
     48       this.satisfied = satisfied;
     49     }
     50   }
     51 
     52   private final boolean interruptible;
     53   private Monitor monitor;
     54   private final TearDownStack tearDownStack = new TearDownStack(true);
     55   private TestThread<Monitor> thread1;
     56   private TestThread<Monitor> thread2;
     57 
     58   protected MonitorTestCase(boolean interruptible) {
     59     this.interruptible = interruptible;
     60   }
     61 
     62   @Override protected final void setUp() throws Exception {
     63     boolean fair = new Random().nextBoolean();
     64     monitor = new Monitor(fair);
     65     tearDownStack.addTearDown(thread1 = new TestThread<Monitor>(monitor, "TestThread #1"));
     66     tearDownStack.addTearDown(thread2 = new TestThread<Monitor>(monitor, "TestThread #2"));
     67   }
     68 
     69   @Override protected final void tearDown() {
     70     tearDownStack.runTearDown();
     71   }
     72 
     73   private String enter() {
     74     return interruptible ? "enterInterruptibly" : "enter";
     75   }
     76 
     77   private String tryEnter() {
     78     return "tryEnter";
     79   }
     80 
     81   private String enterIf() {
     82     return interruptible ? "enterIfInterruptibly" : "enterIf";
     83   }
     84 
     85   private String tryEnterIf() {
     86     return "tryEnterIf";
     87   }
     88 
     89   private String enterWhen() {
     90     return interruptible ? "enterWhen" : "enterWhenUninterruptibly";
     91   }
     92 
     93   private String waitFor() {
     94     return interruptible ? "waitFor" : "waitForUninterruptibly";
     95   }
     96 
     97   private String leave() {
     98     return "leave";
     99   }
    100 
    101   public final void testMutualExclusion() throws Exception {
    102     thread1.callAndAssertReturns(enter());
    103     thread2.callAndAssertBlocks(enter());
    104     thread1.callAndAssertReturns(leave());
    105     thread2.assertPriorCallReturns(enter());
    106   }
    107 
    108   public final void testTryEnter() throws Exception {
    109     thread1.callAndAssertReturns(true, tryEnter());
    110     thread2.callAndAssertReturns(false, tryEnter());
    111     thread1.callAndAssertReturns(true, tryEnter());
    112     thread2.callAndAssertReturns(false, tryEnter());
    113     thread1.callAndAssertReturns(leave());
    114     thread2.callAndAssertReturns(false, tryEnter());
    115     thread1.callAndAssertReturns(leave());
    116     thread2.callAndAssertReturns(true, tryEnter());
    117   }
    118 
    119   public final void testSystemStateMethods() throws Exception {
    120     checkSystemStateMethods(0);
    121     thread1.callAndAssertReturns(enter());
    122     checkSystemStateMethods(1);
    123     thread1.callAndAssertReturns(enter());
    124     checkSystemStateMethods(2);
    125     thread1.callAndAssertReturns(leave());
    126     checkSystemStateMethods(1);
    127     thread1.callAndAssertReturns(leave());
    128     checkSystemStateMethods(0);
    129   }
    130 
    131   private void checkSystemStateMethods(int enterCount) throws Exception {
    132     thread1.callAndAssertReturns(enterCount != 0, "isOccupied");
    133     thread1.callAndAssertReturns(enterCount != 0, "isOccupiedByCurrentThread");
    134     thread1.callAndAssertReturns(enterCount, "getOccupiedDepth");
    135 
    136     thread2.callAndAssertReturns(enterCount != 0, "isOccupied");
    137     thread2.callAndAssertReturns(false, "isOccupiedByCurrentThread");
    138     thread2.callAndAssertReturns(0, "getOccupiedDepth");
    139   }
    140 
    141   public final void testEnterWhen_initiallyTrue() throws Exception {
    142     TestGuard guard = new TestGuard(true);
    143     thread1.callAndAssertReturns(enterWhen(), guard);
    144   }
    145 
    146   public final void testEnterWhen_initiallyFalse() throws Exception {
    147     TestGuard guard = new TestGuard(false);
    148     thread1.callAndAssertWaits(enterWhen(), guard);
    149     monitor.enter();
    150     guard.setSatisfied(true);
    151     monitor.leave();
    152     thread1.assertPriorCallReturns(enterWhen());
    153   }
    154 
    155   public final void testEnterWhen_alreadyOccupied() throws Exception {
    156     TestGuard guard = new TestGuard(true);
    157     thread2.callAndAssertReturns(enter());
    158     thread1.callAndAssertBlocks(enterWhen(), guard);
    159     thread2.callAndAssertReturns(leave());
    160     thread1.assertPriorCallReturns(enterWhen());
    161   }
    162 
    163   public final void testEnterIf_initiallyTrue() throws Exception {
    164     TestGuard guard = new TestGuard(true);
    165     thread1.callAndAssertReturns(true, enterIf(), guard);
    166     thread2.callAndAssertBlocks(enter());
    167   }
    168 
    169   public final void testEnterIf_initiallyFalse() throws Exception {
    170     TestGuard guard = new TestGuard(false);
    171     thread1.callAndAssertReturns(false, enterIf(), guard);
    172     thread2.callAndAssertReturns(enter());
    173   }
    174 
    175   public final void testEnterIf_alreadyOccupied() throws Exception {
    176     TestGuard guard = new TestGuard(true);
    177     thread2.callAndAssertReturns(enter());
    178     thread1.callAndAssertBlocks(enterIf(), guard);
    179     thread2.callAndAssertReturns(leave());
    180     thread1.assertPriorCallReturns(true, enterIf());
    181   }
    182 
    183   public final void testTryEnterIf_initiallyTrue() throws Exception {
    184     TestGuard guard = new TestGuard(true);
    185     thread1.callAndAssertReturns(true, tryEnterIf(), guard);
    186     thread2.callAndAssertBlocks(enter());
    187   }
    188 
    189   public final void testTryEnterIf_initiallyFalse() throws Exception {
    190     TestGuard guard = new TestGuard(false);
    191     thread1.callAndAssertReturns(false, tryEnterIf(), guard);
    192     thread2.callAndAssertReturns(enter());
    193   }
    194 
    195   public final void testTryEnterIf_alreadyOccupied() throws Exception {
    196     TestGuard guard = new TestGuard(true);
    197     thread2.callAndAssertReturns(enter());
    198     thread1.callAndAssertReturns(false, tryEnterIf(), guard);
    199   }
    200 
    201   public final void testWaitFor_initiallyTrue() throws Exception {
    202     TestGuard guard = new TestGuard(true);
    203     thread1.callAndAssertReturns(enter());
    204     thread1.callAndAssertReturns(waitFor(), guard);
    205   }
    206 
    207   public final void testWaitFor_initiallyFalse() throws Exception {
    208     TestGuard guard = new TestGuard(false);
    209     thread1.callAndAssertReturns(enter());
    210     thread1.callAndAssertWaits(waitFor(), guard);
    211     monitor.enter();
    212     guard.setSatisfied(true);
    213     monitor.leave();
    214     thread1.assertPriorCallReturns(waitFor());
    215   }
    216 
    217   public final void testWaitFor_withoutEnter() throws Exception {
    218     TestGuard guard = new TestGuard(true);
    219     thread1.callAndAssertThrows(IllegalMonitorStateException.class, waitFor(), guard);
    220   }
    221 
    222   public void testNulls() {
    223     monitor.enter();  // Inhibit IllegalMonitorStateException
    224     new NullPointerTester()
    225         .setDefault(TimeUnit.class, TimeUnit.SECONDS)
    226         .setDefault(Monitor.Guard.class, new TestGuard(true))
    227         .testAllPublicInstanceMethods(monitor);
    228   }
    229 
    230   // TODO: Test enter(long, TimeUnit).
    231   // TODO: Test enterWhen(Guard, long, TimeUnit).
    232   // TODO: Test enterIf(Guard, long, TimeUnit).
    233   // TODO: Test waitFor(Guard, long, TimeUnit).
    234   // TODO: Test getQueueLength().
    235   // TODO: Test hasQueuedThreads().
    236   // TODO: Test getWaitQueueLength(Guard).
    237   // TODO: Test automatic signaling before leave, waitFor, and reentrant enterWhen.
    238   // TODO: Test blocking to re-enter monitor after being signaled.
    239   // TODO: Test interrupts with both interruptible and uninterruptible monitor.
    240   // TODO: Test multiple waiters: If guard is still satisfied, signal next waiter.
    241   // TODO: Test multiple waiters: If guard is no longer satisfied, do not signal next waiter.
    242 
    243 }
    244