@@ -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 } at { RobotController .getFPGATime ()/ 1000_000.0 :.3f} " )
445+ result = func (* args , ** kwargs )
446+ print (f"Exit_:{ name } at { 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 } or { 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