Skip to content

Commit

Permalink
Add java.time.Duration overloads to timeout and after (mockito#1818)
Browse files Browse the repository at this point in the history
  • Loading branch information
kluever authored and epeee committed Jun 22, 2020
1 parent 1416dc6 commit 4a42a34
Show file tree
Hide file tree
Showing 11 changed files with 218 additions and 54 deletions.
109 changes: 102 additions & 7 deletions src/main/java/org/mockito/Mockito.java
Expand Up @@ -4,6 +4,8 @@
*/
package org.mockito;

import java.time.Duration;

import org.mockito.exceptions.misusing.PotentialStubbingProblem;
import org.mockito.exceptions.misusing.UnnecessaryStubbingException;
import org.mockito.internal.InternalMockHandler;
Expand Down Expand Up @@ -886,15 +888,15 @@
* <pre class="code"><code class="java">
* //passes when someMethod() is called no later than within 100 ms
* //exits immediately when verification is satisfied (e.g. may not wait full 100 ms)
* verify(mock, timeout(100)).someMethod();
* verify(mock, timeout(Duration.ofMillis(100))).someMethod();
* //above is an alias to:
* verify(mock, timeout(100).times(1)).someMethod();
* verify(mock, timeout(Duration.ofMillis(100)).times(1)).someMethod();
*
* //passes as soon as someMethod() has been called 2 times under 100 ms
* verify(mock, timeout(100).times(2)).someMethod();
* verify(mock, timeout(Duration.ofMillis(100)).times(2)).someMethod();
*
* //equivalent: this also passes as soon as someMethod() has been called 2 times under 100 ms
* verify(mock, timeout(100).atLeast(2)).someMethod();
* verify(mock, timeout(Duration.ofMillis(100)).atLeast(2)).someMethod();
* </code></pre>
*
*
Expand Down Expand Up @@ -2846,10 +2848,45 @@ public static VerificationMode only() {
* @param millis - duration in milliseconds
*
* @return object that allows fluent specification of the verification (times(x), atLeast(y), etc.)
* @deprecated Use {@link #timeout(Duration)} instead.
*/
@CheckReturnValue
@Deprecated
public static VerificationWithTimeout timeout(long millis) {
return new Timeout(millis, VerificationModeFactory.times(1));
return timeout(Duration.ofMillis(millis));
}

/**
* Verification will be triggered over and over until the given amount of time, allowing testing of async code.
* Useful when interactions with the mock object did not happened yet.
* Extensive use of {@code timeout()} method can be a code smell - there are better ways of testing concurrent code.
* <p>
* See also {@link #after(Duration)} method for testing async code.
* Differences between {@code timeout()} and {@code after} are explained in Javadoc for {@link #after(Duration)}.
*
* <pre class="code"><code class="java">
* //passes when someMethod() is called no later than within 100 ms
* //exits immediately when verification is satisfied (e.g. may not wait full 100 ms)
* verify(mock, timeout(Duration.ofMillis(100))).someMethod();
* //above is an alias to:
* verify(mock, timeout(Duration.ofMillis(100)).times(1)).someMethod();
*
* //passes as soon as someMethod() has been called 2 times under 100 ms
* verify(mock, timeout(Duration.ofMillis(100)).times(2)).someMethod();
*
* //equivalent: this also passes as soon as someMethod() has been called 2 times under 100 ms
* verify(mock, timeout(Duration.ofMillis(100)).atLeast(2)).someMethod();
* </code></pre>
*
* See examples in javadoc for {@link Mockito} class
*
* @param timeout how long to wait before timing out
*
* @return object that allows fluent specification of the verification (times(x), atLeast(y), etc.)
*/
@CheckReturnValue
public static VerificationWithTimeout timeout(Duration timeout) {
return new Timeout(timeout, VerificationModeFactory.times(1));
}

/**
Expand Down Expand Up @@ -2889,7 +2926,7 @@ public static VerificationWithTimeout timeout(long millis) {
* //1.
* mock.foo();
* verify(mock, after(1000)).foo();
* //waits 1000 millis and succeeds
* //waits 1 second and succeeds
*
* //2.
* mock.foo();
Expand All @@ -2902,10 +2939,68 @@ public static VerificationWithTimeout timeout(long millis) {
* @param millis - duration in milliseconds
*
* @return object that allows fluent specification of the verification
* @deprecated Use {@link #after(Duration)} instead.
*/
@CheckReturnValue
@Deprecated
public static VerificationAfterDelay after(long millis) {
return new After(millis, VerificationModeFactory.times(1));
return after(Duration.ofMillis(millis));
}

/**
* Verification will be triggered after given amount of time, allowing testing of async code.
* Useful when interactions with the mock object did not happened yet.
* Extensive use of {@code after()} method can be a code smell - there are better ways of testing concurrent code.
* <p>
* Not yet implemented to work with InOrder verification.
* <p>
* See also {@link #timeout(Duration)} method for testing async code.
* Differences between {@code timeout()} and {@code after()} are explained below.
*
* <pre class="code"><code class="java">
* //passes after 100ms, if someMethod() has only been called once at that time.
* verify(mock, after(Duration.ofMillis(100))).someMethod();
* //above is an alias to:
* verify(mock, after(Duration.ofMillis(100)).times(1)).someMethod();
*
* //passes if someMethod() is called <b>*exactly*</b> 2 times, as tested after 100 millis
* verify(mock, after(Duration.ofMillis(100)).times(2)).someMethod();
*
* //passes if someMethod() has not been called, as tested after 100 millis
* verify(mock, after(Duration.ofMillis(100)).never()).someMethod();
*
* //verifies someMethod() after a given time span using given verification mode
* //useful only if you have your own custom verification modes.
* verify(mock, new After(Duration.ofMillis(100), yourOwnVerificationMode)).someMethod();
* </code></pre>
*
* <strong>timeout() vs. after()</strong>
* <ul>
* <li>timeout() exits immediately with success when verification passes</li>
* <li>after() awaits full duration to check if verification passes</li>
* </ul>
* Examples:
* <pre class="code"><code class="java">
* //1.
* mock.foo();
* verify(mock, after(Duration.ofSeconds(1))).foo();
* //waits 1 second and succeeds
*
* //2.
* mock.foo();
* verify(mock, timeout(Duration.ofSeconds(1))).foo();
* //succeeds immediately
* </code></pre>
*
* See examples in javadoc for {@link Mockito} class
*
* @param delay how to to wait before triggering verification
*
* @return object that allows fluent specification of the verification
*/
@CheckReturnValue
public static VerificationAfterDelay after(Duration delay) {
return new After(delay, VerificationModeFactory.times(1));
}

/**
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/org/mockito/internal/exceptions/Reporter.java
Expand Up @@ -10,6 +10,7 @@

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
Expand Down Expand Up @@ -863,12 +864,12 @@ public static MockitoException usingConstructorWithFancySerializable(Serializabl
return new MockitoException("Mocks instantiated with constructor cannot be combined with " + mode + " serialization mode.");
}

public static MockitoException cannotCreateTimerWithNegativeDurationTime(long durationMillis) {
public static MockitoException cannotCreateTimerWithNegativeDurationTime(Duration duration) {
return new FriendlyReminderException(join(
"",
"Don't panic! I'm just a friendly reminder!",
"It is impossible for time to go backward, therefore...",
"You cannot put negative value of duration: (" + durationMillis + ")",
"You cannot put negative value of duration: (" + duration + ")",
"as argument of timer methods (after(), timeout())",
""
));
Expand Down
30 changes: 17 additions & 13 deletions src/main/java/org/mockito/internal/util/Timer.java
Expand Up @@ -6,38 +6,42 @@

import static org.mockito.internal.exceptions.Reporter.cannotCreateTimerWithNegativeDurationTime;

import java.time.Duration;
import java.time.Instant;

public class Timer {

private final long durationMillis;
private long startTime = -1;
private final Duration duration;
private Instant startTime = null;

public Timer(long durationMillis) {
validateInput(durationMillis);
this.durationMillis = durationMillis;
public Timer(Duration duration) {
validateInput(duration);
this.duration = duration;
}

/**
* Informs whether the timer is still counting down.
*/
public boolean isCounting() {
assert startTime != -1;
return System.currentTimeMillis() - startTime <= durationMillis;
assert startTime != null;
Duration elapsed = Duration.between(startTime, Instant.now());
return elapsed.compareTo(duration) <= 0;
}

/**
* Starts the timer count down.
*/
public void start() {
startTime = System.currentTimeMillis();
startTime = Instant.now();
}

private void validateInput(long durationMillis) {
if (durationMillis < 0) {
throw cannotCreateTimerWithNegativeDurationTime(durationMillis);
private void validateInput(Duration duration) {
if (duration.isNegative()) {
throw cannotCreateTimerWithNegativeDurationTime(duration);
}
}

public long duration() {
return durationMillis;
public Duration duration() {
return duration;
}
}
Expand Up @@ -4,51 +4,53 @@
*/
package org.mockito.internal.verification;

import java.time.Duration;

import org.mockito.exceptions.base.MockitoAssertionError;
import org.mockito.internal.util.Timer;
import org.mockito.internal.verification.api.VerificationData;
import org.mockito.verification.VerificationMode;

/**
* Verifies that another verification mode (the delegate) is satisfied within a certain timeframe
* (before timeoutMillis has passed, measured from the call to verify()), and either returns immediately
* (before timeout has passed, measured from the call to verify()), and either returns immediately
* once it does, or waits until it is definitely satisfied once the full time has passed.
*/
public class VerificationOverTimeImpl implements VerificationMode {

private final long pollingPeriodMillis;
private final Duration pollingPeriod;
private final VerificationMode delegate;
private final boolean returnOnSuccess;
private final Timer timer;

/**
* Create this verification mode, to be used to verify invocation ongoing data later.
*
* @param pollingPeriodMillis The frequency to poll delegate.verify(), to check whether the delegate has been satisfied
* @param durationMillis The max time to wait (in millis) for the delegate verification mode to be satisfied
* @param pollingPeriod The frequency to poll delegate.verify(), to check whether the delegate has been satisfied
* @param duration The max time to wait for the delegate verification mode to be satisfied
* @param delegate The verification mode to delegate overall success or failure to
* @param returnOnSuccess Whether to immediately return successfully once the delegate is satisfied (as in
* {@link org.mockito.verification.VerificationWithTimeout}, or to only return once
* the delegate is satisfied and the full duration has passed (as in
* {@link org.mockito.verification.VerificationAfterDelay}).
*/
public VerificationOverTimeImpl(long pollingPeriodMillis, long durationMillis, VerificationMode delegate, boolean returnOnSuccess) {
this(pollingPeriodMillis, delegate, returnOnSuccess, new Timer(durationMillis));
public VerificationOverTimeImpl(Duration pollingPeriod, Duration duration, VerificationMode delegate, boolean returnOnSuccess) {
this(pollingPeriod, delegate, returnOnSuccess, new Timer(duration));
}

/**
* Create this verification mode, to be used to verify invocation ongoing data later.
*
* @param pollingPeriodMillis The frequency to poll delegate.verify(), to check whether the delegate has been satisfied
* @param pollingPeriod The frequency to poll delegate.verify(), to check whether the delegate has been satisfied
* @param delegate The verification mode to delegate overall success or failure to
* @param returnOnSuccess Whether to immediately return successfully once the delegate is satisfied (as in
* {@link org.mockito.verification.VerificationWithTimeout}, or to only return once
* the delegate is satisfied and the full duration has passed (as in
* {@link org.mockito.verification.VerificationAfterDelay}).
* @param timer Checker of whether the duration of the verification is still acceptable
*/
public VerificationOverTimeImpl(long pollingPeriodMillis, VerificationMode delegate, boolean returnOnSuccess, Timer timer) {
this.pollingPeriodMillis = pollingPeriodMillis;
public VerificationOverTimeImpl(Duration pollingPeriod, VerificationMode delegate, boolean returnOnSuccess, Timer timer) {
this.pollingPeriod = pollingPeriod;
this.delegate = delegate;
this.returnOnSuccess = returnOnSuccess;
this.timer = timer;
Expand Down Expand Up @@ -97,7 +99,7 @@ public void verify(VerificationData data) {

private AssertionError handleVerifyException(AssertionError e) {
if (canRecoverFromFailure(delegate)) {
sleep(pollingPeriodMillis);
sleep(pollingPeriod);
return e;
} else {
throw e;
Expand All @@ -109,12 +111,12 @@ protected boolean canRecoverFromFailure(VerificationMode verificationMode) {
}

public VerificationOverTimeImpl copyWithVerificationMode(VerificationMode verificationMode) {
return new VerificationOverTimeImpl(pollingPeriodMillis, timer.duration(), verificationMode, returnOnSuccess);
return new VerificationOverTimeImpl(pollingPeriod, timer.duration(), verificationMode, returnOnSuccess);
}

private void sleep(long sleep) {
private void sleep(Duration sleep) {
try {
Thread.sleep(sleep);
Thread.sleep(sleep.toMillis());
} catch (InterruptedException ie) {
throw new RuntimeException("Thread sleep has been interrupted", ie);
}
Expand All @@ -124,8 +126,8 @@ public boolean isReturnOnSuccess() {
return returnOnSuccess;
}

public long getPollingPeriodMillis() {
return pollingPeriodMillis;
public Duration getPollingPeriod() {
return pollingPeriod;
}

public Timer getTimer() {
Expand Down
Expand Up @@ -35,7 +35,7 @@ private VerificationMode wrapInOrder(VerificationWrapper<?> verificationWrapper,
if (verificationMode instanceof VerificationOverTimeImpl) {
final VerificationOverTimeImpl verificationOverTime = (VerificationOverTimeImpl)verificationMode;
if (verificationOverTime.isReturnOnSuccess()) {
return new VerificationOverTimeImpl(verificationOverTime.getPollingPeriodMillis(),
return new VerificationOverTimeImpl(verificationOverTime.getPollingPeriod(),
verificationOverTime.getTimer().duration(),
wrapInOrder(verificationWrapper, verificationOverTime.getDelegate(), inOrder),
verificationOverTime.isReturnOnSuccess());
Expand Down
20 changes: 17 additions & 3 deletions src/main/java/org/mockito/verification/After.java
Expand Up @@ -4,6 +4,8 @@
*/
package org.mockito.verification;

import java.time.Duration;

import org.mockito.internal.verification.VerificationOverTimeImpl;
import org.mockito.internal.verification.VerificationWrapper;

Expand All @@ -20,13 +22,25 @@ public class After extends VerificationWrapper<VerificationOverTimeImpl> impleme
* <p>
* Typically, you won't use this class explicitly. Instead use timeout() method on Mockito class.
* See javadoc for {@link VerificationWithTimeout}
* @deprecated Use {@link After#After(Duration, VerificationMode)} instead.
*/
@Deprecated
public After(long delayMillis, VerificationMode verificationMode) {
this(10, delayMillis, verificationMode);
this(Duration.ofMillis(delayMillis), verificationMode);
}

/**
* See the javadoc for {@link VerificationAfterDelay}
* <p>
* Typically, you won't use this class explicitly. Instead use timeout() method on Mockito class.
* See javadoc for {@link VerificationWithTimeout}
*/
public After(Duration delay, VerificationMode verificationMode) {
this(Duration.ofMillis(10), delay, verificationMode);
}

After(long pollingPeriod, long delayMillis, VerificationMode verificationMode) {
this(new VerificationOverTimeImpl(pollingPeriod, delayMillis, verificationMode, false));
After(Duration pollingPeriod, Duration delay, VerificationMode verificationMode) {
this(new VerificationOverTimeImpl(pollingPeriod, delay, verificationMode, false));
}

After(VerificationOverTimeImpl verificationOverTime) {
Expand Down

0 comments on commit 4a42a34

Please sign in to comment.