@@ -107,7 +107,6 @@ def process_test_suite(self, in_suite, *, seed='new'):
107107
108108 def _try_to_reach_full_coverage (self , allow_duplicate_scenarios ):
109109 tracestate = TraceState (self .shuffled )
110- refinement_stack = []
111110 while not tracestate .coverage_reached ():
112111 candidate_id = tracestate .next_candidate (retry = allow_duplicate_scenarios )
113112 if candidate_id is None : # No more candidates remaining for this level
@@ -122,7 +121,7 @@ def _try_to_reach_full_coverage(self, allow_duplicate_scenarios):
122121 tracestate .reject_scenario (candidate_id )
123122 continue
124123 previous_len = len (tracestate )
125- self ._try_to_fit_in_scenario (candidate , tracestate , refinement_stack )
124+ self ._try_to_fit_in_scenario (candidate , tracestate )
126125 if len (tracestate ) > previous_len :
127126 self .DROUGHT_LIMIT = 50
128127 if self .__last_candidate_changed_nothing (tracestate ):
@@ -146,11 +145,10 @@ def __last_candidate_changed_nothing(tracestate):
146145
147146 def _select_scenario_variant (self , candidate_id , tracestate ):
148147 candidate = self ._scenario_with_repeat_counter (candidate_id , tracestate )
149- scenarios_in_refinement = tracestate .find_scenarios_with_active_refinement ()
150- if candidate_id in [s .src_id for s in scenarios_in_refinement ]:
148+ if candidate_id in tracestate .active_refinements :
151149 # reuse previous solution for all parts in split-up scenario
152150 candidate = candidate .copy ()
153- candidate .data_choices = scenarios_in_refinement [ candidate_id ] .data_choices .copy ()
151+ candidate .data_choices = tracestate . get_remainder ( candidate_id ) .data_choices .copy ()
154152 else :
155153 candidate = self ._generate_scenario_variant (candidate , tracestate .model or ModelSpace ())
156154 return candidate
@@ -176,38 +174,36 @@ def _fail_on_step_errors(suite):
176174 for s in error_list ])
177175 raise Exception (err_msg )
178176
179- def _try_to_fit_in_scenario (self , candidate , tracestate , refinement_stack ):
177+ def _try_to_fit_in_scenario (self , candidate , tracestate ):
180178 """
181179 Tries to insert the candidate scenario into the trace (in full or partial) and
182- updates tracestate and refinement_stack accordingly.
180+ updates tracestate accordingly.
183181 """
184182 model = tracestate .model if tracestate .model else ModelSpace ()
185183 model .new_scenario_scope ()
186184 inserted , remainder , new_model , extra_data = self ._process_scenario (candidate , model )
187185 if not inserted : # insertion failed
188- model .end_scenario_scope () # redundant??
189186 tracestate .reject_scenario (candidate .src_id )
190187 logger .debug (extra_data ['fail_msg' ])
191188 self ._report_tracestate_to_user (tracestate )
192189 elif not remainder : # the scenario processed in full
193190 new_model .end_scenario_scope ()
194191 tracestate .confirm_full_scenario (inserted .src_id , inserted , new_model )
195192 logger .debug (f"Inserted scenario { inserted .src_id } , { inserted .name } " )
196- if refinement_stack :
197- self ._handle_refinement_exit (inserted , tracestate , refinement_stack )
193+ if tracestate . is_refinement_active () :
194+ self ._handle_refinement_exit (inserted , tracestate )
198195 self ._report_tracestate_to_user (tracestate )
199196 logger .debug (f"last state:\n { tracestate .model .get_status_text ()} " )
200197 else : # the scenario is split into two parts, ready for refinement
201198 logger .debug (f"Partially inserted scenario { inserted .src_id } , { inserted .name } \n "
202199 f"Refinement needed at step: { remainder .steps [1 ]} " )
203200 inserted .name = f"{ inserted .name } (part { tracestate .highest_part (inserted .src_id )+ 1 } )"
204- tracestate .push_partial_scenario (inserted .src_id , inserted , new_model )
205- refinement_stack .append (remainder )
201+ tracestate .push_partial_scenario (inserted .src_id , inserted , new_model , remainder )
206202 self ._report_tracestate_to_user (tracestate )
207203 logger .debug (f"last state:\n { new_model .get_status_text ()} " )
208204
209- def _handle_refinement_exit (self , inserted_refinement , tracestate , refinement_stack ):
210- refinement_tail = refinement_stack . pop ( )
205+ def _handle_refinement_exit (self , inserted_refinement , tracestate ):
206+ refinement_tail = tracestate . get_remainder ( tracestate . active_refinements [ - 1 ] )
211207 exit_conditions = refinement_tail .steps [1 ].model_info ['OUT' ]
212208 exit_conditions_processed = False
213209 for expr in exit_conditions :
@@ -221,7 +217,6 @@ def _handle_refinement_exit(self, inserted_refinement, tracestate, refinement_st
221217
222218 if not exit_conditions_processed :
223219 self ._rewind (tracestate ) # Reject insterted scenario. Even though it fits, it is not a refinement.
224- refinement_stack .append (refinement_tail )
225220 logger .debug (f"Reconsidering scenario { inserted_refinement .src_id } , { inserted_refinement .name } , "
226221 f"did not meet refinement conditions: { exit_conditions } " )
227222 return
@@ -238,14 +233,13 @@ def _handle_refinement_exit(self, inserted_refinement, tracestate, refinement_st
238233 new_model .end_scenario_scope ()
239234 tracestate .confirm_full_scenario (tail_inserted .src_id , tail_inserted , new_model )
240235 logger .debug (f"Scenario '{ tail_inserted .name } ' completed after refinement" )
241- if refinement_stack :
242- self ._handle_refinement_exit (tail_inserted , tracestate , refinement_stack )
236+ if tracestate . is_refinement_active () :
237+ self ._handle_refinement_exit (tail_inserted , tracestate )
243238 else :
244239 logger .debug (f"Partially inserted remainder of scenario { tail_inserted .src_id } , { tail_inserted .name } \n "
245240 f"refinement needed at step: { remainder .steps [1 ]} " )
246241 tail_inserted .name = f"{ tail_inserted .name } (part { tracestate .highest_part (tail_inserted .src_id )+ 1 } )"
247- tracestate .push_partial_scenario (tail_inserted .src_id , tail_inserted , new_model )
248- refinement_stack .append (remainder )
242+ tracestate .push_partial_scenario (tail_inserted .src_id , tail_inserted , new_model , remainder )
249243
250244 @staticmethod
251245 def _split_for_refinement (scenario , step ):
@@ -264,7 +258,9 @@ def _split_for_refinement(scenario, step):
264258 return (front , back )
265259
266260 def _rewind (self , tracestate , drought_recovery = False ):
267- # Todo: Rewind needs to consider refinement stack.
261+ if tracestate [- 1 ].remainder and tracestate .highest_part (tracestate [- 1 ].remainder .src_id ) > 1 :
262+ # When rewinding an 'in between' part, rewind both the part and the refinement
263+ tracestate .rewind ()
268264 tail = tracestate .rewind ()
269265 while drought_recovery and tracestate .coverage_drought :
270266 tail = tracestate .rewind ()
@@ -278,24 +274,23 @@ def _escape_robot_vars(text):
278274
279275 @staticmethod
280276 def _process_scenario (scenario , model ):
281- m = model .copy ()
282277 for step in scenario .steps :
283278 if 'error' in step .model_info :
284- return None , None , m , dict (fail_masg = f"Error in scenario { scenario .name } "
285- f"at step { step } : { step .model_info ['error' ]} " )
279+ return None , None , model , dict (fail_masg = f"Error in scenario { scenario .name } "
280+ f"at step { step } : { step .model_info ['error' ]} " )
286281 for expr in SuiteProcessors ._relevant_expressions (step ):
287282 try :
288- if m .process_expression (expr , step .args ) is False :
283+ if model .process_expression (expr , step .args ) is False :
289284 if step .gherkin_kw in ['when' , None ] and expr in step .model_info ['OUT' ]:
290285 part1 , part2 = SuiteProcessors ._split_for_refinement (scenario , step )
291- return part1 , part2 , m , dict ()
286+ return part1 , part2 , model , dict ()
292287 else :
293- return None , None , m , dict (fail_msg = f"Unable to insert scenario { scenario .src_id } , "
294- f"{ scenario .name } , due to step '{ step } ': [{ expr } ] is False" )
288+ return None , None , model , dict (fail_msg = f"Unable to insert scenario { scenario .src_id } , "
289+ f"{ scenario .name } , due to step '{ step } ': [{ expr } ] is False" )
295290 except Exception as err :
296- return None , None , m , dict (fail_msg = f"Unable to insert scenario { scenario .src_id } , { scenario . name } , "
297- f" due to step '{ step } ': [{ expr } ] { err } " )
298- return scenario .copy (), None , m , dict ()
291+ return None , None , model , dict (fail_msg = f"Unable to insert scenario { scenario .src_id } , "
292+ f" { scenario . name } , due to step '{ step } ': [{ expr } ] { err } " )
293+ return scenario .copy (), None , model , dict ()
299294
300295 @staticmethod
301296 def _relevant_expressions (step ):
0 commit comments