@@ -738,6 +738,40 @@ def test_stop_report_logs_throttled_by_laststopreport(self):
738
738
instance .stop_report ()
739
739
self .assertEqual (len (options .logger .data ), 1 ) # throttled
740
740
741
+ def test_stop_report_laststopreport_in_future (self ):
742
+ future_time = time .time () + 3600 # 1 hour into the future
743
+ options = DummyOptions ()
744
+ config = DummyPConfig (options , 'test' , '/test' )
745
+ instance = self ._makeOne (config )
746
+ instance .pid = 11
747
+ dispatcher = DummyDispatcher (writable = True )
748
+ instance .dispatchers = {'foo' :dispatcher }
749
+ from supervisor .states import ProcessStates
750
+ instance .state = ProcessStates .STOPPING
751
+ instance .laststopreport = future_time
752
+
753
+ # This iteration of stop_report() should reset instance.laststopreport
754
+ # to the current time
755
+ instance .stop_report ()
756
+
757
+ # No logging should have taken place
758
+ self .assertEqual (len (options .logger .data ), 0 )
759
+
760
+ # Ensure instance.laststopreport has rolled backward
761
+ self .assertTrue (instance .laststopreport < future_time )
762
+
763
+ # Sleep for 2 seconds
764
+ time .sleep (2 )
765
+
766
+ # This iteration of stop_report() should actaully trigger the report
767
+ instance .stop_report ()
768
+
769
+ self .assertEqual (len (options .logger .data ), 1 )
770
+ self .assertEqual (options .logger .data [0 ], 'waiting for test to stop' )
771
+ self .assertNotEqual (instance .laststopreport , 0 )
772
+ instance .stop_report ()
773
+ self .assertEqual (len (options .logger .data ), 1 ) # throttled
774
+
741
775
def test_give_up (self ):
742
776
options = DummyOptions ()
743
777
config = DummyPConfig (options , 'test' , '/test' )
@@ -1403,6 +1437,92 @@ def test_transition_starting_to_running(self):
1403
1437
event = L [0 ]
1404
1438
self .assertEqual (event .__class__ , events .ProcessStateRunningEvent )
1405
1439
1440
+ def test_transition_starting_to_running_laststart_in_future (self ):
1441
+ from supervisor import events
1442
+ L = []
1443
+ events .subscribe (events .ProcessStateEvent , lambda x : L .append (x ))
1444
+ from supervisor .states import ProcessStates
1445
+
1446
+ future_time = time .time () + 3600 # 1 hour into the future
1447
+ options = DummyOptions ()
1448
+ test_startsecs = 2
1449
+
1450
+ # this should go from STARTING to RUNNING via transition()
1451
+ pconfig = DummyPConfig (options , 'process' , 'process' ,'/bin/process' ,
1452
+ startsecs = test_startsecs )
1453
+ process = self ._makeOne (pconfig )
1454
+ process .backoff = 1
1455
+ process .delay = 1
1456
+ process .system_stop = False
1457
+ process .laststart = future_time
1458
+ process .pid = 1
1459
+ process .stdout_buffer = 'abc'
1460
+ process .stderr_buffer = 'def'
1461
+ process .state = ProcessStates .STARTING
1462
+
1463
+ # This iteration of transition() should reset process.laststart
1464
+ # to the current time
1465
+ process .transition ()
1466
+
1467
+ # Process state should still be STARTING
1468
+ self .assertEqual (process .state , ProcessStates .STARTING )
1469
+
1470
+ # Ensure process.laststart has rolled backward
1471
+ self .assertTrue (process .laststart < future_time )
1472
+
1473
+ # Sleep for (startsecs + 1)
1474
+ time .sleep (test_startsecs + 1 )
1475
+
1476
+ # This iteration of transition() should actaully trigger the state
1477
+ # transition to RUNNING
1478
+ process .transition ()
1479
+
1480
+ # this implies RUNNING
1481
+ self .assertEqual (process .backoff , 0 )
1482
+ self .assertEqual (process .delay , 0 )
1483
+ self .assertFalse (process .system_stop )
1484
+ self .assertEqual (process .state , ProcessStates .RUNNING )
1485
+ self .assertEqual (options .logger .data [0 ],
1486
+ 'success: process entered RUNNING state, process has '
1487
+ 'stayed up for > than {} seconds (startsecs)' .format (test_startsecs ))
1488
+ self .assertEqual (len (L ), 1 )
1489
+ event = L [0 ]
1490
+ self .assertEqual (event .__class__ , events .ProcessStateRunningEvent )
1491
+
1492
+ def test_transition_backoff_to_starting_delay_in_future (self ):
1493
+ from supervisor import events
1494
+ L = []
1495
+ events .subscribe (events .ProcessStateEvent , lambda x : L .append (x ))
1496
+ from supervisor .states import ProcessStates , SupervisorStates
1497
+
1498
+ future_time = time .time () + 3600 # 1 hour into the future
1499
+ options = DummyOptions ()
1500
+
1501
+ pconfig = DummyPConfig (options , 'process' , 'process' ,'/bin/process' )
1502
+ process = self ._makeOne (pconfig )
1503
+ process .laststart = 1
1504
+ process .delay = future_time
1505
+ process .backoff = 0
1506
+ process .state = ProcessStates .BACKOFF
1507
+
1508
+ # This iteration of transition() should reset process.delay
1509
+ # to the current time
1510
+ process .transition ()
1511
+
1512
+ # Process state should still be BACKOFF
1513
+ self .assertEqual (process .state , ProcessStates .BACKOFF )
1514
+
1515
+ # Ensure process.delay has rolled backward
1516
+ self .assertTrue (process .delay < future_time )
1517
+
1518
+ # This iteration of transition() should actaully trigger the state
1519
+ # transition to STARTING
1520
+ process .transition ()
1521
+
1522
+ self .assertEqual (process .state , ProcessStates .STARTING )
1523
+ self .assertEqual (len (L ), 1 )
1524
+ self .assertEqual (L [0 ].__class__ , events .ProcessStateStartingEvent )
1525
+
1406
1526
def test_transition_backoff_to_fatal (self ):
1407
1527
from supervisor import events
1408
1528
L = []
@@ -2034,6 +2154,32 @@ class DummyGroup:
2034
2154
self .assertEqual (process1 .listener_state , EventListenerStates .BUSY )
2035
2155
self .assertEqual (process1 .event , event )
2036
2156
2157
+ def test_transition_event_proc_running_with_dispatch_throttle_last_dispatch_in_future (self ):
2158
+ future_time = time .time () + 3600 # 1 hour into the future
2159
+ options = DummyOptions ()
2160
+ from supervisor .states import ProcessStates
2161
+ pconfig1 = DummyPConfig (options , 'process1' , 'process1' ,'/bin/process1' )
2162
+ process1 = DummyProcess (pconfig1 , state = ProcessStates .RUNNING )
2163
+ gconfig = DummyPGroupConfig (options , pconfigs = [pconfig1 ])
2164
+ pool = self ._makeOne (gconfig )
2165
+ pool .dispatch_throttle = 5
2166
+ pool .last_dispatch = future_time
2167
+ pool .processes = {'process1' : process1 }
2168
+ event = DummyEvent ()
2169
+ from supervisor .states import EventListenerStates
2170
+ process1 .listener_state = EventListenerStates .READY
2171
+ class DummyGroup :
2172
+ config = gconfig
2173
+ process1 .group = DummyGroup
2174
+ pool ._acceptEvent (event )
2175
+ pool .transition ()
2176
+
2177
+ self .assertEqual (process1 .transitioned , True )
2178
+ self .assertEqual (pool .event_buffer , [event ]) # not popped
2179
+
2180
+ # Ensure pool.last_dispatch has been rolled backward
2181
+ self .assertTrue (pool .last_dispatch < future_time )
2182
+
2037
2183
def test__dispatchEvent_notready (self ):
2038
2184
options = DummyOptions ()
2039
2185
from supervisor .states import ProcessStates
0 commit comments