1/*
2 * Copyright (c) 2007 Mockito contributors
3 * This program is made available under the terms of the MIT License.
4 */
5package org.mockito.internal.verification;
6
7import org.mockito.exceptions.base.MockitoAssertionError;
8import org.mockito.internal.util.Timer;
9import org.mockito.internal.verification.api.VerificationData;
10import org.mockito.verification.VerificationMode;
11
12/**
13 * Verifies that another verification mode (the delegate) is satisfied within a certain timeframe
14 * (before timeoutMillis has passed, measured from the call to verify()), and either returns immediately
15 * once it does, or waits until it is definitely satisfied once the full time has passed.
16 */
17public class VerificationOverTimeImpl implements VerificationMode {
18
19    private final long pollingPeriodMillis;
20    private final VerificationMode delegate;
21    private final boolean returnOnSuccess;
22    private final Timer timer;
23
24    /**
25     * Create this verification mode, to be used to verify invocation ongoing data later.
26     *
27     * @param pollingPeriodMillis The frequency to poll delegate.verify(), to check whether the delegate has been satisfied
28     * @param durationMillis The max time to wait (in millis) for the delegate verification mode to be satisfied
29     * @param delegate The verification mode to delegate overall success or failure to
30     * @param returnOnSuccess Whether to immediately return successfully once the delegate is satisfied (as in
31     *                        {@link org.mockito.verification.VerificationWithTimeout}, or to only return once
32     *                        the delegate is satisfied and the full duration has passed (as in
33     *                        {@link org.mockito.verification.VerificationAfterDelay}).
34     */
35    public VerificationOverTimeImpl(long pollingPeriodMillis, long durationMillis, VerificationMode delegate, boolean returnOnSuccess) {
36        this(pollingPeriodMillis, delegate, returnOnSuccess, new Timer(durationMillis));
37    }
38
39    /**
40     * Create this verification mode, to be used to verify invocation ongoing data later.
41     *
42     * @param pollingPeriodMillis The frequency to poll delegate.verify(), to check whether the delegate has been satisfied
43     * @param delegate The verification mode to delegate overall success or failure to
44     * @param returnOnSuccess Whether to immediately return successfully once the delegate is satisfied (as in
45     *                        {@link org.mockito.verification.VerificationWithTimeout}, or to only return once
46     *                        the delegate is satisfied and the full duration has passed (as in
47     *                        {@link org.mockito.verification.VerificationAfterDelay}).
48     * @param timer Checker of whether the duration of the verification is still acceptable
49     */
50    public VerificationOverTimeImpl(long pollingPeriodMillis, VerificationMode delegate, boolean returnOnSuccess, Timer timer) {
51        this.pollingPeriodMillis = pollingPeriodMillis;
52        this.delegate = delegate;
53        this.returnOnSuccess = returnOnSuccess;
54        this.timer = timer;
55    }
56
57    /**
58     * Verify the given ongoing verification data, and confirm that it satisfies the delegate verification mode
59     * before the full duration has passed.
60     *
61     * In practice, this polls the delegate verification mode until it is satisfied. If it is not satisfied once
62     * the full duration has passed, the last error returned by the delegate verification mode will be thrown
63     * here in turn. This may be thrown early if the delegate is unsatisfied and the verification mode is known
64     * to never recover from this situation (e.g. {@link AtMost}).
65     *
66     * If it is satisfied before the full duration has passed, behaviour is dependent on the returnOnSuccess parameter
67     * given in the constructor. If true, this verification mode is immediately satisfied once the delegate is. If
68     * false, this verification mode is not satisfied until the delegate is satisfied and the full time has passed.
69     *
70     * @throws MockitoAssertionError if the delegate verification mode does not succeed before the timeout
71     */
72    public void verify(VerificationData data) {
73        AssertionError error = null;
74
75        timer.start();
76        while (timer.isCounting()) {
77            try {
78                delegate.verify(data);
79
80                if (returnOnSuccess) {
81                    return;
82                } else {
83                    error = null;
84                }
85            } catch (MockitoAssertionError e) {
86                error = handleVerifyException(e);
87            }
88            catch (AssertionError e) {
89                error = handleVerifyException(e);
90            }
91        }
92
93        if (error != null) {
94            throw error;
95        }
96    }
97
98    private AssertionError handleVerifyException(AssertionError e) {
99        if (canRecoverFromFailure(delegate)) {
100            sleep(pollingPeriodMillis);
101            return e;
102        } else {
103            throw e;
104        }
105    }
106
107    protected boolean canRecoverFromFailure(VerificationMode verificationMode) {
108        return !(verificationMode instanceof AtMost || verificationMode instanceof NoMoreInteractions);
109    }
110
111    public VerificationOverTimeImpl copyWithVerificationMode(VerificationMode verificationMode) {
112        return new VerificationOverTimeImpl(pollingPeriodMillis, timer.duration(), verificationMode, returnOnSuccess);
113    }
114
115    private void sleep(long sleep) {
116        try {
117            Thread.sleep(sleep);
118        } catch (InterruptedException ie) {
119            throw new RuntimeException("Thread sleep has been interrupted", ie);
120        }
121    }
122
123    @Override
124    public VerificationMode description(String description) {
125        return VerificationModeFactory.description(this, description);
126    }
127
128    public boolean isReturnOnSuccess() {
129        return returnOnSuccess;
130    }
131
132    public long getPollingPeriodMillis() {
133        return pollingPeriodMillis;
134    }
135
136    public Timer getTimer() {
137        return timer;
138    }
139
140    public VerificationMode getDelegate() {
141        return delegate;
142    }
143}
144