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
17package com.google.common.util.concurrent;
18
19import com.google.common.testing.NullPointerTester;
20import com.google.common.testing.TearDownStack;
21
22import junit.framework.TestCase;
23
24import java.util.Random;
25import java.util.concurrent.TimeUnit;
26
27/**
28 * Tests for {@link Monitor}, either interruptible or uninterruptible.
29 *
30 * @author Justin T. Sampson
31 */
32
33public 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