View Javadoc

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 }