@@ -51,11 +51,12 @@ class TestController:
51
51
Use this object to control the robot's state during tests
52
52
"""
53
53
54
- def __init__ (self , reraise , robot : wpilib .RobotBase ):
54
+ def __init__ (self , reraise , robot : wpilib .RobotBase , expectFinished : bool ):
55
55
self ._reraise = reraise
56
56
57
57
self ._thread : typing .Optional [threading .Thread ] = None
58
58
self ._robot = robot
59
+ self ._expectFinished = expectFinished
59
60
60
61
self ._cond = threading .Condition ()
61
62
self ._robot_started = False
@@ -79,9 +80,10 @@ def _robot_thread(self, robot):
79
80
80
81
try :
81
82
robot .startCompetition ()
82
- assert self ._robot_finished
83
+ assert self ._expectFinished == self . _robot_finished
83
84
finally :
84
85
# always call endCompetition or python hangs
86
+ print ("_robot_thread is calling endCompetition()" )
85
87
robot .endCompetition ()
86
88
del robot
87
89
@@ -118,6 +120,7 @@ def run_robot(self):
118
120
yield
119
121
finally :
120
122
self ._robot_finished = True
123
+ print ("run_robot is calling endCompetition()" )
121
124
robot .endCompetition ()
122
125
123
126
if isinstance (self ._reraise .exception , RuntimeError ):
@@ -162,8 +165,9 @@ def step_timing(
162
165
self ,
163
166
* ,
164
167
seconds : float ,
165
- autonomous : bool ,
166
- enabled : bool ,
168
+ autonomous : bool = False ,
169
+ test : bool = False ,
170
+ enabled : bool = False ,
167
171
assert_alive : bool = True ,
168
172
) -> float :
169
173
"""
@@ -178,22 +182,24 @@ def step_timing(
178
182
:returns: Number of seconds time was incremented
179
183
"""
180
184
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()?"
182
187
183
188
assert seconds > 0
184
189
185
190
DriverStationSim .setDsAttached (True )
186
191
DriverStationSim .setAutonomous (autonomous )
192
+ DriverStationSim .setTest (test )
187
193
DriverStationSim .setEnabled (enabled )
188
194
189
195
tm = 0.0
190
196
191
- while tm < seconds + 0.01 :
197
+ while tm < seconds :
192
198
DriverStationSim .notifyNewData ()
193
- stepTiming (0.2 )
194
- if assert_alive :
199
+ stepTiming (0.001 )
200
+ if assert_alive and self . _expectFinished :
195
201
assert self .robot_is_alive
196
- tm += 0.2
202
+ tm += 0.001
197
203
198
204
return tm
199
205
@@ -304,12 +310,12 @@ def robot_with_sim_setup_teardown(decorated_robot_class):
304
310
305
311
@pytest .fixture (scope = "function" )
306
312
def getTestController (
307
- reraise , robot_with_sim_setup_teardown : wpilib .RobotBase
313
+ reraise , robot_with_sim_setup_teardown : wpilib .RobotBase , expectFinished
308
314
) -> TestController :
309
315
"""
310
316
A pytest fixture that provides control over your robot_with_sim_setup_teardown
311
317
"""
312
- return TestController (reraise , robot_with_sim_setup_teardown )
318
+ return TestController (reraise , robot_with_sim_setup_teardown , expectFinished )
313
319
314
320
315
321
def run_practice (control : "TestController" ):
@@ -381,10 +387,15 @@ def testPeriodic(self):
381
387
pass
382
388
383
389
@classmethod
384
- @pytest .fixture (scope = "function" , autouse = True )
390
+ @pytest .fixture (scope = "function" , autouse = False )
385
391
def myrobot_class (cls ) -> type [MyRobot ]:
386
392
return cls .MyRobot
387
393
394
+ @classmethod
395
+ @pytest .fixture (scope = "function" , autouse = False )
396
+ def expectFinished (cls ) -> bool :
397
+ return True
398
+
388
399
def test_iterative (self , getTestController , robot_with_sim_setup_teardown ):
389
400
"""Ensure that all states of the iterative robot run"""
390
401
assert robot_with_sim_setup_teardown .robotInitialized == False
@@ -403,76 +414,180 @@ def test_iterative_again(self, getTestController, robot_with_sim_setup_teardown)
403
414
assert robot_with_sim_setup_teardown .robotInitialized == True
404
415
assert robot_with_sim_setup_teardown .robotPeriodicCount > 0
405
416
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 ():
407
495
def robotInit (self ):
408
496
assert False
409
497
410
- class MyRobotRobotPeriodicFails (TimedRobotPy ):
498
+ class MyRobotRobotPeriodicFails ():
411
499
def robotPeriodic (self ):
412
500
assert False
413
501
414
- class MyRobotAutonomousInitFails (TimedRobotPy ):
502
+ class MyRobotAutonomousInitFails ():
415
503
def autonomousInit (self ):
416
504
assert False
417
505
418
- class MyRobotAutonomousPeriodicFails (TimedRobotPy ):
506
+ class MyRobotAutonomousPeriodicFails ():
419
507
def autonomousPeriodic (self ):
420
508
assert False
421
509
422
- class MyRobotAutonomousExitFails (TimedRobotPy ):
510
+ class MyRobotAutonomousExitFails ():
423
511
def autonomousExit (self ):
424
512
assert False
425
513
426
- class MyRobotTeleopInitFails (TimedRobotPy ):
514
+ class MyRobotTeleopInitFails ():
427
515
def teleopInit (self ):
428
516
assert False
429
517
430
- class MyRobotTeleopPeriodicFails (TimedRobotPy ):
518
+ class MyRobotTeleopPeriodicFails ():
431
519
def teleopPeriodic (self ):
432
520
assert False
433
521
434
- class MyRobotDisabledPeriodicFails (TimedRobotPy ):
522
+ class MyRobotDisabledPeriodicFails ():
435
523
def disabledPeriodic (self ):
436
524
assert False
437
525
438
- class MyRobotDisabledInitFails (TimedRobotPy ):
526
+ class MyRobotDisabledInitFails ():
439
527
def disabledInit (self ):
440
528
assert False
441
529
442
- class MyRobotTestInitFails (TimedRobotPy ):
530
+ class MyRobotTestInitFails ():
443
531
def testInit (self ):
444
532
assert False
445
533
446
- class MyRobotTestPeriodicFails (TimedRobotPy ):
534
+ class MyRobotTestPeriodicFails ():
447
535
def testPeriodic (self ):
448
536
assert False
449
537
450
- """
451
- @pytest.mark.parametrize("myrobot_class", [
452
- MyRobotRobotInitFails,
453
- MyRobotAutonomousInitFails,
454
- MyRobotAutonomousPeriodicFails,
455
- MyRobotAutonomousExitFails,
456
- ])
457
- class TestCanThrowFailures:
458
538
459
539
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 ):
461
581
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 )
477
592
478
593
0 commit comments