1 package com.atlassian.pageobjects.elements.query;
2
3 import com.atlassian.annotations.PublicApi;
4 import org.apache.commons.lang.StringUtils;
5 import org.hamcrest.Description;
6 import org.hamcrest.Matcher;
7 import org.hamcrest.StringDescription;
8
9 import java.util.concurrent.TimeUnit;
10
11 import static com.google.common.base.Preconditions.checkNotNull;
12 import static org.hamcrest.Matchers.equalTo;
13 import static org.hamcrest.Matchers.is;
14
15 /**
16 * Utility class to poll and wait for a particular states of timeout-based queries inheriting from {@link PollingQuery}.
17 *
18 * @see PollingQuery
19 * @see TimedQuery
20 * @see TimedCondition
21 */
22 @PublicApi
23 public final class Poller
24 {
25 private Poller() {
26 throw new AssertionError("Don't instantiate me");
27 }
28
29 /**
30 * Wait until given <tt>condition</tt> is <code>true</code> by default timeout
31 *
32 * @param condition condition to exercise
33 */
34 public static void waitUntilTrue(TimedQuery<Boolean> condition)
35 {
36 waitUntil(condition, is(true));
37 }
38
39 /**
40 * Wait until given <tt>condition</tt> is <code>true</code> by default timeout, with custom error message.
41 *
42 * @param message error message
43 * @param condition condition to exercise
44 */
45 public static void waitUntilTrue(String message, TimedQuery<Boolean> condition)
46 {
47 waitUntil(message, condition, is(true));
48 }
49
50 /**
51 * Wait until given <tt>condition</tt> is <code>false</code> by default timeout
52 *
53 * @param condition condition to exercise
54 */
55 public static void waitUntilFalse(TimedQuery<Boolean> condition)
56 {
57 waitUntil(condition, is(false));
58 }
59
60 /**
61 * Wait until given <tt>condition</tt> is <code>false</code> by default timeout, with custom error message.
62 *
63 * @param message error message
64 * @param condition condition to exercise
65 */
66 public static void waitUntilFalse(String message, TimedQuery<Boolean> condition)
67 {
68 waitUntil(message, condition, is(false));
69 }
70
71 /**
72 * Wait until given <tt>query</tt> evaluates to actual value that is equal to <tt>expectedValue</tt>
73 * by default timeout.
74 *
75 * @param expectedValue expected value
76 * @param query query to evaluate
77 * @return last value from the query, satisfying the expected value
78 */
79 public static <T> T waitUntilEquals(T expectedValue, TimedQuery<T> query)
80 {
81 return waitUntil(query, equalTo(expectedValue));
82 }
83
84 /**
85 * Wait until given <tt>query</tt> evaluates to actual value that is equal to <tt>expectedValue</tt> by default timeout,
86 * with custom error message.
87 *
88 * @param message error message
89 * @param expectedValue expected value
90 * @param query query to evaluate
91 * @return last value from the query, satisfying the expected value
92 */
93 public static <T> T waitUntilEquals(String message, T expectedValue, TimedQuery<T> query)
94 {
95 return waitUntil(message, query, equalTo(expectedValue));
96 }
97
98 /**
99 * <p>
100 * Wait until given <tt>query</tt> fulfils certain condition specified by the <tt>matcher</tt>, by
101 * default timeout of the <tt>query</tt>.
102 *
103 * <p>
104 * Use any matcher from the available libraries (e.g. Hamcrest, JUnit etc.), or a custom one.
105 *
106 * @param query timed query to evaluate
107 * @param matcher a matcher representing the assertion condition
108 * @see Matcher
109 * @see org.hamcrest.Matchers
110 * @return last value from the query, satisfying the matcher
111 */
112 public static <T> T waitUntil(TimedQuery<T> query, Matcher<? super T> matcher)
113 {
114 return waitUntil(null, query, matcher, byDefaultTimeout());
115 }
116
117 /**
118 * <p>
119 * Wait until given <tt>query</tt> fulfils certain condition specified by the <tt>matcher</tt>, by
120 * default timeout of the <tt>query</tt>.
121 *
122 * <p>
123 * Use any matcher from the available libraries (e.g. Hamcrest, JUnit etc.), or a custom one.
124 *
125 * @param message message displayed in case of failure
126 * @param query timed query to evaluate
127 * @param matcher a matcher representing the assertion condition
128 * @see Matcher
129 * @see org.hamcrest.Matchers
130 * @return last value from the query, satisfying the matcher
131 */
132 public static <T> T waitUntil(String message, TimedQuery<T> query, Matcher<? super T> matcher)
133 {
134 return waitUntil(message, query, matcher, byDefaultTimeout());
135 }
136
137 /**
138 * <p>
139 * Wait until given <tt>query</tt> fulfils certain condition specified by given the <tt>matcher</tt>, by
140 * given <tt>timeout</tt>.
141 *
142 * <p>
143 * Use any matcher from the available libraries (e.g. Hamcrest, JUnit etc.), or a custom one.
144 *
145 * <p>
146 * To specify desired timeout, use one of four provided timeouts: {@link #now()}, {@link #byDefaultTimeout()},
147 * #{@link #by(long)}, {@link #by(long, java.util.concurrent.TimeUnit)}.
148 *
149 * @param query timed query to evaluate
150 * @param matcher a matcher representing the assertion condition
151 * @param timeout time limit for this wait
152 * @see Matcher
153 * @see org.hamcrest.Matchers
154 * @see #now()
155 * @see #by(long)
156 * @see #by(long, java.util.concurrent.TimeUnit)
157 * @see #byDefaultTimeout()
158 * @return last value from the query, satisfying the matcher
159 */
160 public static <T> T waitUntil(TimedQuery<T> query, Matcher<? super T> matcher, WaitTimeout timeout)
161 {
162 return waitUntil(null, query, matcher, timeout);
163 }
164
165
166 /**
167 * <p>
168 * Wait until given <tt>query</tt> fulfils certain condition specified by given the <tt>matcher</tt>, by
169 * given <tt>timeout</tt>.
170 *
171 * <p>
172 * Use any matcher from the libraries available (e.g. Hamcrest, JUnit etc.), or a custom one.
173 *
174 * <p>
175 * To specify desired timeout, use one of four provided timeouts: {@link #now()}, {@link #byDefaultTimeout()},
176 * #{@link #by(long)}, {@link #by(long, java.util.concurrent.TimeUnit)}.
177 *
178 * @param message message displayed for failed assertion
179 * @param query timed query to verify
180 * @param matcher a matcher representing the assertion condition
181 * @param timeout timeout of the assertion
182 * @see Matcher
183 * @see org.hamcrest.Matchers
184 * @see #now()
185 * @see #by(long)
186 * @see #by(long, java.util.concurrent.TimeUnit)
187 * @see #byDefaultTimeout()
188 * @return last value from the query, satisfying the matcher
189 */
190 public static <T> T waitUntil(String message, TimedQuery<T> query, Matcher<? super T> matcher, WaitTimeout timeout)
191 {
192 checkNotNull(timeout);
193 final Conditions.MatchingCondition<T> assertion = new Conditions.MatchingCondition<T>(query, matcher);
194 if (!timeout.evaluate(assertion))
195 {
196 throw new AssertionError(buildMessage(message, assertion, matcher, timeout));
197 }
198 return assertion.lastValue;
199 }
200
201 private static <T> String buildMessage(String message, Conditions.MatchingCondition<T> assertion,
202 Matcher<? super T> matcher, WaitTimeout timeout)
203 {
204 final Description answer = new StringDescription();
205 if (StringUtils.isNotEmpty(message))
206 {
207 answer.appendText(message).appendText(":\n");
208 }
209 return answer.appendText("Query ").appendValue(assertion.query).appendText("\nExpected: ")
210 .appendDescriptionOf(matcher).appendText(timeout.msgTimeoutSuffix(assertion))
211 .appendText("\nGot (last value): ").appendValue(assertion.lastValue).toString();
212 }
213
214
215
216
217 public static abstract class WaitTimeout
218 {
219 // we don't want this to be instantiated, or extended by anybody
220 private WaitTimeout() {}
221
222 abstract boolean evaluate(TimedCondition condition);
223
224 abstract String msgTimeoutSuffix(TimedCondition condition);
225 }
226
227
228 /**
229 * Timeout indicating that the assertion method will evaluate the assertion condition immediately, without waiting.
230 *
231 * @return new immediate assertion timeout
232 */
233 public static WaitTimeout now()
234 {
235 return new WaitTimeout()
236 {
237 @Override
238 boolean evaluate(final TimedCondition condition)
239 {
240 return condition.now();
241 }
242 @Override
243 String msgTimeoutSuffix(final TimedCondition condition)
244 {
245 return " immediately";
246 }
247 };
248 }
249
250 /**
251 * Timeout indicating that the assertion method will wait for the default timeout of exercised timed query for
252 * the condition to become <code>true</code>.
253 *
254 * @return new default assertion timeout
255 */
256 public static WaitTimeout byDefaultTimeout()
257 {
258 return new WaitTimeout()
259 {
260 @Override
261 boolean evaluate(final TimedCondition condition)
262 {
263 return condition.byDefaultTimeout();
264 }
265 @Override
266 String msgTimeoutSuffix(final TimedCondition condition)
267 {
268 return " by " + condition.defaultTimeout() + "ms (default timeout)";
269 }
270 };
271 }
272
273 /**
274 * Custom assertion timeout expressed in milliseconds. E.g. <code>TimedAssertions.by(500L);</code>
275 * passed to an assertion method will make it wait 500 milliseconds for given condition to become <code>true</code>.
276 *
277 * @param timeoutInMillis number of milliseconds to wait for the assertion condition
278 * @return new custom timeout assertion
279 */
280 public static WaitTimeout by(final long timeoutInMillis)
281 {
282 return new WaitTimeout()
283 {
284 @Override
285 boolean evaluate(final TimedCondition condition)
286 {
287 return condition.by(timeoutInMillis);
288 }
289 @Override
290 String msgTimeoutSuffix(final TimedCondition condition)
291 {
292 return " by " + timeoutInMillis + "ms";
293 }
294 };
295 }
296
297 /**
298 * Custom assertion timeout expressed in a number of time units. E.g. <code>TimedAssertions.by(5, TimeUnit.SECONDS);</code>
299 * passed to an assertion method will make it wait 5 seconds for given condition to become <code>true</code>.
300 *
301 * @param timeout timeout count
302 * @param unit unit of the timeout
303 * @return new custom timeout assertion
304 */
305 public static WaitTimeout by(final long timeout, final TimeUnit unit)
306 {
307 return new WaitTimeout()
308 {
309 @Override
310 boolean evaluate(final TimedCondition condition)
311 {
312 return condition.by(timeout, unit);
313 }
314 @Override
315 String msgTimeoutSuffix(final TimedCondition condition)
316 {
317 return " by " + unit.toMillis(timeout) + "ms";
318 }
319 };
320 }
321
322 }