blob: 6d620ffc24cd604d1c8f8e806d085f77c4ce2f30 [file] [log] [blame]
/*
* Copyright (C) 2010 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.util.concurrent;
import com.google.common.testing.NullPointerTester;
import com.google.common.testing.TearDownStack;
import java.util.Random;
import junit.framework.TestCase;
/**
* Tests for {@link Monitor}, either interruptible or uninterruptible.
*
* @author Justin T. Sampson
*/
public abstract class MonitorTestCase extends TestCase {
public class TestGuard extends Monitor.Guard {
private volatile boolean satisfied;
public TestGuard(boolean satisfied) {
super(MonitorTestCase.this.monitor);
this.satisfied = satisfied;
}
@Override
public boolean isSatisfied() {
return this.satisfied;
}
public void setSatisfied(boolean satisfied) {
this.satisfied = satisfied;
}
}
private final boolean interruptible;
private Monitor monitor;
private final TearDownStack tearDownStack = new TearDownStack(true);
private TestThread<Monitor> thread1;
private TestThread<Monitor> thread2;
protected MonitorTestCase(boolean interruptible) {
this.interruptible = interruptible;
}
@Override
protected final void setUp() throws Exception {
boolean fair = new Random().nextBoolean();
monitor = new Monitor(fair);
tearDownStack.addTearDown(thread1 = new TestThread<>(monitor, "TestThread #1"));
tearDownStack.addTearDown(thread2 = new TestThread<>(monitor, "TestThread #2"));
}
@Override
protected final void tearDown() {
tearDownStack.runTearDown();
}
private String enter() {
return interruptible ? "enterInterruptibly" : "enter";
}
private String tryEnter() {
return "tryEnter";
}
private String enterIf() {
return interruptible ? "enterIfInterruptibly" : "enterIf";
}
private String tryEnterIf() {
return "tryEnterIf";
}
private String enterWhen() {
return interruptible ? "enterWhen" : "enterWhenUninterruptibly";
}
private String waitFor() {
return interruptible ? "waitFor" : "waitForUninterruptibly";
}
private String leave() {
return "leave";
}
public final void testMutualExclusion() throws Exception {
thread1.callAndAssertReturns(enter());
thread2.callAndAssertBlocks(enter());
thread1.callAndAssertReturns(leave());
thread2.assertPriorCallReturns(enter());
}
public final void testTryEnter() throws Exception {
thread1.callAndAssertReturns(true, tryEnter());
thread2.callAndAssertReturns(false, tryEnter());
thread1.callAndAssertReturns(true, tryEnter());
thread2.callAndAssertReturns(false, tryEnter());
thread1.callAndAssertReturns(leave());
thread2.callAndAssertReturns(false, tryEnter());
thread1.callAndAssertReturns(leave());
thread2.callAndAssertReturns(true, tryEnter());
}
public final void testSystemStateMethods() throws Exception {
checkSystemStateMethods(0);
thread1.callAndAssertReturns(enter());
checkSystemStateMethods(1);
thread1.callAndAssertReturns(enter());
checkSystemStateMethods(2);
thread1.callAndAssertReturns(leave());
checkSystemStateMethods(1);
thread1.callAndAssertReturns(leave());
checkSystemStateMethods(0);
}
private void checkSystemStateMethods(int enterCount) throws Exception {
thread1.callAndAssertReturns(enterCount != 0, "isOccupied");
thread1.callAndAssertReturns(enterCount != 0, "isOccupiedByCurrentThread");
thread1.callAndAssertReturns(enterCount, "getOccupiedDepth");
thread2.callAndAssertReturns(enterCount != 0, "isOccupied");
thread2.callAndAssertReturns(false, "isOccupiedByCurrentThread");
thread2.callAndAssertReturns(0, "getOccupiedDepth");
}
public final void testEnterWhen_initiallyTrue() throws Exception {
TestGuard guard = new TestGuard(true);
thread1.callAndAssertReturns(enterWhen(), guard);
}
public final void testEnterWhen_initiallyFalse() throws Exception {
TestGuard guard = new TestGuard(false);
thread1.callAndAssertWaits(enterWhen(), guard);
monitor.enter();
guard.setSatisfied(true);
monitor.leave();
thread1.assertPriorCallReturns(enterWhen());
}
public final void testEnterWhen_alreadyOccupied() throws Exception {
TestGuard guard = new TestGuard(true);
thread2.callAndAssertReturns(enter());
thread1.callAndAssertBlocks(enterWhen(), guard);
thread2.callAndAssertReturns(leave());
thread1.assertPriorCallReturns(enterWhen());
}
public final void testEnterIf_initiallyTrue() throws Exception {
TestGuard guard = new TestGuard(true);
thread1.callAndAssertReturns(true, enterIf(), guard);
thread2.callAndAssertBlocks(enter());
}
public final void testEnterIf_initiallyFalse() throws Exception {
TestGuard guard = new TestGuard(false);
thread1.callAndAssertReturns(false, enterIf(), guard);
thread2.callAndAssertReturns(enter());
}
public final void testEnterIf_alreadyOccupied() throws Exception {
TestGuard guard = new TestGuard(true);
thread2.callAndAssertReturns(enter());
thread1.callAndAssertBlocks(enterIf(), guard);
thread2.callAndAssertReturns(leave());
thread1.assertPriorCallReturns(true, enterIf());
}
public final void testTryEnterIf_initiallyTrue() throws Exception {
TestGuard guard = new TestGuard(true);
thread1.callAndAssertReturns(true, tryEnterIf(), guard);
thread2.callAndAssertBlocks(enter());
}
public final void testTryEnterIf_initiallyFalse() throws Exception {
TestGuard guard = new TestGuard(false);
thread1.callAndAssertReturns(false, tryEnterIf(), guard);
thread2.callAndAssertReturns(enter());
}
public final void testTryEnterIf_alreadyOccupied() throws Exception {
TestGuard guard = new TestGuard(true);
thread2.callAndAssertReturns(enter());
thread1.callAndAssertReturns(false, tryEnterIf(), guard);
}
public final void testWaitFor_initiallyTrue() throws Exception {
TestGuard guard = new TestGuard(true);
thread1.callAndAssertReturns(enter());
thread1.callAndAssertReturns(waitFor(), guard);
}
public final void testWaitFor_initiallyFalse() throws Exception {
TestGuard guard = new TestGuard(false);
thread1.callAndAssertReturns(enter());
thread1.callAndAssertWaits(waitFor(), guard);
monitor.enter();
guard.setSatisfied(true);
monitor.leave();
thread1.assertPriorCallReturns(waitFor());
}
public final void testWaitFor_withoutEnter() throws Exception {
TestGuard guard = new TestGuard(true);
thread1.callAndAssertThrows(IllegalMonitorStateException.class, waitFor(), guard);
}
public void testNulls() {
monitor.enter(); // Inhibit IllegalMonitorStateException
new NullPointerTester()
.setDefault(Monitor.Guard.class, new TestGuard(true))
.testAllPublicInstanceMethods(monitor);
}
// TODO: Test enter(long, TimeUnit).
// TODO: Test enterWhen(Guard, long, TimeUnit).
// TODO: Test enterIf(Guard, long, TimeUnit).
// TODO: Test waitFor(Guard, long, TimeUnit).
// TODO: Test getQueueLength().
// TODO: Test hasQueuedThreads().
// TODO: Test getWaitQueueLength(Guard).
// TODO: Test automatic signaling before leave, waitFor, and reentrant enterWhen.
// TODO: Test blocking to re-enter monitor after being signaled.
// TODO: Test interrupts with both interruptible and uninterruptible monitor.
// TODO: Test multiple waiters: If guard is still satisfied, signal next waiter.
// TODO: Test multiple waiters: If guard is no longer satisfied, do not signal next waiter.
}