Migrate Get Requests invocations(libraries)
[integration/test.git] / csit / libraries / WaitUtils.robot
1 *** Settings ***
2 Documentation       Robot keyword library (Resource) with several Keywords for monitoring and waiting.
3 ...
4 ...                 Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
5 ...
6 ...                 This program and the accompanying materials are made available under the
7 ...                 terms of the Eclipse Public License v1.0 which accompanies this distribution,
8 ...                 and is available at http://www.eclipse.org/legal/epl-v10.html
9 ...
10 ...
11 ...                 BuiltIn.Wait_Until_Keyword_Succeeds has two possible results: Fast pass or fail on timeout.
12 ...                 Generally, keywords in this Resource also allow for some kind of fast failure condition.
13 ...                 This usually requires more than a single keyword to run inside the iteration loop.
14 ...                 This library uses ScalarClosures for plugging in specific (multiple) Keywords.
15 ...
16 ...                 Storing private state in suite variables is easy, but it can lead to hard-to-debug issues,
17 ...                 so this library tries to support explicit state passing.
18 ...                 Unfortunately, failing limits type of message to return,
19 ...                 so implementation of some Keywords looks quite convoluted.
20 ...
21 ...                 Particular closures are to be given by caller:
22 ...                 Stateless Assertor: Take no arguments. Return comment or Fail with message.
23 ...                 Stateful Assertor: Take single ${state} argument. Return new state and comment, or Fail with message.
24 ...                 (Stateless) Getter: Take no argument. Return single scalar data, or Fail with message.
25 ...                 Stateless Validator: Take single ${data} argument. Return comment, or Fail with message.
26 ...                 (Unsafe) Stateful Validator: Take ${state} and ${data} arguments. Return new state and comment, or Fail with message.
27 ...                 Safe Stateful Validator: Take ${state} and ${data} arguments. Return new state, validation status and comment/message.
28 ...                 TODO: Create a dummy closure for each type to be used as default value?
29 ...
30 ...                 TODO: Figure out a way to merge this with WaitForFailure.robot
31 ...                 TODO: Add Keywords that are Safe (return state, success and message)
32 ...                 so that callers do not need to refresh state explicitly.
33
34 Library             DateTime
35 Library             String
36 Resource            ${CURDIR}/ScalarClosures.robot
37
38
39 *** Keywords ***
40 WU_Setup
41     [Documentation]    Call dependency setup. Perhaps needed.
42     ScalarClosures.SC_Setup
43
44 Limiting_Stability_Safe_Stateful_Validator_As_Keyword
45     [Documentation]    Report failure if minimum not reached or data value changed from last time. Useful to become validator.
46     [Arguments]    ${old_state}    ${data}    ${valid_minimum}=-1
47     ${new_state} =    BuiltIn.Set_Variable    ${data}
48     IF    ${data} < ${valid_minimum}
49         RETURN    ${new_state}    FAIL    Minimum not reached.
50     END
51     IF    ${data} != ${old_state}
52         RETURN    ${new_state}    FAIL    Data value has changed.
53     END
54     RETURN    ${new_state}    PASS    Validated stable: ${data}
55
56 Create_Limiting_Stability_Safe_Stateful_Validator_From_Value_To_Overcome
57     [Documentation]    Helper function to use if maximum invalid value (instead of minimum valid) is known.
58     [Arguments]    ${maximum_invalid}=-1
59     ${valid_minimum} =    BuiltIn.Evaluate    str(int(${maximum_invalid}) + 1)
60     ${validator} =    ScalarClosures.Closure_From_Keyword_And_Arguments
61     ...    WaitUtils.Limiting_Stability_Safe_Stateful_Validator_As_Keyword
62     ...    state_holder
63     ...    data_holder
64     ...    valid_minimum=${valid_minimum}
65     RETURN    ${validator}
66
67 Excluding_Stability_Safe_Stateful_Validator_As_Keyword
68     [Documentation]    Report failure if got the excluded value or if data value changed from last time. Useful to become validator.
69     [Arguments]    ${old_state}    ${data}    ${excluded_value}=-1
70     ${new_state} =    BuiltIn.Set_Variable    ${data}
71     IF    ${data} == ${excluded_value}
72         RETURN    ${new_state}    FAIL    Got the excluded value.
73     END
74     IF    ${data} != ${old_state}
75         RETURN    ${new_state}    FAIL    Data value has changed.
76     END
77     RETURN    ${new_state}    PASS    Validated stable: ${data}
78
79 WaitUtils__Check_Sanity_And_Compute_Derived_Times
80     [Documentation]    Common checks for argument values. Return times in seconds and deadline date implied by timeout time.
81     [Arguments]    ${timeout}=60s    ${period}=1s    ${count}=1
82     # Sanity check ${count}.
83     IF    int(${count}) < 1
84         BuiltIn.Fail    \${count} is ${count} and not at least 1.
85     END
86     # Sanity check ${period}.
87     ${period_in_seconds} =    DateTime.Convert_Time    ${period}    result_format=number
88     IF    ${period_in_seconds} <= 0.0
89         BuiltIn.Fail    \${period} ${period} has to be positive.
90     END
91     # Figure out deadline.
92     ${date_now} =    DateTime.Get_Current_Date
93     ${timeout_in_seconds} =    DateTime.Convert_Time    ${timeout}    result_format=number
94     # In the following line, arguments have to be in order which is opposite to what name suggests.
95     ${date_deadline} =    DateTime.Add_Time_To_Date    ${date_now}    ${timeout_in_seconds}
96     RETURN    ${timeout_in_seconds}    ${period_in_seconds}    ${date_deadline}
97
98 WaitUtils__Is_Deadline_Reachable
99     [Documentation]    Compute time to be wasted in sleeps, compare to deadline. Fail with message when needed.
100     [Arguments]    ${date_deadline}=0    ${period_in_seconds}=1    ${sleeps_left}=1    ${message}=No attempt made.
101     # FIXME: Sensible default for deadline?
102     ${date_now} =    DateTime.Get_Current_Date
103     ${time_deadline} =    DateTime.Subtract_Date_From_Date    ${date_deadline}    ${date_now}    result_format=number
104     ${time_minimal} =    BuiltIn.Evaluate    int(${sleeps_left}) * ${period_in_seconds}
105     IF    ${time_minimal} >= ${time_deadline}
106         BuiltIn.Fail    Not possible to succeed within the deadline. ${message}
107     END
108
109 Wait_For_Getter_Failure_Or_Stateless_Validator_Pass
110     [Documentation]    Repeatedly run getter and plug its output to validator. If both pass, return validator message.
111     ...    If getter fails, fail. If validator fails, repeat in WUKS fashion (fail when timeout is exceeded).
112     ...    FIXME: Cover this keyword in WaitUtilTest.robot
113     [Arguments]    ${timeout}=60s    ${period}=1s    ${getter}=${ScalarClosures__fail}    ${stateless_validator}=${ScalarClosures__identity}
114     ${timeout_in_seconds}
115     ...    ${period_in_seconds}
116     ...    ${date_deadline} =
117     ...    WaitUtils__Check_Sanity_And_Compute_Derived_Times
118     ...    timeout=${timeout}
119     ...    period=${period}
120     ${iterations} =    BuiltIn.Evaluate    ${timeout_in_seconds} / ${period_in_seconds}
121     FOR    ${i}    IN RANGE    ${iterations}
122         ${data} =    ScalarClosures.Run_Keyword_And_Collect_Garbage    ScalarClosures.Run_Closure_As_Is    ${getter}
123         ${status}    ${message} =    BuiltIn.Run_Keyword_And_Ignore_Error
124         ...    ScalarClosures.Run_Keyword_And_Collect_Garbage
125         ...    ScalarClosures.Run_Closure_After_Replacing_First_Argument
126         ...    ${stateless_validator}
127         ...    ${data}
128         IF    "${status}" == "PASS"    RETURN    ${message}
129         WaitUtils__Is_Deadline_Reachable
130         ...    date_deadline=${date_deadline}
131         ...    period_in_seconds=${period_in_seconds}
132         ...    message=Last validator message: ${message}
133         BuiltIn.Sleep    ${period_in_seconds} s
134     END
135     BuiltIn.Fail    Logic error, we should have returned before.
136
137 Stateless_Assert_Closure_Has_To_Succeed_Consecutively_By_Deadline
138     [Documentation]    Pass only if \${assertor} passes ${count} times in a row with ${period_in_seconds} between attempts; less standard arguments.
139     [Arguments]    ${date_deadline}=0    ${period_in_seconds}=1    ${count}=1    ${assertor}=${ScalarClosures__fail}
140     ${result} =    BuiltIn.Set_Variable    No result yet.
141     # Do we have enough time to succeed?
142     ${sleeps} =    BuiltIn.Evaluate    ${count} - 1
143     WaitUtils__Is_Deadline_Reachable
144     ...    date_deadline=${date_deadline}
145     ...    period_in_seconds=${period_in_seconds}
146     ...    sleeps_left=${sleeps}
147     ...    message=Last result: ${result}
148     # Entering the main loop.
149     FOR    ${sleeps_left}    IN RANGE    ${count}-1    -1    -1    # If count is 3, for will go through 2, 1, and 0.
150         # Run the assertor and collect the garbage.
151         ${result} =    ScalarClosures.Run_Keyword_And_Collect_Garbage
152         ...    ScalarClosures.Run_Closure_As_Is
153         ...    ${assertor}
154         # We have not failed yet. Was this the final try?
155         IF    ${sleeps_left} <= 0    RETURN    ${result}
156         # Is there enough time left?
157         WaitUtils__Is_Deadline_Reachable
158         ...    date_deadline=${date_deadline}
159         ...    period_in_seconds=${period_in_seconds}
160         ...    sleeps_left=${sleeps_left}
161         ...    message=Last result: ${result}
162         # We will do next try, byt we have to sleep before.
163         BuiltIn.Sleep    ${period_in_seconds} s
164     END
165     BuiltIn.Fail    Logic error, we should have returned before.
166
167 Stateless_Assert_Closure_Has_To_Succeed_Consecutively
168     [Documentation]    Pass only if \${assertor} passes ${count} times in a row with ${period} between attempts; standard arguments.
169     [Arguments]    ${timeout}=60s    ${period}=1s    ${count}=1    ${assertor}=${ScalarClosures__fail}
170     # TODO: Put default values into variables for users to override at pybot invocation?
171     ${timeout_in_seconds}
172     ...    ${period_in_seconds}
173     ...    ${date_deadline} =
174     ...    WaitUtils__Check_Sanity_And_Compute_Derived_Times
175     ...    timeout=${timeout}
176     ...    period=${period}
177     ...    count=${count}
178     ${result} =    Stateless_Assert_Closure_Has_To_Succeed_Consecutively_By_Deadline
179     ...    date_deadline=${date_deadline}
180     ...    period_in_seconds=${period_in_seconds}
181     ...    count=${count}
182     ...    assertor=${assertor}
183     RETURN    ${result}
184
185 Stateful_Assert_Closure_Has_To_Succeed_Consecutively_By_Deadline
186     [Documentation]    Pass only if $\{assertor} passes ${count} times in a row with ${period} between attempts. Keep assertor state in local variable. Less standard arguments.
187     [Arguments]    ${date_deadline}=0    ${period_in_seconds}=1    ${count}=1    ${assertor}=${ScalarClosures__fail}    ${initial_state}=${None}
188     # TODO: Put default values into variables for users to override.
189     ${result} =    BuiltIn.Set_Variable    No result yet.
190     ${state} =    BuiltIn.Set_Variable    ${initial_state}
191     # Do we have enough time to succeed?
192     ${sleeps} =    BuiltIn.Evaluate    ${count} - 1
193     WaitUtils__Is_Deadline_Reachable
194     ...    date_deadline=${date_deadline}
195     ...    period_in_seconds=${period_in_seconds}
196     ...    sleeps_left=${sleeps}
197     ...    message=Last result: ${result}
198     # Entering the main loop.
199     FOR    ${sleeps_left}    IN RANGE    ${count}-1    -1    -1
200         ${state}    ${result} =    ScalarClosures.Run_Keyword_And_Collect_Garbage
201         ...    ScalarClosures.Run_Closure_After_Replacing_First_Argument
202         ...    ${assertor}
203         ...    ${state}
204         # We have not failed yet. Was this the final try?
205         IF    ${sleeps_left} <= 0    RETURN    ${result}
206         # Is there enough time left?
207         WaitUtils__Is_Deadline_Reachable
208         ...    date_deadline=${date_deadline}
209         ...    period_in_seconds=${period_in_seconds}
210         ...    sleeps_left=${sleeps_left}
211         ...    message=Last result: ${result}
212         # We will do next try, byt we have to sleep before.
213         BuiltIn.Sleep    ${period_in_seconds} s
214     END
215     BuiltIn.Fail    Logic error, we should have returned before.
216
217 Stateful_Assert_Closure_Has_To_Succeed_Consecutively
218     [Documentation]    Pass only if \${assertor} passes ${count} times in a row with ${period} between attempts. Keep assertor state in local variable. Standard arguments.
219     [Arguments]    ${timeout}=60s    ${period}=1s    ${count}=1    ${assertor}=${ScalarClosures__fail}    ${initial_state}=${NONE}
220     # TODO: Put default values into variables for users to override.
221     ${timeout_in_seconds}
222     ...    ${period_in_seconds}
223     ...    ${date_deadline} =
224     ...    WaitUtils__Check_Sanity_And_Compute_Derived_Times
225     ...    timeout=${timeout}
226     ...    period=${period}
227     ...    count=${count}
228     ${result} =    Stateful_Assert_Closure_Has_To_Succeed_Consecutively_By_Deadline
229     ...    date_deadline=${date_deadline}
230     ...    period_in_seconds=${period_in_seconds}
231     ...    count=${count}
232     ...    assertor=${assertor}
233     ...    initial_state=${initial_state}
234     RETURN    ${result}
235
236 Getter_And_Safe_Stateful_Validator_Have_To_Succeed_Consecutively_By_Deadline
237     [Documentation]    Pass only if consecutively ${count} times in a row with ${period} between attempts: \${getter} creates data and \${safe_validator} passes. Validator updates its state even if it reports failure. Always return validator state, status and message.
238     [Arguments]    ${date_deadline}=0    ${period_in_seconds}=1    ${count}=1    ${getter}=${ScalarClosures__fail}    ${safe_validator}=${ScalarClosures__fail}    ${initial_state}=${NONE}
239     ${result} =    BuiltIn.Set_Variable    No result yet.
240     ${state} =    BuiltIn.Set_Variable    ${initial_state}
241     # Do we have enough time to succeed?
242     ${sleeps} =    BuiltIn.Evaluate    ${count} - 1
243     ${status}    ${message} =    BuiltIn.Run_Keyword_And_Ignore_Error
244     ...    WaitUtils__Is_Deadline_Reachable
245     ...    date_deadline=${date_deadline}
246     ...    period_in_seconds=${period_in_seconds}
247     ...    sleeps_left=${sleeps}
248     ...    message=Last result: ${result}
249     IF    '''${status}''' != '''PASS'''
250         RETURN    ${state}    ${status}    ${message}
251     END
252     # Entering the main loop.
253     FOR    ${sleeps_left}    IN RANGE    ${count}-1    -1    -1
254         # Getter may fail, but this Keyword should return state, so we need RKAIE.
255         ${status}    ${data} =    BuiltIn.Run_Keyword_And_Ignore_Error
256         ...    ScalarClosures.Run_Keyword_And_Collect_Garbage
257         ...    ScalarClosures.Run_Closure_As_Is
258         ...    ${getter}
259         IF    '''${status}''' != '''PASS'''
260             RETURN    ${state}    ${status}    Getter failed: ${data}
261         END
262         # Is there enough time left?
263         ${status}    ${message} =    BuiltIn.Run_Keyword_And_Ignore_Error
264         ...    WaitUtils__Is_Deadline_Reachable
265         ...    date_deadline=${date_deadline}
266         ...    period_in_seconds=${period_in_seconds}
267         ...    sleeps_left=${sleeps_left}
268         ...    message=Last result: ${result}
269         IF    '''${status}''' != '''PASS'''
270             RETURN    ${state}    ${status}    ${message}
271         END
272         ${state}    ${status}    ${result} =    ScalarClosures.Run_Keyword_And_Collect_Garbage
273         ...    ScalarClosures.Run_Closure_After_Replacing_First_Two_Arguments
274         ...    ${safe_validator}
275         ...    ${state}
276         ...    ${data}
277         # Validator may have reported failure.
278         IF    '''${status}''' != '''PASS'''
279             RETURN    ${state}    ${status}    Validator failed: ${result}
280         END
281         # Was this the final try?
282         IF    ${sleeps_left} <= 0
283             RETURN    ${state}    ${status}    ${result}
284         END
285         # Is there enough time left?
286         ${status}    ${message} =    BuiltIn.Run_Keyword_And_Ignore_Error
287         ...    WaitUtils__Is_Deadline_Reachable
288         ...    date_deadline=${date_deadline}
289         ...    period_in_seconds=${period_in_seconds}
290         ...    sleeps_left=${sleeps_left}
291         ...    message=Last result: ${result}
292         IF    '''${status}''' != '''PASS'''
293             RETURN    ${state}    ${status}    ${message}
294         END
295         # We will do next try, byt we have to sleep before.
296         BuiltIn.Sleep    ${period_in_seconds} s
297     END
298     BuiltIn.Fail    Logic error, we should have returned before.
299
300 Propagate_Fail_If_Message_Starts_With_Prefix
301     [Documentation]    Helper keyword to distinguish escalable failures by their prefix. If it is escalable, Fail without changing the message; otherwise Return comment.
302     [Arguments]    ${message}=${EMPTY}    ${prefix}=magic
303     # TODO: Move to a more appropriate Resource.
304     # Empty message cannot fit prefix.
305     ${status}    ${result} =    BuiltIn.Run_Keyword_And_Ignore_Error    BuiltIn.Should_Be_Empty    ${message}
306     IF    '${status}' == 'PASS'    RETURN    Got empty message.
307     # Is there anything except the prefix?
308     @{message_chunks} =    String.Split_String    ${message}    ${prefix}
309     # If there is something at the first chunk, the prefix was not at start.
310     ${status}    ${result} =    BuiltIn.Run_Keyword_And_Ignore_Error    BuiltIn.Should_Be_Empty    ${message_chunks[0]}
311     IF    '${status}' != 'PASS'
312         RETURN    ${message} does not start with ${prefix}
313     END
314     # We got the fail to propagate
315     BuiltIn.Fail    ${message}
316
317 Wait_For_Getter_And_Safe_Stateful_Validator_Consecutive_Success
318     [Documentation]    Analogue of Wait Until Keyword Succeeds, but it passes state of validator around. Calls GASSVHTSCBD to verify data is "stable".
319     [Arguments]    ${timeout}=60s    ${period}=1s    ${count}=1    ${getter}=${ScalarClosures__fail}    ${safe_validator}=${ScalarClosures__fail}    ${initial_state}=${NONE}
320     # FIXME: Document that Safe Stateful Validator has to return state, status and message (and never fail)
321     ${timeout_in_seconds}
322     ...    ${period_in_seconds}
323     ...    ${date_deadline} =
324     ...    WaitUtils__Check_Sanity_And_Compute_Derived_Times
325     ...    timeout=${timeout}
326     ...    period=${period}
327     ...    count=${count}
328     # Maximum number of sleeps. TODO: Move to separate Keyword?
329     ${maximum_sleeps} =    BuiltIn.Evaluate
330     ...    math.ceil(${timeout_in_seconds} / ${period_in_seconds}) + 1
331     ...    modules=math
332     ${result} =    BuiltIn.Set_Variable    No result yet.
333     ${state} =    BuiltIn.Set_Variable    ${initial_state}
334     # The loop for failures.
335     FOR    ${try}    IN RANGE    1    ${maximum_sleeps}+2    # If maximum_sleeps is 2, for will go through 1, 2, and 3.
336         ${state}
337         ...    ${status}
338         ...    ${result} =
339         ...    Getter_And_Safe_Stateful_Validator_Have_To_Succeed_Consecutively_By_Deadline
340         ...    date_deadline=${date_deadline}
341         ...    period_in_seconds=${period_in_seconds}
342         ...    count=${count}
343         ...    getter=${getter}
344         ...    safe_validator=${safe_validator}
345         ...    initial_state=${state}
346         # Have we passed?
347         IF    '''${status}''' == '''PASS'''    RETURN    ${result}
348         # Are we out of time?
349         Propagate_Fail_If_Message_Starts_With_Prefix    ${result}    Not possible to succeed within the deadline.
350         # We will do next try, but we have to sleep before.
351         BuiltIn.Sleep    ${period_in_seconds} s
352     END
353     BuiltIn.Fail    Logic error, we should have returned before.
354
355 Wait_For_Getter_Error_Or_Safe_Stateful_Validator_Consecutive_Success
356     [Documentation]    Analogue of Wait Until Keyword Succeeds, but it passes state of validator around and exits early on getter failure. Calls GASSVHTSCBD to verify data is "stable".
357     [Arguments]    ${timeout}=60s    ${period}=1s    ${count}=1    ${getter}=${ScalarClosures__fail}    ${safe_validator}=${ScalarClosures__fail}    ${initial_state}=${NONE}
358     # If this ever fails, we want to know the exact inputs passed to it.
359     ${tmp} =    BuiltIn.Evaluate    int(${count})
360     BuiltIn.Log    count=${tmp}
361     ${timeout_in_seconds}
362     ...    ${period_in_seconds}
363     ...    ${date_deadline} =
364     ...    WaitUtils__Check_Sanity_And_Compute_Derived_Times
365     ...    timeout=${timeout}
366     ...    period=${period}
367     ...    count=${count}
368     # Maximum number of sleeps. TODO: Move to separate Keyword or add into CSACDT?
369     ${maximum_sleeps} =    BuiltIn.Evaluate    math.ceil(${timeout_in_seconds} / ${period_in_seconds})    modules=math
370     ${result} =    BuiltIn.Set_Variable    No result yet.
371     ${state} =    BuiltIn.Set_Variable    ${initial_state}
372     # The loop for failures.
373     FOR    ${try}    IN RANGE    1    ${maximum_sleeps}+2    # If maximum_sleeps is 2, for will go through 1, 2, and 3.
374         ${state}
375         ...    ${status}
376         ...    ${result} =
377         ...    Getter_And_Safe_Stateful_Validator_Have_To_Succeed_Consecutively_By_Deadline
378         ...    date_deadline=${date_deadline}
379         ...    period_in_seconds=${period_in_seconds}
380         ...    count=${count}
381         ...    getter=${getter}
382         ...    safe_validator=${safe_validator}
383         ...    initial_state=${state}
384         # Have we passed?
385         IF    '''${status}''' == '''PASS'''    RETURN    ${result}
386         # Are we out of time? Look at ${result}.
387         Propagate_Fail_If_Message_Starts_With_Prefix    ${result}    Not possible to succeed within the deadline.
388         # Now check for getter error, by analysing ${result}.
389         Propagate_Fail_If_Message_Starts_With_Prefix    ${result}    Getter failed
390         # We can do the next try, byt we have to sleep before.
391         BuiltIn.Sleep    ${period_in_seconds} s
392     END
393     BuiltIn.Fail    Logic error, we should have returned before.