2 Documentation Robot keyword library (Resource) with several Keywords for monitoring and waiting.
4 ... Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
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.
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.
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?
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.
35 Resource ${CURDIR}/ScalarClosures.robot
39 [Documentation] Call dependency setup. Perhaps needed.
40 ScalarClosures.SC_Setup
42 Limiting_Stability_Safe_Stateful_Validator_As_Keyword
43 [Arguments] ${old_state} ${data} ${valid_minimum}=-1
44 [Documentation] Report failure if minimum not reached or data value changed from last time. Useful to become validator.
45 ${new_state} = BuiltIn.Set_Variable ${data}
46 BuiltIn.Return_From_Keyword_If ${data} < ${valid_minimum} ${new_state} FAIL Minimum not reached.
47 BuiltIn.Return_From_Keyword_If ${data} != ${old_state} ${new_state} FAIL Data value has changed.
48 [Return] ${new_state} PASS Validated stable: ${data}
50 Create_Limiting_Stability_Safe_Stateful_Validator_From_Value_To_Overcome
51 [Arguments] ${maximum_invalid}=-1
52 [Documentation] Helper function to use if maximum invalid value (instead of minimum valid) is known.
53 ${valid_minimum} = BuiltIn.Evaluate str(int(${maximum_invalid}) + 1)
54 ${validator} = ScalarClosures.Closure_From_Keyword_And_Arguments WaitUtils.Limiting_Stability_Safe_Stateful_Validator_As_Keyword state_holder data_holder valid_minimum=${valid_minimum}
57 Excluding_Stability_Safe_Stateful_Validator_As_Keyword
58 [Arguments] ${old_state} ${data} ${excluded_value}=-1
59 [Documentation] Report failure if got the excluded value or if data value changed from last time. Useful to become validator.
60 ${new_state} = BuiltIn.Set_Variable ${data}
61 BuiltIn.Return_From_Keyword_If ${data} == ${excluded_value} ${new_state} FAIL Got the excluded value.
62 BuiltIn.Return_From_Keyword_If ${data} != ${old_state} ${new_state} FAIL Data value has changed.
63 [Return] ${new_state} PASS Validated stable: ${data}
65 WaitUtils__Check_Sanity_And_Compute_Derived_Times
66 [Arguments] ${timeout}=60s ${period}=1s ${count}=1
67 [Documentation] Common checks for argument values. Return times in seconds and deadline date implied by timeout time.
68 # Sanity check ${count}.
69 BuiltIn.Run_Keyword_If int(${count}) < 1 BuiltIn.Fail \${count} is ${count} and not at least 1.
70 # Sanity check ${period}.
71 ${period_in_seconds} = DateTime.Convert_Time ${period} result_format=number
72 BuiltIn.Run_Keyword_If ${period_in_seconds} <= 0.0 BuiltIn.Fail \${period} ${period} has to be positive.
73 # Figure out deadline.
74 ${date_now} = DateTime.Get_Current_Date
75 ${timeout_in_seconds} = DateTime.Convert_Time ${timeout} result_format=number
76 # In the following line, arguments have to be in order which is opposite to what name suggests.
77 ${date_deadline} = DateTime.Add_Time_To_Date ${date_now} ${timeout_in_seconds}
78 [Return] ${timeout_in_seconds} ${period_in_seconds} ${date_deadline}
80 WaitUtils__Is_Deadline_Reachable
81 [Arguments] ${date_deadline}=0 ${period_in_seconds}=1 ${sleeps_left}=1 ${message}=No attempt made.
82 [Documentation] Compute time to be wasted in sleeps, compare to deadline. Fail with message when needed.
83 # FIXME: Sensible default for deadline?
84 ${date_now} = DateTime.Get_Current_Date
85 ${time_deadline} = DateTime.Subtract_Date_From_Date ${date_deadline} ${date_now} result_format=number
86 ${time_minimal} = BuiltIn.Evaluate int(${sleeps_left}) * ${period_in_seconds}
87 BuiltIn.Run_Keyword_If ${time_minimal} >= ${time_deadline} BuiltIn.Fail Not possible to succeed within the deadline. ${message}
89 Wait_For_Getter_Failure_Or_Stateless_Validator_Pass
90 [Arguments] ${timeout}=60s ${period}=1s ${getter}=${ScalarClosures__fail} ${stateless_validator}=${ScalarClosures__identity}
91 [Documentation] Repeatedly run getter and plug its output to validator. If both pass, return validator message.
92 ... If getter fails, fail. If validator fails, repeat in WUKS fashion (fail when timeout is exceeded).
93 ... FIXME: Cover this keyword in WaitUtilTest.robot
94 ${timeout_in_seconds} ${period_in_seconds} ${date_deadline} = WaitUtils__Check_Sanity_And_Compute_Derived_Times timeout=${timeout} period=${period}
95 ${iterations} = BuiltIn.Evaluate ${timeout_in_seconds} / ${period_in_seconds}
96 FOR ${i} IN RANGE ${iterations}
97 ${data} = ScalarClosures.Run_Keyword_And_Collect_Garbage ScalarClosures.Run_Closure_As_Is ${getter}
98 ${status} ${message} = BuiltIn.Run_Keyword_And_Ignore_Error ScalarClosures.Run_Keyword_And_Collect_Garbage ScalarClosures.Run_Closure_After_Replacing_First_Argument ${stateless_validator}
100 BuiltIn.Return_From_Keyword_If "${status}" == "PASS" ${message}
101 WaitUtils__Is_Deadline_Reachable date_deadline=${date_deadline} period_in_seconds=${period_in_seconds} message=Last validator message: ${message}
102 BuiltIn.Sleep ${period_in_seconds} s
104 BuiltIn.Fail Logic error, we should have returned before.
106 Stateless_Assert_Closure_Has_To_Succeed_Consecutively_By_Deadline
107 [Arguments] ${date_deadline}=0 ${period_in_seconds}=1 ${count}=1 ${assertor}=${ScalarClosures__fail}
108 [Documentation] Pass only if \${assertor} passes ${count} times in a row with ${period_in_seconds} between attempts; less standard arguments.
109 ${result} = BuiltIn.Set_Variable No result yet.
110 # Do we have enough time to succeed?
111 ${sleeps} = BuiltIn.Evaluate ${count} - 1
112 WaitUtils__Is_Deadline_Reachable date_deadline=${date_deadline} period_in_seconds=${period_in_seconds} sleeps_left=${sleeps} message=Last result: ${result}
113 # Entering the main loop.
114 FOR ${sleeps_left} IN RANGE ${count}-1 -1 -1 # If count is 3, for will go through 2, 1, and 0.
115 # Run the assertor and collect the garbage.
116 ${result} = ScalarClosures.Run_Keyword_And_Collect_Garbage ScalarClosures.Run_Closure_As_Is ${assertor}
117 # We have not failed yet. Was this the final try?
118 BuiltIn.Return_From_Keyword_If ${sleeps_left} <= 0 ${result}
119 # Is there enough time left?
120 WaitUtils__Is_Deadline_Reachable date_deadline=${date_deadline} period_in_seconds=${period_in_seconds} sleeps_left=${sleeps_left} message=Last result: ${result}
121 # We will do next try, byt we have to sleep before.
122 BuiltIn.Sleep ${period_in_seconds} s
124 BuiltIn.Fail Logic error, we should have returned before.
126 Stateless_Assert_Closure_Has_To_Succeed_Consecutively
127 [Arguments] ${timeout}=60s ${period}=1s ${count}=1 ${assertor}=${ScalarClosures__fail}
128 [Documentation] Pass only if \${assertor} passes ${count} times in a row with ${period} between attempts; standard arguments.
129 # TODO: Put default values into variables for users to override at pybot invocation?
130 ${timeout_in_seconds} ${period_in_seconds} ${date_deadline} = WaitUtils__Check_Sanity_And_Compute_Derived_Times timeout=${timeout} period=${period} count=${count}
131 ${result} = Stateless_Assert_Closure_Has_To_Succeed_Consecutively_By_Deadline date_deadline=${date_deadline} period_in_seconds=${period_in_seconds} count=${count} assertor=${assertor}
134 Stateful_Assert_Closure_Has_To_Succeed_Consecutively_By_Deadline
135 [Arguments] ${date_deadline}=0 ${period_in_seconds}=1 ${count}=1 ${assertor}=${ScalarClosures__fail} ${initial_state}=${None}
136 [Documentation] Pass only if $\{assertor} passes ${count} times in a row with ${period} between attempts. Keep assertor state in local variable. Less standard arguments.
137 # TODO: Put default values into variables for users to override.
138 ${result} = BuiltIn.Set_Variable No result yet.
139 ${state} = BuiltIn.Set_Variable ${initial_state}
140 # Do we have enough time to succeed?
141 ${sleeps} = BuiltIn.Evaluate ${count} - 1
142 WaitUtils__Is_Deadline_Reachable date_deadline=${date_deadline} period_in_seconds=${period_in_seconds} sleeps_left=${sleeps} message=Last result: ${result}
143 # Entering the main loop.
144 FOR ${sleeps_left} IN RANGE ${count}-1 -1 -1
145 ${state} ${result} = ScalarClosures.Run_Keyword_And_Collect_Garbage ScalarClosures.Run_Closure_After_Replacing_First_Argument ${assertor} ${state}
146 # We have not failed yet. Was this the final try?
147 BuiltIn.Return_From_Keyword_If ${sleeps_left} <= 0 ${result}
148 # Is there enough time left?
149 WaitUtils__Is_Deadline_Reachable date_deadline=${date_deadline} period_in_seconds=${period_in_seconds} sleeps_left=${sleeps_left} message=Last result: ${result}
150 # We will do next try, byt we have to sleep before.
151 BuiltIn.Sleep ${period_in_seconds} s
153 BuiltIn.Fail Logic error, we should have returned before.
155 Stateful_Assert_Closure_Has_To_Succeed_Consecutively
156 [Arguments] ${timeout}=60s ${period}=1s ${count}=1 ${assertor}=${ScalarClosures__fail} ${initial_state}=${NONE}
157 [Documentation] Pass only if \${assertor} passes ${count} times in a row with ${period} between attempts. Keep assertor state in local variable. Standard arguments.
158 # TODO: Put default values into variables for users to override.
159 ${timeout_in_seconds} ${period_in_seconds} ${date_deadline} = WaitUtils__Check_Sanity_And_Compute_Derived_Times timeout=${timeout} period=${period} count=${count}
160 ${result} = Stateful_Assert_Closure_Has_To_Succeed_Consecutively_By_Deadline date_deadline=${date_deadline} period_in_seconds=${period_in_seconds} count=${count} assertor=${assertor} initial_state=${initial_state}
163 Getter_And_Safe_Stateful_Validator_Have_To_Succeed_Consecutively_By_Deadline
164 [Arguments] ${date_deadline}=0 ${period_in_seconds}=1 ${count}=1 ${getter}=${ScalarClosures__fail} ${safe_validator}=${ScalarClosures__fail} ${initial_state}=${NONE}
165 [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.
166 ${result} = BuiltIn.Set_Variable No result yet.
167 ${state} = BuiltIn.Set_Variable ${initial_state}
168 # Do we have enough time to succeed?
169 ${sleeps} = BuiltIn.Evaluate ${count} - 1
170 ${status} ${message} = BuiltIn.Run_Keyword_And_Ignore_Error WaitUtils__Is_Deadline_Reachable date_deadline=${date_deadline} period_in_seconds=${period_in_seconds} sleeps_left=${sleeps}
171 ... message=Last result: ${result}
172 BuiltIn.Return_From_Keyword_If '''${status}''' != '''PASS''' ${state} ${status} ${message}
173 # Entering the main loop.
174 FOR ${sleeps_left} IN RANGE ${count}-1 -1 -1
175 # Getter may fail, but this Keyword should return state, so we need RKAIE.
176 ${status} ${data} = BuiltIn.Run_Keyword_And_Ignore_Error ScalarClosures.Run_Keyword_And_Collect_Garbage ScalarClosures.Run_Closure_As_Is ${getter}
177 BuiltIn.Return_From_Keyword_If '''${status}''' != '''PASS''' ${state} ${status} Getter failed: ${data}
178 # Is there enough time left?
179 ${status} ${message} = BuiltIn.Run_Keyword_And_Ignore_Error WaitUtils__Is_Deadline_Reachable date_deadline=${date_deadline} period_in_seconds=${period_in_seconds}
180 ... sleeps_left=${sleeps_left} message=Last result: ${result}
181 BuiltIn.Return_From_Keyword_If '''${status}''' != '''PASS''' ${state} ${status} ${message}
182 ${state} ${status} ${result} = ScalarClosures.Run_Keyword_And_Collect_Garbage ScalarClosures.Run_Closure_After_Replacing_First_Two_Arguments ${safe_validator}
184 # Validator may have reported failure.
185 BuiltIn.Return_From_Keyword_If '''${status}''' != '''PASS''' ${state} ${status} Validator failed: ${result}
186 # Was this the final try?
187 BuiltIn.Return_From_Keyword_If ${sleeps_left} <= 0 ${state} ${status} ${result}
188 # Is there enough time left?
189 ${status} ${message} = BuiltIn.Run_Keyword_And_Ignore_Error WaitUtils__Is_Deadline_Reachable date_deadline=${date_deadline} period_in_seconds=${period_in_seconds}
190 ... sleeps_left=${sleeps_left} message=Last result: ${result}
191 BuiltIn.Return_From_Keyword_If '''${status}''' != '''PASS''' ${state} ${status} ${message}
192 # We will do next try, byt we have to sleep before.
193 BuiltIn.Sleep ${period_in_seconds} s
195 BuiltIn.Fail Logic error, we should have returned before.
197 Propagate_Fail_If_Message_Starts_With_Prefix
198 [Arguments] ${message}=${EMPTY} ${prefix}=magic
199 [Documentation] Helper keyword to distinguish escalable failures by their prefix. If it is escalable, Fail without changing the message; otherwise Return comment.
200 # TODO: Move to a more appropriate Resource.
201 # Empty message cannot fit prefix.
202 ${status} ${result} = BuiltIn.Run_Keyword_And_Ignore_Error BuiltIn.Should_Be_Empty ${message}
203 BuiltIn.Return_From_Keyword_If '${status}' == 'PASS' Got empty message.
204 # Is there anything except the prefix?
205 @{message_chunks}= String.Split_String ${message} ${prefix}
206 # If there is something at the first chunk, the prefix was not at start.
207 ${status} ${result} = BuiltIn.Run_Keyword_And_Ignore_Error BuiltIn.Should_Be_Empty ${message_chunks[0]}
208 BuiltIn.Return_From_Keyword_If '${status}' != 'PASS' ${message} does not start with ${prefix}
209 # We got the fail to propagate
210 BuiltIn.Fail ${message}
212 Wait_For_Getter_And_Safe_Stateful_Validator_Consecutive_Success
213 [Arguments] ${timeout}=60s ${period}=1s ${count}=1 ${getter}=${ScalarClosures__fail} ${safe_validator}=${ScalarClosures__fail} ${initial_state}=${NONE}
214 [Documentation] Analogue of Wait Until Keyword Succeeds, but it passes state of validator around. Calls GASSVHTSCBD to verify data is "stable".
215 # FIXME: Document that Safe Stateful Validator has to return state, status and message (and never fail)
216 ${timeout_in_seconds} ${period_in_seconds} ${date_deadline} = WaitUtils__Check_Sanity_And_Compute_Derived_Times timeout=${timeout} period=${period} count=${count}
217 # Maximum number of sleeps. TODO: Move to separate Keyword?
218 ${maximum_sleeps} = BuiltIn.Evaluate math.ceil(${timeout_in_seconds} / ${period_in_seconds}) + 1 modules=math
219 ${result} = BuiltIn.Set_Variable No result yet.
220 ${state} = BuiltIn.Set_Variable ${initial_state}
221 # The loop for failures.
222 FOR ${try} IN RANGE 1 ${maximum_sleeps}+2 # If maximum_sleeps is 2, for will go through 1, 2, and 3.
223 ${state} ${status} ${result} = Getter_And_Safe_Stateful_Validator_Have_To_Succeed_Consecutively_By_Deadline date_deadline=${date_deadline} period_in_seconds=${period_in_seconds}
224 ... count=${count} getter=${getter} safe_validator=${safe_validator} initial_state=${state}
226 BuiltIn.Return_From_Keyword_If '''${status}''' == '''PASS''' ${result}
227 # Are we out of time?
228 Propagate_Fail_If_Message_Starts_With_Prefix ${result} Not possible to succeed within the deadline.
229 # We will do next try, but we have to sleep before.
230 BuiltIn.Sleep ${period_in_seconds} s
232 BuiltIn.Fail Logic error, we should have returned before.
234 Wait_For_Getter_Error_Or_Safe_Stateful_Validator_Consecutive_Success
235 [Arguments] ${timeout}=60s ${period}=1s ${count}=1 ${getter}=${ScalarClosures__fail} ${safe_validator}=${ScalarClosures__fail} ${initial_state}=${NONE}
236 [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".
237 # If this ever fails, we want to know the exact inputs passed to it.
238 ${tmp}= BuiltIn.Evaluate int(${count})
239 BuiltIn.Log count=${tmp}
240 ${timeout_in_seconds} ${period_in_seconds} ${date_deadline} = WaitUtils__Check_Sanity_And_Compute_Derived_Times timeout=${timeout} period=${period} count=${count}
241 # Maximum number of sleeps. TODO: Move to separate Keyword or add into CSACDT?
242 ${maximum_sleeps} = BuiltIn.Evaluate math.ceil(${timeout_in_seconds} / ${period_in_seconds}) modules=math
243 ${result} = BuiltIn.Set_Variable No result yet.
244 ${state} = BuiltIn.Set_Variable ${initial_state}
245 # The loop for failures.
246 FOR ${try} IN RANGE 1 ${maximum_sleeps}+2 # If maximum_sleeps is 2, for will go through 1, 2, and 3.
247 ${state} ${status} ${result} = Getter_And_Safe_Stateful_Validator_Have_To_Succeed_Consecutively_By_Deadline date_deadline=${date_deadline} period_in_seconds=${period_in_seconds}
248 ... count=${count} getter=${getter} safe_validator=${safe_validator} initial_state=${state}
250 BuiltIn.Return_From_Keyword_If '''${status}''' == '''PASS''' ${result}
251 # Are we out of time? Look at ${result}.
252 Propagate_Fail_If_Message_Starts_With_Prefix ${result} Not possible to succeed within the deadline.
253 # Now check for getter error, by analysing ${result}.
254 Propagate_Fail_If_Message_Starts_With_Prefix ${result} Getter failed
255 # We can do the next try, byt we have to sleep before.
256 BuiltIn.Sleep ${period_in_seconds} s
258 BuiltIn.Fail Logic error, we should have returned before.