class ProcmonTests(unittest.TestCase): """ Tests for L{ProcessMonitor}. """ def setUp(self): """ Create an L{ProcessMonitor} wrapped around a fake reactor. """ self.reactor = DummyProcessReactor() self.pm = ProcessMonitor(reactor=self.reactor) self.pm.minRestartDelay = 2 self.pm.maxRestartDelay = 10 self.pm.threshold = 10 def test_getStateIncludesProcesses(self): """ The list of monitored processes must be included in the pickle state. """ self.pm.addProcess("foo", ["arg1", "arg2"], uid=1, gid=2, env={}) self.assertEqual(self.pm.__getstate__()['processes'], {'foo': (['arg1', 'arg2'], 1, 2, {})}) def test_getStateExcludesReactor(self): """ The private L{ProcessMonitor._reactor} instance variable should not be included in the pickle state. """ self.assertNotIn('_reactor', self.pm.__getstate__()) def test_addProcess(self): """ L{ProcessMonitor.addProcess} only starts the named program if L{ProcessMonitor.startService} has been called. """ self.pm.addProcess("foo", ["arg1", "arg2"], uid=1, gid=2, env={}) self.assertEqual(self.pm.protocols, {}) self.assertEqual(self.pm.processes, {"foo": (["arg1", "arg2"], 1, 2, {})}) self.pm.startService() self.reactor.advance(0) self.assertEqual(self.pm.protocols.keys(), ["foo"]) def test_addProcessDuplicateKeyError(self): """ L{ProcessMonitor.addProcess} raises a C{KeyError} if a process with the given name already exists. """ self.pm.addProcess("foo", ["arg1", "arg2"], uid=1, gid=2, env={}) self.assertRaises(KeyError, self.pm.addProcess, "foo", ["arg1", "arg2"], uid=1, gid=2, env={}) def test_addProcessEnv(self): """ L{ProcessMonitor.addProcess} takes an C{env} parameter that is passed to L{IReactorProcess.spawnProcess}. """ fakeEnv = {"KEY": "value"} self.pm.startService() self.pm.addProcess("foo", ["foo"], uid=1, gid=2, env=fakeEnv) self.reactor.advance(0) self.assertEqual( self.reactor.spawnedProcesses[0]._environment, fakeEnv) def test_removeProcess(self): """ L{ProcessMonitor.removeProcess} removes the process from the public processes list. """ self.pm.startService() self.pm.addProcess("foo", ["foo"]) self.assertEqual(len(self.pm.processes), 1) self.pm.removeProcess("foo") self.assertEqual(len(self.pm.processes), 0) def test_removeProcessUnknownKeyError(self): """ L{ProcessMonitor.removeProcess} raises a C{KeyError} if the given process name isn't recognised. """ self.pm.startService() self.assertRaises(KeyError, self.pm.removeProcess, "foo") def test_startProcess(self): """ When a process has been started, an instance of L{LoggingProtocol} will be added to the L{ProcessMonitor.protocols} dict and the start time of the process will be recorded in the L{ProcessMonitor.timeStarted} dictionary. """ self.pm.addProcess("foo", ["foo"]) self.pm.startProcess("foo") self.assertIsInstance(self.pm.protocols["foo"], LoggingProtocol) self.assertIn("foo", self.pm.timeStarted.keys()) def test_startProcessAlreadyStarted(self): """ L{ProcessMonitor.startProcess} silently returns if the named process is already started. """ self.pm.addProcess("foo", ["foo"]) self.pm.startProcess("foo") self.assertIsNone(self.pm.startProcess("foo")) def test_startProcessUnknownKeyError(self): """ L{ProcessMonitor.startProcess} raises a C{KeyError} if the given process name isn't recognised. """ self.assertRaises(KeyError, self.pm.startProcess, "foo") def test_stopProcessNaturalTermination(self): """ L{ProcessMonitor.stopProcess} immediately sends a TERM signal to the named process. """ self.pm.startService() self.pm.addProcess("foo", ["foo"]) self.assertIn("foo", self.pm.protocols) # Configure fake process to die 1 second after receiving term signal timeToDie = self.pm.protocols["foo"].transport._terminationDelay = 1 # Advance the reactor to just before the short lived process threshold # and leave enough time for the process to die self.reactor.advance(self.pm.threshold) # Then signal the process to stop self.pm.stopProcess("foo") # Advance the reactor just enough to give the process time to die and # verify that the process restarts self.reactor.advance(timeToDie) # We expect it to be restarted immediately self.assertEqual(self.reactor.seconds(), self.pm.timeStarted["foo"]) def test_stopProcessForcedKill(self): """ L{ProcessMonitor.stopProcess} kills a process which fails to terminate naturally within L{ProcessMonitor.killTime} seconds. """ self.pm.startService() self.pm.addProcess("foo", ["foo"]) self.assertIn("foo", self.pm.protocols) self.reactor.advance(self.pm.threshold) proc = self.pm.protocols["foo"].transport # Arrange for the fake process to live longer than the killTime proc._terminationDelay = self.pm.killTime + 1 self.pm.stopProcess("foo") # If process doesn't die before the killTime, procmon should # terminate it self.reactor.advance(self.pm.killTime - 1) self.assertEqual(0.0, self.pm.timeStarted["foo"]) self.reactor.advance(1) # We expect it to be immediately restarted self.assertEqual(self.reactor.seconds(), self.pm.timeStarted["foo"]) def test_stopProcessUnknownKeyError(self): """ L{ProcessMonitor.stopProcess} raises a C{KeyError} if the given process name isn't recognised. """ self.assertRaises(KeyError, self.pm.stopProcess, "foo") def test_stopProcessAlreadyStopped(self): """ L{ProcessMonitor.stopProcess} silently returns if the named process is already stopped. eg Process has crashed and a restart has been rescheduled, but in the meantime, the service is stopped. """ self.pm.addProcess("foo", ["foo"]) self.assertIsNone(self.pm.stopProcess("foo")) def test_connectionLostLongLivedProcess(self): """ L{ProcessMonitor.connectionLost} should immediately restart a process if it has been running longer than L{ProcessMonitor.threshold} seconds. """ self.pm.addProcess("foo", ["foo"]) # Schedule the process to start self.pm.startService() # advance the reactor to start the process self.reactor.advance(0) self.assertIn("foo", self.pm.protocols) # Long time passes self.reactor.advance(self.pm.threshold) # Process dies after threshold self.pm.protocols["foo"].processEnded(Failure(ProcessDone(0))) self.assertNotIn("foo", self.pm.protocols) # Process should be restarted immediately self.reactor.advance(0) self.assertIn("foo", self.pm.protocols) def test_connectionLostMurderCancel(self): """ L{ProcessMonitor.connectionLost} cancels a scheduled process killer and deletes the DelayedCall from the L{ProcessMonitor.murder} list. """ self.pm.addProcess("foo", ["foo"]) # Schedule the process to start self.pm.startService() # Advance 1s to start the process then ask ProcMon to stop it self.reactor.advance(1) self.pm.stopProcess("foo") # A process killer has been scheduled, delayedCall is active self.assertIn("foo", self.pm.murder) delayedCall = self.pm.murder["foo"] self.assertTrue(delayedCall.active()) # Advance to the point at which the dummy process exits self.reactor.advance( self.pm.protocols["foo"].transport._terminationDelay) # Now the delayedCall has been cancelled and deleted self.assertFalse(delayedCall.active()) self.assertNotIn("foo", self.pm.murder) def test_connectionLostProtocolDeletion(self): """ L{ProcessMonitor.connectionLost} removes the corresponding ProcessProtocol instance from the L{ProcessMonitor.protocols} list. """ self.pm.startService() self.pm.addProcess("foo", ["foo"]) self.assertIn("foo", self.pm.protocols) self.pm.protocols["foo"].transport.signalProcess("KILL") self.reactor.advance( self.pm.protocols["foo"].transport._terminationDelay) self.assertNotIn("foo", self.pm.protocols) def test_connectionLostMinMaxRestartDelay(self): """ L{ProcessMonitor.connectionLost} will wait at least minRestartDelay s and at most maxRestartDelay s """ self.pm.minRestartDelay = 2 self.pm.maxRestartDelay = 3 self.pm.startService() self.pm.addProcess("foo", ["foo"]) self.assertEqual(self.pm.delay["foo"], self.pm.minRestartDelay) self.reactor.advance(self.pm.threshold - 1) self.pm.protocols["foo"].processEnded(Failure(ProcessDone(0))) self.assertEqual(self.pm.delay["foo"], self.pm.maxRestartDelay) def test_connectionLostBackoffDelayDoubles(self): """ L{ProcessMonitor.connectionLost} doubles the restart delay each time the process dies too quickly. """ self.pm.startService() self.pm.addProcess("foo", ["foo"]) self.reactor.advance(self.pm.threshold - 1) #9s self.assertIn("foo", self.pm.protocols) self.assertEqual(self.pm.delay["foo"], self.pm.minRestartDelay) # process dies within the threshold and should not restart immediately self.pm.protocols["foo"].processEnded(Failure(ProcessDone(0))) self.assertEqual(self.pm.delay["foo"], self.pm.minRestartDelay * 2) def test_startService(self): """ L{ProcessMonitor.startService} starts all monitored processes. """ self.pm.addProcess("foo", ["foo"]) # Schedule the process to start self.pm.startService() # advance the reactor to start the process self.reactor.advance(0) self.assertIn("foo", self.pm.protocols) def test_stopService(self): """ L{ProcessMonitor.stopService} should stop all monitored processes. """ self.pm.addProcess("foo", ["foo"]) self.pm.addProcess("bar", ["bar"]) # Schedule the process to start self.pm.startService() # advance the reactor to start the processes self.reactor.advance(self.pm.threshold) self.assertIn("foo", self.pm.protocols) self.assertIn("bar", self.pm.protocols) self.reactor.advance(1) self.pm.stopService() # Advance to beyond the killTime - all monitored processes # should have exited self.reactor.advance(self.pm.killTime + 1) # The processes shouldn't be restarted self.assertEqual({}, self.pm.protocols) def test_stopServiceCancelRestarts(self): """ L{ProcessMonitor.stopService} should cancel any scheduled process restarts. """ self.pm.addProcess("foo", ["foo"]) # Schedule the process to start self.pm.startService() # advance the reactor to start the processes self.reactor.advance(self.pm.threshold) self.assertIn("foo", self.pm.protocols) self.reactor.advance(1) # Kill the process early self.pm.protocols["foo"].processEnded(Failure(ProcessDone(0))) self.assertTrue(self.pm.restart['foo'].active()) self.pm.stopService() # Scheduled restart should have been cancelled self.assertFalse(self.pm.restart['foo'].active()) def test_stopServiceCleanupScheduledRestarts(self): """ L{ProcessMonitor.stopService} should cancel all scheduled process restarts. """ self.pm.threshold = 5 self.pm.minRestartDelay = 5 # Start service and add a process (started immediately) self.pm.startService() self.pm.addProcess("foo", ["foo"]) # Stop the process after 1s self.reactor.advance(1) self.pm.stopProcess("foo") # Wait 1s for it to exit it will be scheduled to restart 5s later self.reactor.advance(1) # Meanwhile stop the service self.pm.stopService() # Advance to beyond the process restart time self.reactor.advance(6) # The process shouldn't have restarted because stopService has cancelled # all pending process restarts. self.assertEqual(self.pm.protocols, {})
def stopService(self): self.connectionLost = self.stoppedConnectionLost # Can't use super here as ProcessMonitor is an old-style object. return ProcessMonitor.stopService(self)
class ProcmonTests(unittest.TestCase): """ Tests for L{ProcessMonitor}. """ def setUp(self): """ Create an L{ProcessMonitor} wrapped around a fake reactor. """ self.reactor = DummyProcessReactor() self.pm = ProcessMonitor(reactor=self.reactor) self.pm.minRestartDelay = 2 self.pm.maxRestartDelay = 10 self.pm.threshold = 10 def test_reprLooksGood(self): """ Repr includes all details """ self.pm.addProcess("foo", ["arg1", "arg2"], uid=1, gid=2, env={}) representation = repr(self.pm) self.assertIn("foo", representation) self.assertIn("1", representation) self.assertIn("2", representation) def test_simpleReprLooksGood(self): """ Repr does not include unneeded details. Values of attributes that just mean "inherit from launching process" do not appear in the repr of a process. """ self.pm.addProcess("foo", ["arg1", "arg2"], env={}) representation = repr(self.pm) self.assertNotIn("(", representation) self.assertNotIn(")", representation) def test_getStateIncludesProcesses(self): """ The list of monitored processes must be included in the pickle state. """ self.pm.addProcess("foo", ["arg1", "arg2"], uid=1, gid=2, env={}) self.assertEqual(self.pm.__getstate__()["processes"], {"foo": (["arg1", "arg2"], 1, 2, {})}) def test_getStateExcludesReactor(self): """ The private L{ProcessMonitor._reactor} instance variable should not be included in the pickle state. """ self.assertNotIn("_reactor", self.pm.__getstate__()) def test_addProcess(self): """ L{ProcessMonitor.addProcess} only starts the named program if L{ProcessMonitor.startService} has been called. """ self.pm.addProcess("foo", ["arg1", "arg2"], uid=1, gid=2, env={}) self.assertEqual(self.pm.protocols, {}) self.assertEqual(self.pm.processes, {"foo": (["arg1", "arg2"], 1, 2, {})}) self.pm.startService() self.reactor.advance(0) self.assertEqual(list(self.pm.protocols.keys()), ["foo"]) def test_addProcessDuplicateKeyError(self): """ L{ProcessMonitor.addProcess} raises a C{KeyError} if a process with the given name already exists. """ self.pm.addProcess("foo", ["arg1", "arg2"], uid=1, gid=2, env={}) self.assertRaises(KeyError, self.pm.addProcess, "foo", ["arg1", "arg2"], uid=1, gid=2, env={}) def test_addProcessEnv(self): """ L{ProcessMonitor.addProcess} takes an C{env} parameter that is passed to L{IReactorProcess.spawnProcess}. """ fakeEnv = {"KEY": "value"} self.pm.startService() self.pm.addProcess("foo", ["foo"], uid=1, gid=2, env=fakeEnv) self.reactor.advance(0) self.assertEqual(self.reactor.spawnedProcesses[0]._environment, fakeEnv) def test_addProcessCwd(self): """ L{ProcessMonitor.addProcess} takes an C{cwd} parameter that is passed to L{IReactorProcess.spawnProcess}. """ self.pm.startService() self.pm.addProcess("foo", ["foo"], cwd="/mnt/lala") self.reactor.advance(0) self.assertEqual(self.reactor.spawnedProcesses[0]._path, "/mnt/lala") def test_removeProcess(self): """ L{ProcessMonitor.removeProcess} removes the process from the public processes list. """ self.pm.startService() self.pm.addProcess("foo", ["foo"]) self.assertEqual(len(self.pm.processes), 1) self.pm.removeProcess("foo") self.assertEqual(len(self.pm.processes), 0) def test_removeProcessUnknownKeyError(self): """ L{ProcessMonitor.removeProcess} raises a C{KeyError} if the given process name isn't recognised. """ self.pm.startService() self.assertRaises(KeyError, self.pm.removeProcess, "foo") def test_startProcess(self): """ When a process has been started, an instance of L{LoggingProtocol} will be added to the L{ProcessMonitor.protocols} dict and the start time of the process will be recorded in the L{ProcessMonitor.timeStarted} dictionary. """ self.pm.addProcess("foo", ["foo"]) self.pm.startProcess("foo") self.assertIsInstance(self.pm.protocols["foo"], LoggingProtocol) self.assertIn("foo", self.pm.timeStarted.keys()) def test_startProcessAlreadyStarted(self): """ L{ProcessMonitor.startProcess} silently returns if the named process is already started. """ self.pm.addProcess("foo", ["foo"]) self.pm.startProcess("foo") self.assertIsNone(self.pm.startProcess("foo")) def test_startProcessUnknownKeyError(self): """ L{ProcessMonitor.startProcess} raises a C{KeyError} if the given process name isn't recognised. """ self.assertRaises(KeyError, self.pm.startProcess, "foo") def test_stopProcessNaturalTermination(self): """ L{ProcessMonitor.stopProcess} immediately sends a TERM signal to the named process. """ self.pm.startService() self.pm.addProcess("foo", ["foo"]) self.assertIn("foo", self.pm.protocols) # Configure fake process to die 1 second after receiving term signal timeToDie = self.pm.protocols["foo"].transport._terminationDelay = 1 # Advance the reactor to just before the short lived process threshold # and leave enough time for the process to die self.reactor.advance(self.pm.threshold) # Then signal the process to stop self.pm.stopProcess("foo") # Advance the reactor just enough to give the process time to die and # verify that the process restarts self.reactor.advance(timeToDie) # We expect it to be restarted immediately self.assertEqual(self.reactor.seconds(), self.pm.timeStarted["foo"]) def test_stopProcessForcedKill(self): """ L{ProcessMonitor.stopProcess} kills a process which fails to terminate naturally within L{ProcessMonitor.killTime} seconds. """ self.pm.startService() self.pm.addProcess("foo", ["foo"]) self.assertIn("foo", self.pm.protocols) self.reactor.advance(self.pm.threshold) proc = self.pm.protocols["foo"].transport # Arrange for the fake process to live longer than the killTime proc._terminationDelay = self.pm.killTime + 1 self.pm.stopProcess("foo") # If process doesn't die before the killTime, procmon should # terminate it self.reactor.advance(self.pm.killTime - 1) self.assertEqual(0.0, self.pm.timeStarted["foo"]) self.reactor.advance(1) # We expect it to be immediately restarted self.assertEqual(self.reactor.seconds(), self.pm.timeStarted["foo"]) def test_stopProcessUnknownKeyError(self): """ L{ProcessMonitor.stopProcess} raises a C{KeyError} if the given process name isn't recognised. """ self.assertRaises(KeyError, self.pm.stopProcess, "foo") def test_stopProcessAlreadyStopped(self): """ L{ProcessMonitor.stopProcess} silently returns if the named process is already stopped. eg Process has crashed and a restart has been rescheduled, but in the meantime, the service is stopped. """ self.pm.addProcess("foo", ["foo"]) self.assertIsNone(self.pm.stopProcess("foo")) def test_outputReceivedCompleteLine(self): """ Getting a complete output line on stdout generates a log message. """ events = [] self.addCleanup(globalLogPublisher.removeObserver, events.append) globalLogPublisher.addObserver(events.append) self.pm.addProcess("foo", ["foo"]) # Schedule the process to start self.pm.startService() # Advance the reactor to start the process self.reactor.advance(0) self.assertIn("foo", self.pm.protocols) # Long time passes self.reactor.advance(self.pm.threshold) # Process greets self.pm.protocols["foo"].outReceived(b"hello world!\n") self.assertEquals(len(events), 1) namespace = events[0]["log_namespace"] stream = events[0]["stream"] tag = events[0]["tag"] line = events[0]["line"] self.assertEquals(namespace, "twisted.runner.procmon.ProcessMonitor") self.assertEquals(stream, "stdout") self.assertEquals(tag, "foo") self.assertEquals(line, "hello world!") def test_ouputReceivedCompleteErrLine(self): """ Getting a complete output line on stderr generates a log message. """ events = [] self.addCleanup(globalLogPublisher.removeObserver, events.append) globalLogPublisher.addObserver(events.append) self.pm.addProcess("foo", ["foo"]) # Schedule the process to start self.pm.startService() # Advance the reactor to start the process self.reactor.advance(0) self.assertIn("foo", self.pm.protocols) # Long time passes self.reactor.advance(self.pm.threshold) # Process greets self.pm.protocols["foo"].errReceived(b"hello world!\n") self.assertEquals(len(events), 1) namespace = events[0]["log_namespace"] stream = events[0]["stream"] tag = events[0]["tag"] line = events[0]["line"] self.assertEquals(namespace, "twisted.runner.procmon.ProcessMonitor") self.assertEquals(stream, "stderr") self.assertEquals(tag, "foo") self.assertEquals(line, "hello world!") def test_outputReceivedCompleteLineInvalidUTF8(self): """ Getting invalid UTF-8 results in the repr of the raw message """ events = [] self.addCleanup(globalLogPublisher.removeObserver, events.append) globalLogPublisher.addObserver(events.append) self.pm.addProcess("foo", ["foo"]) # Schedule the process to start self.pm.startService() # Advance the reactor to start the process self.reactor.advance(0) self.assertIn("foo", self.pm.protocols) # Long time passes self.reactor.advance(self.pm.threshold) # Process greets self.pm.protocols["foo"].outReceived(b"\xffhello world!\n") self.assertEquals(len(events), 1) message = events[0] namespace = message["log_namespace"] stream = message["stream"] tag = message["tag"] output = message["line"] self.assertEquals(namespace, "twisted.runner.procmon.ProcessMonitor") self.assertEquals(stream, "stdout") self.assertEquals(tag, "foo") self.assertEquals(output, repr(b"\xffhello world!")) def test_outputReceivedPartialLine(self): """ Getting partial line results in no events until process end """ events = [] self.addCleanup(globalLogPublisher.removeObserver, events.append) globalLogPublisher.addObserver(events.append) self.pm.addProcess("foo", ["foo"]) # Schedule the process to start self.pm.startService() # Advance the reactor to start the process self.reactor.advance(0) self.assertIn("foo", self.pm.protocols) # Long time passes self.reactor.advance(self.pm.threshold) # Process greets self.pm.protocols["foo"].outReceived(b"hello world!") self.assertEquals(len(events), 0) self.pm.protocols["foo"].processEnded(Failure(ProcessDone(0))) self.assertEquals(len(events), 1) namespace = events[0]["log_namespace"] stream = events[0]["stream"] tag = events[0]["tag"] line = events[0]["line"] self.assertEquals(namespace, "twisted.runner.procmon.ProcessMonitor") self.assertEquals(stream, "stdout") self.assertEquals(tag, "foo") self.assertEquals(line, "hello world!") def test_connectionLostLongLivedProcess(self): """ L{ProcessMonitor.connectionLost} should immediately restart a process if it has been running longer than L{ProcessMonitor.threshold} seconds. """ self.pm.addProcess("foo", ["foo"]) # Schedule the process to start self.pm.startService() # advance the reactor to start the process self.reactor.advance(0) self.assertIn("foo", self.pm.protocols) # Long time passes self.reactor.advance(self.pm.threshold) # Process dies after threshold self.pm.protocols["foo"].processEnded(Failure(ProcessDone(0))) self.assertNotIn("foo", self.pm.protocols) # Process should be restarted immediately self.reactor.advance(0) self.assertIn("foo", self.pm.protocols) def test_connectionLostMurderCancel(self): """ L{ProcessMonitor.connectionLost} cancels a scheduled process killer and deletes the DelayedCall from the L{ProcessMonitor.murder} list. """ self.pm.addProcess("foo", ["foo"]) # Schedule the process to start self.pm.startService() # Advance 1s to start the process then ask ProcMon to stop it self.reactor.advance(1) self.pm.stopProcess("foo") # A process killer has been scheduled, delayedCall is active self.assertIn("foo", self.pm.murder) delayedCall = self.pm.murder["foo"] self.assertTrue(delayedCall.active()) # Advance to the point at which the dummy process exits self.reactor.advance( self.pm.protocols["foo"].transport._terminationDelay) # Now the delayedCall has been cancelled and deleted self.assertFalse(delayedCall.active()) self.assertNotIn("foo", self.pm.murder) def test_connectionLostProtocolDeletion(self): """ L{ProcessMonitor.connectionLost} removes the corresponding ProcessProtocol instance from the L{ProcessMonitor.protocols} list. """ self.pm.startService() self.pm.addProcess("foo", ["foo"]) self.assertIn("foo", self.pm.protocols) self.pm.protocols["foo"].transport.signalProcess("KILL") self.reactor.advance( self.pm.protocols["foo"].transport._terminationDelay) self.assertNotIn("foo", self.pm.protocols) def test_connectionLostMinMaxRestartDelay(self): """ L{ProcessMonitor.connectionLost} will wait at least minRestartDelay s and at most maxRestartDelay s """ self.pm.minRestartDelay = 2 self.pm.maxRestartDelay = 3 self.pm.startService() self.pm.addProcess("foo", ["foo"]) self.assertEqual(self.pm.delay["foo"], self.pm.minRestartDelay) self.reactor.advance(self.pm.threshold - 1) self.pm.protocols["foo"].processEnded(Failure(ProcessDone(0))) self.assertEqual(self.pm.delay["foo"], self.pm.maxRestartDelay) def test_connectionLostBackoffDelayDoubles(self): """ L{ProcessMonitor.connectionLost} doubles the restart delay each time the process dies too quickly. """ self.pm.startService() self.pm.addProcess("foo", ["foo"]) self.reactor.advance(self.pm.threshold - 1) # 9s self.assertIn("foo", self.pm.protocols) self.assertEqual(self.pm.delay["foo"], self.pm.minRestartDelay) # process dies within the threshold and should not restart immediately self.pm.protocols["foo"].processEnded(Failure(ProcessDone(0))) self.assertEqual(self.pm.delay["foo"], self.pm.minRestartDelay * 2) def test_startService(self): """ L{ProcessMonitor.startService} starts all monitored processes. """ self.pm.addProcess("foo", ["foo"]) # Schedule the process to start self.pm.startService() # advance the reactor to start the process self.reactor.advance(0) self.assertIn("foo", self.pm.protocols) def test_stopService(self): """ L{ProcessMonitor.stopService} should stop all monitored processes. """ self.pm.addProcess("foo", ["foo"]) self.pm.addProcess("bar", ["bar"]) # Schedule the process to start self.pm.startService() # advance the reactor to start the processes self.reactor.advance(self.pm.threshold) self.assertIn("foo", self.pm.protocols) self.assertIn("bar", self.pm.protocols) self.reactor.advance(1) self.pm.stopService() # Advance to beyond the killTime - all monitored processes # should have exited self.reactor.advance(self.pm.killTime + 1) # The processes shouldn't be restarted self.assertEqual({}, self.pm.protocols) def test_restartAllRestartsOneProcess(self): """ L{ProcessMonitor.restartAll} succeeds when there is one process. """ self.pm.addProcess("foo", ["foo"]) self.pm.startService() self.reactor.advance(1) self.pm.restartAll() # Just enough time for the process to die, # not enough time to start a new one. self.reactor.advance(1) processes = list(self.reactor.spawnedProcesses) myProcess = processes.pop() self.assertEquals(processes, []) self.assertIsNone(myProcess.pid) def test_stopServiceCancelRestarts(self): """ L{ProcessMonitor.stopService} should cancel any scheduled process restarts. """ self.pm.addProcess("foo", ["foo"]) # Schedule the process to start self.pm.startService() # advance the reactor to start the processes self.reactor.advance(self.pm.threshold) self.assertIn("foo", self.pm.protocols) self.reactor.advance(1) # Kill the process early self.pm.protocols["foo"].processEnded(Failure(ProcessDone(0))) self.assertTrue(self.pm.restart["foo"].active()) self.pm.stopService() # Scheduled restart should have been cancelled self.assertFalse(self.pm.restart["foo"].active()) def test_stopServiceCleanupScheduledRestarts(self): """ L{ProcessMonitor.stopService} should cancel all scheduled process restarts. """ self.pm.threshold = 5 self.pm.minRestartDelay = 5 # Start service and add a process (started immediately) self.pm.startService() self.pm.addProcess("foo", ["foo"]) # Stop the process after 1s self.reactor.advance(1) self.pm.stopProcess("foo") # Wait 1s for it to exit it will be scheduled to restart 5s later self.reactor.advance(1) # Meanwhile stop the service self.pm.stopService() # Advance to beyond the process restart time self.reactor.advance(6) # The process shouldn't have restarted because stopService has cancelled # all pending process restarts. self.assertEqual(self.pm.protocols, {})
s.killTime = 5 s.minRestartDelay = 1 s.maxRestartDelay = 5 return s ss = ProcessMonitor() #ss.startService() ss.startService() ss.addProcess("calc.exe *32", ['c:\windows\system32\calc.exe', ]) while True: #ss.startProcess('calc.exe') time.sleep(5) break #break ss.stopService() ss.startService() while True: #ss.startProcess('calc.exe') time.sleep(5) #ss.restartAll() #ss.startService()