Skip to content

Commit

Permalink
Add Duration overloads. Fixes mockito#1815
Browse files Browse the repository at this point in the history
  • Loading branch information
kluever committed Nov 6, 2019
1 parent 68bc593 commit f21626b
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 62 deletions.
131 changes: 113 additions & 18 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 @@ -2830,26 +2832,61 @@ public static VerificationMode only() {
* <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>
*
* See examples in javadoc for {@link Mockito} class
*
* @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 All @@ -2864,19 +2901,19 @@ public static VerificationWithTimeout timeout(long millis) {
*
* <pre class="code"><code class="java">
* //passes after 100ms, if someMethod() has only been called once at that time.
* verify(mock, after(100)).someMethod();
* verify(mock, after(Duration.ofMillis(100))).someMethod();
* //above is an alias to:
* verify(mock, after(100).times(1)).someMethod();
* 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(100).times(2)).someMethod();
* verify(mock, after(Duration.ofMillis(100)).times(2)).someMethod();
*
* //passes if someMethod() has not been called, as tested after 100 millis
* verify(mock, after(100).never()).someMethod();
* 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(100, yourOwnVerificationMode)).someMethod();
* verify(mock, new After(Duration.ofMillis(100), yourOwnVerificationMode)).someMethod();
* </code></pre>
*
* <strong>timeout() vs. after()</strong>
Expand All @@ -2888,12 +2925,12 @@ public static VerificationWithTimeout timeout(long millis) {
* <pre class="code"><code class="java">
* //1.
* mock.foo();
* verify(mock, after(1000)).foo();
* //waits 1000 millis and succeeds
* verify(mock, after(Duration.ofSeconds(1))).foo();
* //waits 1 second and succeeds
*
* //2.
* mock.foo();
* verify(mock, timeout(1000)).foo();
* verify(mock, timeout(Duration.ofSeconds(1))).foo();
* //succeeds immediately
* </code></pre>
*
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
22 changes: 12 additions & 10 deletions src/main/java/org/mockito/internal/util/Timer.java
Expand Up @@ -6,22 +6,24 @@

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

import java.time.Duration;

public class Timer {

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

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;
return System.currentTimeMillis() - startTime <= duration.toMillis();
}

/**
Expand All @@ -31,13 +33,13 @@ public void start() {
startTime = System.currentTimeMillis();
}

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

0 comments on commit f21626b

Please sign in to comment.