blob: 58b272ebcfda7a536e84a21495234b0294c8f629 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You 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 org.apache.geode.test.junit.rules;
import static org.junit.Assert.assertThat;
import java.util.concurrent.TimeUnit;
import org.hamcrest.Matcher;
import org.junit.rules.ExpectedException;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
/**
* Expect an Exception within a specified timeout.
*/
public class ExpectedTimeoutRule implements TestRule {
/**
* @return a Rule that expects no timeout (identical to behavior without this Rule)
*/
public static ExpectedTimeoutRule none() {
return new ExpectedTimeoutRule();
}
private ExpectedException delegate;
private boolean expectsThrowable;
private long minDuration;
private long maxDuration;
private TimeUnit timeUnit;
private ExpectedTimeoutRule() {
this.delegate = ExpectedException.none();
}
public ExpectedTimeoutRule expectMinimumDuration(final long minDuration) {
this.minDuration = minDuration;
return this;
}
public ExpectedTimeoutRule expectMaximumDuration(final long maxDuration) {
this.maxDuration = maxDuration;
return this;
}
public ExpectedTimeoutRule expectTimeUnit(final TimeUnit timeUnit) {
this.timeUnit = timeUnit;
return this;
}
/**
* Adds {@code matcher} to the list of requirements for any thrown exception.
*/
public void expect(final Matcher<?> matcher) {
this.delegate.expect(matcher);
}
/**
* Adds to the list of requirements for any thrown exception that it should be an instance of
* {@code type}.
*/
public void expect(final Class<? extends Throwable> type) {
this.delegate.expect(type);
this.expectsThrowable = true;
}
/**
* Adds to the list of requirements for any thrown exception that it should <em>contain</em>
* string {@code substring}
*/
public void expectMessage(final String substring) {
this.delegate.expectMessage(substring);
}
/**
* Adds {@code matcher} to the list of requirements for the message returned from any thrown
* exception.
*/
public void expectMessage(final Matcher<String> matcher) {
this.delegate.expectMessage(matcher);
}
/**
* Adds {@code matcher} to the list of requirements for the cause of any thrown exception.
*/
public void expectCause(final Matcher<? extends Throwable> expectedCause) {
this.delegate.expectCause(expectedCause);
}
/**
* Returns true if a timeout is expected.
*/
protected boolean expectsTimeout() {
return this.minDuration > 0 || this.maxDuration > 0;
}
/**
* Returns true if a Throwable is expected.
*/
protected boolean expectsThrowable() {
return this.expectsThrowable;
}
@Override
public Statement apply(final Statement base, final Description description) {
Statement next = this.delegate.apply(base, description);
return new ExpectedTimeoutStatement(next);
}
private void handleTime(final Long duration) {
if (expectsTimeout()) {
assertThat(this.timeUnit.convert(duration, TimeUnit.NANOSECONDS),
new TimeMatcher(this.timeUnit, this.minDuration, this.maxDuration));
}
}
private static class TimeMatcher extends org.hamcrest.TypeSafeMatcher<Long> {
private final TimeUnit timeUnit;
private final long minDuration;
private final long maxDuration;
public TimeMatcher(final TimeUnit timeUnit, final long minDuration, final long maxDuration) {
this.timeUnit = timeUnit;
this.minDuration = minDuration;
this.maxDuration = maxDuration;
}
@Override
public boolean matchesSafely(final Long duration) {
return duration >= this.minDuration && duration <= this.maxDuration;
}
@Override
public void describeTo(final org.hamcrest.Description description) {
description.appendText("expects duration to be greater than or equal to ")
.appendValue(this.minDuration).appendText(" and less than or equal to ")
.appendValue(this.maxDuration).appendText(" ").appendValue(this.timeUnit);
}
}
private class ExpectedTimeoutStatement extends Statement {
private final Statement next;
public ExpectedTimeoutStatement(final Statement base) {
this.next = base;
}
@Override
public void evaluate() throws Throwable {
long start = System.nanoTime();
this.next.evaluate();
handleTime(System.nanoTime() - start);
}
}
}