|
Java example source code file (GeneratedMonitorTest.java)
The GeneratedMonitorTest.java Java example source code
/*
* Copyright (C) 2014 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 static com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly;
import com.google.common.base.CaseFormat;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Ints;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Locale;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
/**
* Generated tests for {@link Monitor}.
*
* <p>This test class generates all of its own test cases in the {@link #suite()} method. Every
* {@code enterXxx}, {@code tryEnterXxx}, and {@code waitForXxx} method of the {@code Monitor} class
* is analyzed reflectively to determine appropriate test cases based on its signature. Additional
* ad hoc test cases can be found in {@link SupplementalMonitorTest}.
*
* @author Justin T. Sampson
*/
public class GeneratedMonitorTest extends TestCase {
public static TestSuite suite() {
TestSuite suite = new TestSuite();
Method[] methods = Monitor.class.getMethods();
sortMethods(methods);
for (Method method : methods) {
if (isAnyEnter(method) || isWaitFor(method)) {
validateMethod(method);
addTests(suite, method);
}
}
assertEquals(548, suite.testCount());
return suite;
}
/**
* A typical timeout value we'll use in the tests.
*/
private static final long SMALL_TIMEOUT_MILLIS = 10;
/**
* How long to wait when determining that a thread is blocked if we expect it to be blocked.
*/
private static final long EXPECTED_HANG_DELAY_MILLIS = 75;
/**
* How long to wait when determining that a thread is blocked if we DON'T expect it to be blocked.
*/
private static final long UNEXPECTED_HANG_DELAY_MILLIS = 10000;
/**
* Various scenarios to be generated for each method under test. The actual scenario generation
* (determining which scenarios are applicable to which methods and what the outcome should be)
* takes place in {@link #addTests(TestSuite, Method)}.
*/
private enum Scenario {
SATISFIED_AND_UNOCCUPIED_BEFORE_ENTERING,
UNSATISFIED_AND_UNOCCUPIED_BEFORE_ENTERING,
SATISFIED_AND_OCCUPIED_BEFORE_ENTERING,
SATISFIED_UNOCCUPIED_AND_INTERRUPTED_BEFORE_ENTERING,
SATISFIED_BEFORE_WAITING,
SATISFIED_WHILE_WAITING,
SATISFIED_AND_INTERRUPTED_BEFORE_WAITING,
UNSATISFIED_BEFORE_AND_WHILE_WAITING,
UNSATISFIED_AND_INTERRUPTED_BEFORE_WAITING;
@Override
public String toString() {
return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name());
}
}
/**
* Timeout values to combine with each {@link Scenario}.
*/
private enum Timeout {
MIN(Long.MIN_VALUE, "-oo"),
MINUS_SMALL(-SMALL_TIMEOUT_MILLIS, "-" + SMALL_TIMEOUT_MILLIS + "ms"),
ZERO(0L, "0ms"),
SMALL(SMALL_TIMEOUT_MILLIS, SMALL_TIMEOUT_MILLIS + "ms"),
LARGE(UNEXPECTED_HANG_DELAY_MILLIS * 2, (2 * UNEXPECTED_HANG_DELAY_MILLIS) + "ms"),
MAX(Long.MAX_VALUE, "+oo");
final long millis;
final String label;
private Timeout(long millis, String label) {
this.millis = millis;
this.label = label;
}
@Override
public String toString() {
return label;
}
}
/**
* Convenient subsets of the {@link Timeout} enumeration for specifying scenario outcomes.
*/
private enum TimeoutsToUse {
ANY(Timeout.values()),
PAST(Timeout.MIN, Timeout.MINUS_SMALL, Timeout.ZERO),
FUTURE(Timeout.SMALL, Timeout.MAX),
SMALL(Timeout.SMALL),
FINITE(Timeout.MIN, Timeout.MINUS_SMALL, Timeout.ZERO, Timeout.SMALL),
INFINITE(Timeout.LARGE, Timeout.MAX);
final ImmutableList<Timeout> timeouts;
private TimeoutsToUse(Timeout... timeouts) {
this.timeouts = ImmutableList.copyOf(timeouts);
}
}
/**
* Possible outcomes of calling any of the methods under test.
*/
private enum Outcome {
/**
* The method returned normally and is either void or returned true.
*/
SUCCESS,
/**
* The method returned false.
*/
FAILURE,
/**
* The method threw an InterruptedException.
*/
INTERRUPT,
/**
* The method did not return or throw anything.
*/
HANG;
@Override
public String toString() {
return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name());
}
}
/**
* Identifies all enterXxx and tryEnterXxx methods.
*/
private static boolean isAnyEnter(Method method) {
return method.getName().startsWith("enter") || method.getName().startsWith("tryEnter");
}
/**
* Identifies just tryEnterXxx methods (a subset of {@link #isAnyEnter}), which never block.
*/
private static boolean isTryEnter(Method method) {
return method.getName().startsWith("tryEnter");
}
/**
* Identifies just enterIfXxx methods (a subset of {@link #isAnyEnter}), which are mostly like the
* enterXxx methods but behave like tryEnterXxx in some scenarios.
*/
private static boolean isEnterIf(Method method) {
return method.getName().startsWith("enterIf");
}
/**
* Identifies all waitForXxx methods, which must be called while occupying the monitor.
*/
private static boolean isWaitFor(Method method) {
return method.getName().startsWith("waitFor");
}
/**
* Determines whether the given method takes a Guard as its first parameter.
*/
private static boolean isGuarded(Method method) {
Class<?>[] parameterTypes = method.getParameterTypes();
return parameterTypes.length >= 1 && parameterTypes[0] == Monitor.Guard.class;
}
/**
* Determines whether the given method takes a time and unit as its last two parameters.
*/
private static boolean isTimed(Method method) {
Class<?>[] parameterTypes = method.getParameterTypes();
return parameterTypes.length >= 2
&& parameterTypes[parameterTypes.length - 2] == long.class
&& parameterTypes[parameterTypes.length - 1] == TimeUnit.class;
}
/**
* Determines whether the given method returns a boolean value.
*/
private static boolean isBoolean(Method method) {
return method.getReturnType() == boolean.class;
}
/**
* Determines whether the given method can throw InterruptedException.
*/
private static boolean isInterruptible(Method method) {
return Arrays.asList(method.getExceptionTypes()).contains(InterruptedException.class);
}
/**
* Sorts the given methods primarily by name and secondarily by number of parameters.
*/
private static void sortMethods(Method[] methods) {
Arrays.sort(methods, new Comparator<Method>() {
@Override public int compare(Method m1, Method m2) {
int nameComparison = m1.getName().compareTo(m2.getName());
if (nameComparison != 0) {
return nameComparison;
} else {
return Ints.compare(m1.getParameterTypes().length, m2.getParameterTypes().length);
}
}
});
}
/**
* Validates that the given method's signature meets all of our assumptions.
*/
private static void validateMethod(Method method) {
String desc = method.toString();
assertTrue(desc, isAnyEnter(method) || isWaitFor(method));
switch (method.getParameterTypes().length) {
case 0:
assertFalse(desc, isGuarded(method));
assertFalse(desc, isTimed(method));
break;
case 1:
assertTrue(desc, isGuarded(method));
assertFalse(desc, isTimed(method));
break;
case 2:
assertFalse(desc, isGuarded(method));
assertTrue(desc, isTimed(method));
break;
case 3:
assertTrue(desc, isGuarded(method));
assertTrue(desc, isTimed(method));
break;
default:
fail(desc);
}
if (method.getReturnType() == void.class) {
assertFalse(desc, isBoolean(method));
} else {
assertTrue(desc, isBoolean(method));
}
switch (method.getExceptionTypes().length) {
case 0:
assertFalse(desc, isInterruptible(method));
break;
case 1:
assertTrue(desc, isInterruptible(method));
break;
default:
fail(desc);
}
if (isEnterIf(method)) {
assertTrue(desc, isGuarded(method));
assertTrue(desc, isBoolean(method));
} else if (isTryEnter(method)) {
assertFalse(desc, isTimed(method));
assertTrue(desc, isBoolean(method));
assertFalse(desc, isInterruptible(method));
} else if (isWaitFor(method)) {
assertTrue(desc, isGuarded(method));
assertEquals(desc, isTimed(method), isBoolean(method));
} else { // any other enterXxx method
assertEquals(desc, isTimed(method), isBoolean(method));
}
}
/**
* Generates all test cases appropriate for the given method.
*/
private static void addTests(TestSuite suite, Method method) {
if (isGuarded(method)) {
for (boolean fair1 : new boolean[] { true, false }) {
for (boolean fair2 : new boolean[] { true, false }) {
suite.addTest(generateGuardWithWrongMonitorTestCase(method, fair1, fair2));
}
}
}
if (isAnyEnter(method)) {
addTests(suite, method,
Scenario.SATISFIED_AND_UNOCCUPIED_BEFORE_ENTERING,
TimeoutsToUse.ANY,
Outcome.SUCCESS);
addTests(suite, method,
Scenario.UNSATISFIED_AND_UNOCCUPIED_BEFORE_ENTERING,
TimeoutsToUse.FINITE,
isGuarded(method)
? (isBoolean(method) ? Outcome.FAILURE : Outcome.HANG)
: Outcome.SUCCESS);
addTests(suite, method,
Scenario.UNSATISFIED_AND_UNOCCUPIED_BEFORE_ENTERING,
TimeoutsToUse.INFINITE,
isGuarded(method)
? (isTryEnter(method) || isEnterIf(method) ? Outcome.FAILURE : Outcome.HANG)
: Outcome.SUCCESS);
addTests(suite, method,
Scenario.SATISFIED_AND_OCCUPIED_BEFORE_ENTERING,
TimeoutsToUse.FINITE,
isBoolean(method) ? Outcome.FAILURE : Outcome.HANG);
addTests(suite, method,
Scenario.SATISFIED_AND_OCCUPIED_BEFORE_ENTERING,
TimeoutsToUse.INFINITE,
isGuarded(method)
? Outcome.HANG
: (isTryEnter(method) ? Outcome.FAILURE : Outcome.HANG));
addTests(suite, method,
Scenario.SATISFIED_UNOCCUPIED_AND_INTERRUPTED_BEFORE_ENTERING,
TimeoutsToUse.ANY,
isInterruptible(method) ? Outcome.INTERRUPT : Outcome.SUCCESS);
} else { // any waitForXxx method
suite.addTest(generateWaitForWhenNotOccupyingTestCase(method, true));
suite.addTest(generateWaitForWhenNotOccupyingTestCase(method, false));
addTests(suite, method,
Scenario.SATISFIED_BEFORE_WAITING,
TimeoutsToUse.ANY,
Outcome.SUCCESS);
addTests(suite, method,
Scenario.SATISFIED_WHILE_WAITING,
TimeoutsToUse.INFINITE,
Outcome.SUCCESS);
addTests(suite, method,
Scenario.SATISFIED_WHILE_WAITING,
TimeoutsToUse.PAST,
Outcome.FAILURE);
addTests(suite, method,
Scenario.SATISFIED_AND_INTERRUPTED_BEFORE_WAITING,
TimeoutsToUse.ANY,
Outcome.SUCCESS);
addTests(suite, method,
Scenario.UNSATISFIED_BEFORE_AND_WHILE_WAITING,
TimeoutsToUse.FINITE,
Outcome.FAILURE);
addTests(suite, method,
Scenario.UNSATISFIED_BEFORE_AND_WHILE_WAITING,
TimeoutsToUse.INFINITE,
Outcome.HANG);
addTests(suite, method,
Scenario.UNSATISFIED_AND_INTERRUPTED_BEFORE_WAITING,
TimeoutsToUse.PAST,
// prefer responding to interrupt over timing out
isInterruptible(method) ? Outcome.INTERRUPT : Outcome.FAILURE);
addTests(suite, method,
Scenario.UNSATISFIED_AND_INTERRUPTED_BEFORE_WAITING,
TimeoutsToUse.SMALL,
isInterruptible(method) ? Outcome.INTERRUPT : Outcome.FAILURE);
addTests(suite, method,
Scenario.UNSATISFIED_AND_INTERRUPTED_BEFORE_WAITING,
TimeoutsToUse.INFINITE,
isInterruptible(method) ? Outcome.INTERRUPT : Outcome.HANG);
}
}
/**
* Generates test cases for the given combination of scenario and timeouts. For methods that take
* an explicit timeout value, all of the given timeoutsToUse result in individual test cases. For
* methods that do not take an explicit timeout value, a single test case is generated only if the
* implicit timeout of that method matches the given timeoutsToUse. For example, enter() is
* treated like enter(MAX, MILLIS) and tryEnter() is treated like enter(0, MILLIS).
*/
private static void addTests(TestSuite suite, Method method, Scenario scenario,
TimeoutsToUse timeoutsToUse, Outcome expectedOutcome) {
for (boolean fair : new boolean[] { true, false }) {
if (isTimed(method)) {
for (Timeout timeout : timeoutsToUse.timeouts) {
suite.addTest(new GeneratedMonitorTest(method, scenario, fair, timeout, expectedOutcome));
}
} else {
Timeout implicitTimeout = (isTryEnter(method) ? Timeout.ZERO : Timeout.MAX);
if (timeoutsToUse.timeouts.contains(implicitTimeout)) {
suite.addTest(new GeneratedMonitorTest(method, scenario, fair, null, expectedOutcome));
}
}
}
}
/**
* A guard that encapsulates a simple, mutable boolean flag.
*/
static class FlagGuard extends Monitor.Guard {
private boolean satisfied;
protected FlagGuard(Monitor monitor) {
super(monitor);
}
@Override
public boolean isSatisfied() {
return satisfied;
}
public void setSatisfied(boolean satisfied) {
this.satisfied = satisfied;
}
}
private final Method method;
private final Scenario scenario;
private final Timeout timeout;
private final Outcome expectedOutcome;
private final Monitor monitor;
private final FlagGuard guard;
private final CountDownLatch tearDownLatch;
private final CountDownLatch doingCallLatch;
private final CountDownLatch callCompletedLatch;
private GeneratedMonitorTest(
Method method, Scenario scenario, boolean fair, Timeout timeout, Outcome expectedOutcome) {
super(nameFor(method, scenario, fair, timeout, expectedOutcome));
this.method = method;
this.scenario = scenario;
this.timeout = timeout;
this.expectedOutcome = expectedOutcome;
this.monitor = new Monitor(fair);
this.guard = new FlagGuard(monitor);
this.tearDownLatch = new CountDownLatch(1);
this.doingCallLatch = new CountDownLatch(1);
this.callCompletedLatch = new CountDownLatch(1);
}
private static String nameFor(
Method method, Scenario scenario, boolean fair, Timeout timeout, Outcome expectedOutcome) {
return String.format(Locale.ROOT,
"%s%s(%s)/%s->%s",
method.getName(),
fair ? "(fair)" : "(nonfair)",
(timeout == null) ? "untimed" : timeout,
scenario,
expectedOutcome);
}
@Override
protected void runTest() throws Throwable {
final Runnable runChosenTest = new Runnable() {
@Override public void run() { runChosenTest(); }
};
final FutureTask<Void> task = new FutureTask
Other Java examples (source code examples)Here is a short list of links related to this Java GeneratedMonitorTest.java source code file: |
| ... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.