@@ -51,11 +51,12 @@ class TestController:
5151    Use this object to control the robot's state during tests 
5252    """ 
5353
54-     def  __init__ (self , reraise , robot : wpilib .RobotBase ):
54+     def  __init__ (self , reraise , robot : wpilib .RobotBase ,  expectFinished :  bool ):
5555        self ._reraise  =  reraise 
5656
5757        self ._thread : typing .Optional [threading .Thread ] =  None 
5858        self ._robot  =  robot 
59+         self ._expectFinished  =  expectFinished 
5960
6061        self ._cond  =  threading .Condition ()
6162        self ._robot_started  =  False 
@@ -79,9 +80,10 @@ def _robot_thread(self, robot):
7980
8081            try :
8182                robot .startCompetition ()
82-                 assert  self ._robot_finished 
83+                 assert  self ._expectFinished   ==   self . _robot_finished 
8384            finally :
8485                # always call endCompetition or python hangs 
86+                 print ("_robot_thread is calling endCompetition()" )
8587                robot .endCompetition ()
8688                del  robot 
8789
@@ -118,6 +120,7 @@ def run_robot(self):
118120            yield 
119121        finally :
120122            self ._robot_finished  =  True 
123+             print ("run_robot is calling endCompetition()" )
121124            robot .endCompetition ()
122125
123126            if  isinstance (self ._reraise .exception , RuntimeError ):
@@ -162,8 +165,9 @@ def step_timing(
162165        self ,
163166        * ,
164167        seconds : float ,
165-         autonomous : bool ,
166-         enabled : bool ,
168+         autonomous : bool  =  False ,
169+         test : bool  =  False ,
170+         enabled : bool  =  False ,
167171        assert_alive : bool  =  True ,
168172    ) ->  float :
169173        """ 
@@ -178,22 +182,24 @@ def step_timing(
178182        :returns: Number of seconds time was incremented 
179183        """ 
180184
181-         assert  self .robot_is_alive , "did you call control.run_robot()?" 
185+         if  self ._expectFinished :
186+             assert  self .robot_is_alive , "did you call control.run_robot()?" 
182187
183188        assert  seconds  >  0 
184189
185190        DriverStationSim .setDsAttached (True )
186191        DriverStationSim .setAutonomous (autonomous )
192+         DriverStationSim .setTest (test )
187193        DriverStationSim .setEnabled (enabled )
188194
189195        tm  =  0.0 
190196
191-         while  tm  <  seconds   +   0.01 :
197+         while  tm  <  seconds :
192198            DriverStationSim .notifyNewData ()
193-             stepTiming (0.2  )
194-             if  assert_alive :
199+             stepTiming (0.001  )
200+             if  assert_alive   and   self . _expectFinished :
195201                assert  self .robot_is_alive 
196-             tm  +=  0.2  
202+             tm  +=  0.001  
197203
198204        return  tm 
199205
@@ -304,12 +310,12 @@ def robot_with_sim_setup_teardown(decorated_robot_class):
304310
305311@pytest .fixture (scope = "function" ) 
306312def  getTestController (
307-     reraise , robot_with_sim_setup_teardown : wpilib .RobotBase 
313+     reraise , robot_with_sim_setup_teardown : wpilib .RobotBase ,  expectFinished 
308314) ->  TestController :
309315    """ 
310316    A pytest fixture that provides control over your robot_with_sim_setup_teardown 
311317    """ 
312-     return  TestController (reraise , robot_with_sim_setup_teardown )
318+     return  TestController (reraise , robot_with_sim_setup_teardown ,  expectFinished )
313319
314320
315321def  run_practice (control : "TestController" ):
@@ -381,10 +387,15 @@ def testPeriodic(self):
381387            pass 
382388
383389    @classmethod  
384-     @pytest .fixture (scope = "function" , autouse = True ) 
390+     @pytest .fixture (scope = "function" , autouse = False ) 
385391    def  myrobot_class (cls ) ->  type [MyRobot ]:
386392        return  cls .MyRobot 
387393
394+     @classmethod  
395+     @pytest .fixture (scope = "function" , autouse = False ) 
396+     def  expectFinished (cls ) ->  bool :
397+         return  True 
398+ 
388399    def  test_iterative (self , getTestController , robot_with_sim_setup_teardown ):
389400        """Ensure that all states of the iterative robot run""" 
390401        assert  robot_with_sim_setup_teardown .robotInitialized  ==  False 
@@ -403,76 +414,180 @@ def test_iterative_again(self, getTestController, robot_with_sim_setup_teardown)
403414        assert  robot_with_sim_setup_teardown .robotInitialized  ==  True 
404415        assert  robot_with_sim_setup_teardown .robotPeriodicCount  >  0 
405416
406- class  MyRobotRobotInitFails (TimedRobotPy ):
417+ class  TimedRobotPyExpectsException (TimedRobotPy ):
418+     def  startCompetition (self ) ->  None :
419+         hasAssertionError  =  False 
420+         try :
421+             super ().startCompetition ()
422+         except  AssertionError :
423+             hasAssertionError  =  True 
424+         print (f"TimedRobotPyExpectsException hasAssertionError={ hasAssertionError }  )
425+         assert  hasAssertionError 
426+ 
427+ class  TimedRobotPyDoNotExpectException (TimedRobotPy ):
428+     def  startCompetition (self ) ->  None :
429+         hasAssertionError  =  False 
430+         try :
431+             super ().startCompetition ()
432+         except  AssertionError :
433+             hasAssertionError  =  True 
434+         print (f"TimedRobotPyExpectsException hasAssertionError={ hasAssertionError }  )
435+         assert  not  hasAssertionError 
436+ 
437+ 
438+ from  wpilib  import  RobotController 
439+ 
440+ def  printEntryAndExit (func ):
441+     def  wrapper (* args , ** kwargs ):
442+         #name = inspect.currentframe().f_code.co_name 
443+         name  =  func .__name__ 
444+         print (f"Enter:{ name } { RobotController .getFPGATime ()/ 1000_000.0 :.3f}  )
445+         result  =  func (* args , ** kwargs )
446+         print (f"Exit_:{ name } { RobotController .getFPGATime ()/ 1000_000.0 :.3f}  )
447+         return  result 
448+     return  wrapper 
449+ 
450+ class  MyRobotRobotDefaultPass ():
451+ 
452+     @printEntryAndExit  
453+     def  robotInit (self ):
454+         pass 
455+ 
456+     @printEntryAndExit  
457+     def  robotPeriodic (self ):
458+         pass 
459+ 
460+     @printEntryAndExit  
461+     def  autonomousInit (self ):
462+         pass 
463+ 
464+     @printEntryAndExit  
465+     def  autonomousPeriodic (self ):
466+         pass 
467+ 
468+     @printEntryAndExit  
469+     def  autonomousExit (self ):
470+         pass 
471+ 
472+     @printEntryAndExit  
473+     def  disabledInit (self ):
474+         pass 
475+ 
476+     @printEntryAndExit  
477+     def  disabledPeriodic (self ):
478+         pass 
479+ 
480+     @printEntryAndExit  
481+     def  disabledExit (self ):
482+         pass 
483+ 
484+     @printEntryAndExit  
485+     def  _simulationInit (self ):
486+         pass 
487+ 
488+     @printEntryAndExit  
489+     def  _simulationPeriodic (self ):
490+         pass 
491+ 
492+ 
493+ 
494+ class  MyRobotRobotInitFails ():
407495    def  robotInit (self ):
408496        assert  False 
409497
410- class  MyRobotRobotPeriodicFails (TimedRobotPy ):
498+ class  MyRobotRobotPeriodicFails ():
411499    def  robotPeriodic (self ):
412500        assert  False 
413501
414- class  MyRobotAutonomousInitFails (TimedRobotPy ):
502+ class  MyRobotAutonomousInitFails ():
415503    def  autonomousInit (self ):
416504        assert  False 
417505
418- class  MyRobotAutonomousPeriodicFails (TimedRobotPy ):
506+ class  MyRobotAutonomousPeriodicFails ():
419507    def  autonomousPeriodic (self ):
420508        assert  False 
421509
422- class  MyRobotAutonomousExitFails (TimedRobotPy ):
510+ class  MyRobotAutonomousExitFails ():
423511    def  autonomousExit (self ):
424512        assert  False 
425513
426- class  MyRobotTeleopInitFails (TimedRobotPy ):
514+ class  MyRobotTeleopInitFails ():
427515    def  teleopInit (self ):
428516        assert  False 
429517
430- class  MyRobotTeleopPeriodicFails (TimedRobotPy ):
518+ class  MyRobotTeleopPeriodicFails ():
431519    def  teleopPeriodic (self ):
432520        assert  False 
433521
434- class  MyRobotDisabledPeriodicFails (TimedRobotPy ):
522+ class  MyRobotDisabledPeriodicFails ():
435523    def  disabledPeriodic (self ):
436524        assert  False 
437525
438- class  MyRobotDisabledInitFails (TimedRobotPy ):
526+ class  MyRobotDisabledInitFails ():
439527    def  disabledInit (self ):
440528        assert  False 
441529
442- class  MyRobotTestInitFails (TimedRobotPy ):
530+ class  MyRobotTestInitFails ():
443531    def  testInit (self ):
444532        assert  False 
445533
446- class  MyRobotTestPeriodicFails (TimedRobotPy ):
534+ class  MyRobotTestPeriodicFails ():
447535    def  testPeriodic (self ):
448536        assert  False 
449537
450- """ 
451- @pytest.mark.parametrize("myrobot_class", [ 
452-     MyRobotRobotInitFails, 
453-     MyRobotAutonomousInitFails, 
454-     MyRobotAutonomousPeriodicFails, 
455-     MyRobotAutonomousExitFails, 
456- ]) 
457- class TestCanThrowFailures: 
458538
459539
460-     def test_autonomous_fails(self, getTestController, robot_with_sim_setup_teardown): 
540+ @pytest .mark .parametrize ("myRobotAddMethods, timedRobotExpectation, _expectFinished, _autonomous, _test" , [ 
541+         (MyRobotRobotDefaultPass , TimedRobotPyDoNotExpectException , True , True , False ), 
542+         (MyRobotRobotInitFails , TimedRobotPyExpectsException , False , True , False ), 
543+         (MyRobotAutonomousInitFails , TimedRobotPyExpectsException , False , True , False ), 
544+         (MyRobotAutonomousPeriodicFails , TimedRobotPyExpectsException , False , True , False ), 
545+         (MyRobotAutonomousExitFails , TimedRobotPyExpectsException , False , True , False ) 
546+     ] 
547+ ) 
548+ class  TestCanThrowExceptions :
549+     @classmethod  
550+     @pytest .fixture (scope = "function" , autouse = False ) 
551+     def  myrobot_class (cls , myRobotAddMethods , timedRobotExpectation , _expectFinished , _autonomous , _test ) ->  type [TimedRobotPy ]:
552+         class  MyRobot (myRobotAddMethods , timedRobotExpectation ):
553+ 
554+             @printEntryAndExit  
555+             def  startCompetition (self ):
556+                 super ().startCompetition ()
557+ 
558+             @printEntryAndExit  
559+             def  endCompetition (self ):
560+                 super ().endCompetition ()
561+ 
562+         return  MyRobot 
563+ 
564+     @classmethod  
565+     @pytest .fixture (scope = "function" , autouse = False ) 
566+     def  expectFinished (cls , myRobotAddMethods , timedRobotExpectation , _expectFinished , _autonomous , _test ) ->  bool :
567+         return  _expectFinished 
568+ 
569+     @classmethod  
570+     @pytest .fixture (scope = "function" , autouse = False ) 
571+     def  autonomous (cls , myRobotAddMethods , timedRobotExpectation , _expectFinished , _autonomous , _test ) ->  bool :
572+         return  _autonomous 
573+ 
574+     @classmethod  
575+     @pytest .fixture (scope = "function" , autouse = False ) 
576+     def  test (cls , myRobotAddMethods , timedRobotExpectation , _expectFinished , _autonomous , _test ) ->  bool :
577+         return  _test 
578+ 
579+ 
580+     def  test_robot_mode_with_exceptions (self , getTestController , robot_with_sim_setup_teardown , autonomous , test ):
461581        with  getTestController .run_robot ():
462-             hasAssertionError = False 
463-             try: 
464-                 # Run disabled for a short period 
465-                 getTestController.step_timing(seconds=0.5, autonomous=True, enabled=False) 
466- 
467-                 # Run autonomous + enabled for 15 seconds 
468-                 getTestController.step_timing(seconds=15, autonomous=True, enabled=True) 
469- 
470-                 # Disabled for another short period 
471-                 getTestController.step_timing(seconds=0.5, autonomous=False, enabled=False) 
472-             except AssertionError: 
473-                 hasAssertionError = True 
474-                 print("We had an assertion error") 
475-             assert hasAssertionError 
476- """ 
582+             periodS  =  robot_with_sim_setup_teardown .getPeriod ()
583+             # Run disabled for a short period 
584+             print (f"periodS={ periodS } { periodS * 1.5 }  )
585+             getTestController .step_timing (seconds = periodS * 1.5 , autonomous = autonomous , test = test , enabled = False )
586+ 
587+             # Run in desired mode for 1 period 
588+             getTestController .step_timing (seconds = periodS , autonomous = autonomous , test = test , enabled = True )
589+ 
590+             # Disabled for 1 period 
591+             getTestController .step_timing (seconds = periodS , autonomous = autonomous , test = test , enabled = False )
477592
478593
0 commit comments