@@ -154,8 +154,64 @@ impl TestRunner {
154154 self . remaining_iterations == 1
155155 }
156156
157- /// Tick this test forward, running any actionscript and progressing the timeline by one.
158- pub fn tick ( & mut self ) {
157+ pub fn is_preloaded ( & self ) -> bool {
158+ self . preloaded
159+ }
160+
161+ /// Ticks this test forward: runs actionscript, progresses the timeline by one,
162+ /// executes custom FsCommands and performs scheduled tests.
163+ pub fn tick ( & mut self ) -> Result < TestStatus > {
164+ use std:: panic:: { AssertUnwindSafe , catch_unwind, resume_unwind} ;
165+
166+ let unwind_result = catch_unwind ( AssertUnwindSafe ( || self . tick_inner ( ) ) ) ;
167+
168+ match ( unwind_result, self . options . known_failure ) {
169+ ( Ok ( ret) , _) => ret,
170+ // known_failure tests may pass by panicking.
171+ ( Err ( _) , true ) => Ok ( TestStatus :: Finished ) ,
172+ ( Err ( panic) , false ) => resume_unwind ( panic) ,
173+ }
174+ }
175+
176+ fn tick_inner ( & mut self ) -> Result < TestStatus > {
177+ self . do_tick ( ) ;
178+
179+ match ( self . test ( ) , self . options . known_failure ) {
180+ ( Ok ( ( ) ) , _) => ( ) ,
181+ ( Err ( _) , true ) => return Ok ( TestStatus :: Finished ) ,
182+ ( Err ( err) , false ) => return Err ( err) ,
183+ }
184+
185+ match self . remaining_iterations {
186+ 0 => match ( self . last_test ( ) , self . options . known_failure ) {
187+ ( Ok ( ( ) ) , true ) => Err ( anyhow ! (
188+ "Test was known to be failing, but now passes successfully. Please update it and remove `known_failure = true`!" ,
189+ ) ) ,
190+ ( Ok ( ( ) ) , false ) => Ok ( TestStatus :: Finished ) ,
191+ ( Err ( _) , true ) => Ok ( TestStatus :: Finished ) ,
192+ ( Err ( err) , false ) => Err ( err) ,
193+ } ,
194+ _ if self . options . sleep_to_meet_frame_rate => {
195+ // If requested, ensure that the 'expected' amount of
196+ // time actually elapses between frames. This is useful for
197+ // tests that call 'flash.utils.getTimer()' and use
198+ // 'setInterval'/'flash.utils.Timer'
199+ //
200+ // Note that when Ruffle actually runs frames, we can
201+ // execute frames faster than this in order to 'catch up'
202+ // if we've fallen behind. However, in order to make regression
203+ // tests deterministic, we always call 'update_timers' with
204+ // an elapsed time of 'frame_time'. By sleeping for 'frame_time_duration',
205+ // we ensure that the result of 'flash.utils.getTimer()' is consistent
206+ // with timer execution (timers will see an elapsed time of *at least*
207+ // the requested timer interval).
208+ Ok ( TestStatus :: Sleep ( self . frame_time_duration ) )
209+ }
210+ _ => Ok ( TestStatus :: Continue ) ,
211+ }
212+ }
213+
214+ fn do_tick ( & mut self ) {
159215 if !self
160216 . player
161217 . lock ( )
@@ -179,14 +235,10 @@ impl TestRunner {
179235 self . executor . run ( ) ;
180236 }
181237
182- pub fn is_preloaded ( & self ) -> bool {
183- self . preloaded
184- }
185-
186238 /// After a tick, run any custom fdcommands that were queued up and perform any scheduled tests.
187- pub fn test ( & mut self ) -> Result < TestStatus > {
239+ fn test ( & mut self ) -> Result < ( ) > {
188240 if !self . preloaded {
189- return Ok ( TestStatus :: Continue ) ;
241+ return Ok ( ( ) ) ;
190242 }
191243 for command in self . fs_commands . try_iter ( ) {
192244 match command {
@@ -225,86 +277,58 @@ impl TestRunner {
225277 // Rendering has side-effects (such as processing 'DisplayObject.scrollRect' updates)
226278 self . player . lock ( ) . unwrap ( ) . render ( ) ;
227279
228- if let Some ( name) = self
229- . images
230- . iter ( )
231- . find ( |( _k, v) | v. trigger == ImageTrigger :: SpecificIteration ( self . current_iteration ) )
232- . map ( |( k, _v) | k. to_owned ( ) )
233- {
234- let image_comparison = self
235- . images
236- . remove ( & name)
237- . expect ( "Name was just retrieved from map, should not be missing!" ) ;
280+ let trigger = ImageTrigger :: SpecificIteration ( self . current_iteration ) ;
281+ if let Some ( ( name, comp) ) = self . take_image_comparison_by_trigger ( trigger) {
238282 capture_and_compare_image (
239283 & self . root_path ,
240284 & self . player ,
241285 & name,
242- image_comparison ,
286+ comp ,
243287 self . options . known_failure ,
244288 self . render_interface . as_deref ( ) ,
245289 ) ?;
246290 }
247291
248- if self . remaining_iterations == 0 {
249- // Last iteration, let's check everything went well
250-
251- if let Some ( name) = self
252- . images
253- . iter ( )
254- . find ( |( _k, v) | v. trigger == ImageTrigger :: LastFrame )
255- . map ( |( k, _v) | k. to_owned ( ) )
256- {
257- let image_comparison = self
258- . images
259- . remove ( & name)
260- . expect ( "Name was just retrieved from map, should not be missing!" ) ;
261-
262- capture_and_compare_image (
263- & self . root_path ,
264- & self . player ,
265- & name,
266- image_comparison,
267- self . options . known_failure ,
268- self . render_interface . as_deref ( ) ,
269- ) ?;
270- }
292+ Ok ( ( ) )
293+ }
271294
272- if !self . images . is_empty ( ) {
273- return Err ( anyhow ! (
274- "Image comparisons didn't trigger: {:?}" ,
275- self . images. keys( )
276- ) ) ;
277- }
295+ fn last_test ( & mut self ) -> Result < ( ) > {
296+ // Last iteration, let's check everything went well
278297
279- self . executor . run ( ) ;
298+ let trigger = ImageTrigger :: LastFrame ;
299+ if let Some ( ( name, comp) ) = self . take_image_comparison_by_trigger ( trigger) {
300+ capture_and_compare_image (
301+ & self . root_path ,
302+ & self . player ,
303+ & name,
304+ comp,
305+ self . options . known_failure ,
306+ self . render_interface . as_deref ( ) ,
307+ ) ?;
308+ }
280309
281- let trace = self . log . trace_output ( ) ;
282- // Null bytes are invisible, and interfere with constructing
283- // the expected output.txt file. Any tests dealing with null
284- // bytes should explicitly test for them in ActionScript.
285- let normalized_trace = trace. replace ( '\0' , "" ) ;
286- compare_trace_output ( & self . output_path , & self . options , & normalized_trace) ?;
310+ if !self . images . is_empty ( ) {
311+ return Err ( anyhow ! (
312+ "Image comparisons didn't trigger: {:?}" ,
313+ self . images. keys( )
314+ ) ) ;
287315 }
288316
289- Ok ( match self . remaining_iterations {
290- 0 => TestStatus :: Finished ,
291- _ if self . options . sleep_to_meet_frame_rate => {
292- // If requested, ensure that the 'expected' amount of
293- // time actually elapses between frames. This is useful for
294- // tests that call 'flash.utils.getTimer()' and use
295- // 'setInterval'/'flash.utils.Timer'
296- //
297- // Note that when Ruffle actually runs frames, we can
298- // execute frames faster than this in order to 'catch up'
299- // if we've fallen behind. However, in order to make regression
300- // tests deterministic, we always call 'update_timers' with
301- // an elapsed time of 'frame_time'. By sleeping for 'frame_time_duration',
302- // we ensure that the result of 'flash.utils.getTimer()' is consistent
303- // with timer execution (timers will see an elapsed time of *at least*
304- // the requested timer interval).
305- TestStatus :: Sleep ( self . frame_time_duration )
306- }
307- _ => TestStatus :: Continue ,
308- } )
317+ self . executor . run ( ) ;
318+
319+ let trace = self . log . trace_output ( ) ;
320+ // Null bytes are invisible, and interfere with constructing
321+ // the expected output.txt file. Any tests dealing with null
322+ // bytes should explicitly test for them in ActionScript.
323+ let normalized_trace = trace. replace ( '\0' , "" ) ;
324+ compare_trace_output ( & self . output_path , & self . options , & normalized_trace) ?;
325+ Ok ( ( ) )
326+ }
327+
328+ fn take_image_comparison_by_trigger (
329+ & mut self ,
330+ trigger : ImageTrigger ,
331+ ) -> Option < ( String , ImageComparison ) > {
332+ self . images . extract_if ( |_k, v| v. trigger == trigger) . next ( )
309333 }
310334}
0 commit comments