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