def testPausePausesOnStart(self): # autoStart reactor = CallTrace('reactor') pc = PeriodicCall(reactor=reactor, autoStart=False) pc.observer_init() self.assertEquals([], reactor.calledMethodNames()) # explicit .pause() pc = PeriodicCall(reactor=reactor, schedule=Schedule(period=1), autoStart=True) pc.pause() pc.observer_init() self.assertEquals([], reactor.calledMethodNames())
class PeriodicCallTest(SeecrTestCase): def setUp(self): SeecrTestCase.setUp(self) self.newDNA(schedule=Schedule(period=3600), errorSchedule=Schedule(period=15), prio=9, name='obs_name') list(compose(self.dna.once.observer_init())) def newDNA(self, **kwargs): emptyGeneratorMethods = [kwargs.get('message', 'handle')] self.reactor = CallTrace('Reactor', returnValues={'addTimer': 'TOKEN'}) self.observer = CallTrace('Observer', emptyGeneratorMethods=emptyGeneratorMethods, ignoredAttributes=['observer_init']) self.pc = PeriodicCall(self.reactor, **kwargs) self.dna = be((Observable(), (self.pc, (self.observer,), ), )) def testWithoutData(self): self.assertEquals('obs_name', self.pc.observable_name()) self.assertEquals(['addTimer'], self.reactor.calledMethodNames()) addTimer, = self.reactor.calledMethods self.assertEquals(((3600, self.pc._periodicCall), {}), (addTimer.args, addTimer.kwargs)) # addTimer(3600, pc._periodicCall) self.reactor.calledMethods.reset() addTimer.args[1]() self.assertEquals(['addProcess'], self.reactor.calledMethodNames()) addProcess, = self.reactor.calledMethods self.assertEquals((1, 1), (len(addProcess.args), len(addProcess.kwargs))) self.assertEquals({'prio': 9}, addProcess.kwargs) thisNext = addProcess.args[0] self.reactor.calledMethods.reset() self.assertEquals([], self.observer.calledMethodNames()) thisNext() self.assertEquals(['handle'], self.observer.calledMethodNames()) self.assertEquals(['removeProcess', 'addTimer'], self.reactor.calledMethodNames()) removeProcess, addTimer = self.reactor.calledMethods self.assertEquals(((), {}), (removeProcess.args, removeProcess.kwargs)) self.assertEquals(((3600, self.pc._periodicCall), {}), (addTimer.args, addTimer.kwargs)) def testWithoutDataWithDefaultValues(self): self.assertEquals(['addTimer'], self.reactor.calledMethodNames()) addTimer, = self.reactor.calledMethods self.assertEquals(((3600, self.pc._periodicCall), {}), (addTimer.args, addTimer.kwargs)) self.reactor.calledMethods.reset() addTimer.args[1]() self.assertEquals(['addProcess'], self.reactor.calledMethodNames()) addProcess, = self.reactor.calledMethods self.assertEquals((1, 1), (len(addProcess.args), len(addProcess.kwargs))) self.assertEquals({'prio': 9}, addProcess.kwargs) thisNext = addProcess.args[0] self.reactor.calledMethods.reset() self.assertEquals([], self.observer.calledMethodNames()) thisNext() self.assertEquals(['handle'], self.observer.calledMethodNames()) self.assertEquals(['removeProcess', 'addTimer'], self.reactor.calledMethodNames()) removeProcess, addTimer = self.reactor.calledMethods self.assertEquals(((), {}), (removeProcess.args, removeProcess.kwargs)) self.assertEquals(((3600, self.pc._periodicCall), {}), (addTimer.args, addTimer.kwargs)) def testAutoStartOrScheduleRequired(self): reactor = CallTrace('reactor') self.assertRaises(ValueError, lambda: PeriodicCall(reactor=reactor)) self.assertRaises(ValueError, lambda: PeriodicCall(reactor=reactor, autoStart=True)) try: PeriodicCall(reactor=reactor, autoStart=False) except: self.fail('Unexpected exception') try: PeriodicCall(reactor=reactor, schedule=Schedule(period=1), autoStart=False) except: self.fail('Unexpected exception') try: PeriodicCall(reactor=reactor, schedule=Schedule(period=1), autoStart=True) except: self.fail('Unexpected exception') def testInitialSchedule(self): self.newDNA(initialSchedule=Schedule(period=0), schedule=Schedule(period=12)) list(compose(self.dna.once.observer_init())) self.assertEquals(['addTimer'], self.reactor.calledMethodNames()) addTimer, = self.reactor.calledMethods self.reactor.calledMethods.reset() self.assertEquals(0, addTimer.args[0]) addTimer.args[1]() addProcess, = self.reactor.calledMethods self.reactor.calledMethods.reset() addProcess.args[0]() self.assertEquals(['removeProcess', 'addTimer'], self.reactor.calledMethodNames()) removeProcess, addTimer = self.reactor.calledMethods self.assertEquals(12, addTimer.args[0]) def testSetSchedule(self): addTimer, = self.reactor.calledMethods self.reactor.calledMethods.reset() addTimer.args[1]() self.pc.setSchedule(schedule=Schedule(period=1)) addProcess, = self.reactor.calledMethods self.reactor.calledMethods.reset() addProcess.args[0]() self.assertEquals(['removeProcess', 'addTimer'], self.reactor.calledMethodNames()) removeProcess, addTimer = self.reactor.calledMethods self.assertEquals(1, addTimer.args[0]) def testSetScheduleWithIdenticalScheduleDoesNothing(self): addTimer, = self.reactor.calledMethods self.reactor.calledMethods.reset() self.assertEquals(3600, addTimer.args[0]) self.pc.setSchedule(schedule=Schedule(period=3600)) self.assertEquals([], self.reactor.calledMethodNames()) def testSetScheduleAddsANewTimer(self): self.reactor.calledMethods.reset() self.pc.setSchedule(schedule=Schedule(period=123)) self.assertEquals(['removeTimer', 'addTimer'], self.reactor.calledMethodNames()) removeTimer, addTimer = self.reactor.calledMethods self.assertEquals('TOKEN', removeTimer.args[0]) self.assertEquals(123, addTimer.args[0]) def testErrorIntervalAndLoggedMessage(self): def raiser(): raise Exception('exception') yield self.observer.methods['handle'] = raiser addTimer, = self.reactor.calledMethods self.reactor.calledMethods.reset() addTimer.args[1]() addProcess, = self.reactor.calledMethods self.reactor.calledMethods.reset() with stderr_replaced() as err: addProcess.args[0]() errValue = err.getvalue() self.assertTrue(errValue.startswith(repr(self.pc))) self.assertTrue('Traceback' in errValue, errValue) self.assertTrue('Exception: exception' in errValue, errValue) self.assertEquals('exception', self.pc.getState().errorState) self.assertEquals(['handle'], self.observer.calledMethodNames()) self.assertEquals(['removeProcess', 'addTimer'], self.reactor.calledMethodNames()) _, addTimer = self.reactor.calledMethods self.assertEquals(((15, self.pc._periodicCall), {}), (addTimer.args, addTimer.kwargs)) def testErrorStateReset(self): handleCalledBefore = [] def handle(): if not handleCalledBefore: handleCalledBefore.append(True) raise Exception('exception') yield Yield self.observer.methods['handle'] = handle addTimer, = self.reactor.calledMethods timerCallback = addTimer.args[1] self.reactor.calledMethods.reset() timerCallback() addProcess, = self.reactor.calledMethods addProcessCallback = addProcess.args[0] self.reactor.calledMethods.reset() with stderr_replaced(): addProcessCallback() self.assertEquals('exception', self.pc.getState().errorState) self.assertEquals(['handle'], self.observer.calledMethodNames()) self.assertEquals(['removeProcess', 'addTimer'], self.reactor.calledMethodNames()) _, addTimer = self.reactor.calledMethods timerCallback = addTimer.args[1] self.reactor.calledMethods.reset() timerCallback() addProcess, = self.reactor.calledMethods addProcessCallback = addProcess.args[0] self.reactor.calledMethods.reset() addProcessCallback() self.assertEquals(['handle', 'handle'], self.observer.calledMethodNames()) addProcessCallback() self.assertEquals(['removeProcess', 'addTimer'], self.reactor.calledMethodNames()) self.assertEquals(None, self.pc.getState().errorState) def testFatalErrorReRaised(self): for exception in [KeyboardInterrupt, SystemExit, AssertionError]: self.newDNA(schedule=Schedule(period=987)) def raiser(): raise exception('msg') yield self.observer.methods['handle'] = raiser list(compose(self.dna.once.observer_init())) addTimer, = self.reactor.calledMethods self.reactor.calledMethods.reset() addTimer.args[1]() addProcess, = self.reactor.calledMethods self.reactor.calledMethods.reset() try: addProcess.args[0]() except exception: pass else: self.fail() self.assertEquals(['handle'], self.observer.calledMethodNames()) self.assertEquals(['removeProcess', 'addTimer'], self.reactor.calledMethodNames()) def testHandleWithCallableAndData(self): suspend = CallTrace('Suspend', returnValues={'__call__': lambda *a, **kw: None}) handleLog = [] def handle(): handleLog.append('ignored-da') yield 'ignored-da' handleLog.append('Suspend') yield suspend handleLog.append('ignored-ta') yield 'ignored-ta' handleLog.append('Stop') self.observer.methods['handle'] = handle addTimer, = self.reactor.calledMethods self.reactor.calledMethods.reset() addTimer.args[1]() addProcess, = self.reactor.calledMethods self.reactor.calledMethods.reset() # 'ignored-da' self.assertEquals([], handleLog) addProcess.args[0]() self.assertEquals(['handle'], self.observer.calledMethodNames()) handleCall, = self.observer.calledMethods self.assertEquals(((), {}), (handleCall.args, handleCall.kwargs)) self.assertEquals(['ignored-da'], handleLog) self.assertEquals([], self.reactor.calledMethodNames()) self.observer.calledMethods.reset() self.reactor.calledMethods.reset() # suspend (suspend(reactor, this.next)) self.assertEquals([], suspend.calledMethodNames()) addProcess.args[0]() self.assertEquals(['ignored-da', 'Suspend'], handleLog) self.assertEquals([], self.observer.calledMethodNames()) self.assertEquals([], self.reactor.calledMethodNames()) self.assertEquals(['__call__'], suspend.calledMethodNames()) dunderCall, = suspend.calledMethods self.assertEquals(2, len(dunderCall.args)) self.assertEquals(self.reactor, dunderCall.args[0]) self.assertEquals(GeneratorType, type(dunderCall.args[1].__self__)) self.assertEquals('_periodicCall', dunderCall.args[1].__self__.gi_frame.f_code.co_name) self.assertEquals({}, dunderCall.kwargs) self.observer.calledMethods.reset() self.reactor.calledMethods.reset() suspend.calledMethods.reset() # suspend (suspend.resumeProcess()) dunderCall.args[1]() self.assertEquals(['resumeProcess'], suspend.calledMethodNames()) resumeProcess, = suspend.calledMethods self.assertEquals(((), {}), (resumeProcess.args, resumeProcess.kwargs)) self.assertEquals([], self.reactor.calledMethodNames()) self.assertEquals([], self.observer.calledMethodNames()) self.observer.calledMethods.reset() self.reactor.calledMethods.reset() suspend.calledMethods.reset() # 'ignored-ta' addProcess.args[0]() self.assertEquals(['ignored-da', 'Suspend', 'ignored-ta'], handleLog) self.assertEquals([], suspend.calledMethodNames()) self.assertEquals([], self.reactor.calledMethodNames()) self.assertEquals([], self.observer.calledMethodNames()) # 'Stop' addProcess.args[0]() self.assertEquals(['ignored-da', 'Suspend', 'ignored-ta', 'Stop'], handleLog) self.assertEquals([], suspend.calledMethodNames()) self.assertEquals([], self.observer.calledMethodNames()) self.assertEquals(['removeProcess', 'addTimer'], self.reactor.calledMethodNames()) removeProcess, addTimer = self.reactor.calledMethods self.assertEquals(((), {}), (removeProcess.args, removeProcess.kwargs)) self.assertEquals(((3600, self.pc._periodicCall), {}), (addTimer.args, addTimer.kwargs)) @stderr_replaced def testPausePausesOnStart(self): # autoStart reactor = CallTrace('reactor') pc = PeriodicCall(reactor=reactor, autoStart=False) pc.observer_init() self.assertEquals([], reactor.calledMethodNames()) # explicit .pause() pc = PeriodicCall(reactor=reactor, schedule=Schedule(period=1), autoStart=True) pc.pause() pc.observer_init() self.assertEquals([], reactor.calledMethodNames()) @stderr_replaced def testPausePausesWhenRunning(self): self.newDNA(schedule=Schedule(period=1), autoStart=True) list(compose(self.dna.once.observer_init())) self.assertEquals(['addTimer'], self.reactor.calledMethodNames()) addTimer, = self.reactor.calledMethods # pauses after completing current task self.pc.pause() self.reactor.calledMethods.reset() addTimer.args[1]() self.assertEquals(['addProcess'], self.reactor.calledMethodNames()) addProcess, = self.reactor.calledMethods self.reactor.calledMethods.reset() addProcess.args[0]() self.assertEquals(['handle'], self.observer.calledMethodNames()) self.assertEquals(['removeProcess'], self.reactor.calledMethodNames()) def testPauseRemovesInitialPendingTimer(self): self.assertEquals(['addTimer'], self.reactor.calledMethodNames()) self.reactor.calledMethods.reset() with stderr_replaced() as err: self.pc.pause() self.assertEquals('%s: paused\n' % repr(self.pc), err.getvalue()) self.assertEquals(['removeTimer'], self.reactor.calledMethodNames()) removeTimer, = self.reactor.calledMethods self.assertEquals((('TOKEN',), {}), (removeTimer.args, removeTimer.kwargs)) def testPauseRemovesEndOfProcessPendingTimer(self): self.assertEquals(['addTimer'], self.reactor.calledMethodNames()) addTimer, = self.reactor.calledMethods self.reactor.calledMethods.reset() # Process until timer added again addTimer.args[1]() addProcess, = self.reactor.calledMethods self.reactor.calledMethods.reset() addProcess.args[0]() self.assertEquals(['removeProcess', 'addTimer'], self.reactor.calledMethodNames()) self.reactor.calledMethods.reset() # pause at pending timer with stderr_replaced() as err: self.pc.pause() self.assertEquals('%s: paused\n' % repr(self.pc), err.getvalue()) self.assertEquals(['removeTimer'], self.reactor.calledMethodNames()) removeTimer, = self.reactor.calledMethods self.assertEquals((('TOKEN',), {}), (removeTimer.args, removeTimer.kwargs)) def testResumeStartsWhenPaused(self): self.newDNA(schedule=Schedule(period=2), autoStart=False) list(compose(self.dna.once.observer_init())) self.assertEquals([], self.reactor.calledMethodNames()) with stderr_replaced() as err: self.pc.resume() self.assertEquals('%s: resumed\n' % repr(self.pc), err.getvalue()) self.assertEquals(['addTimer'], self.reactor.calledMethodNames()) addTimer, = self.reactor.calledMethods self.assertEquals(self.pc._periodicCall, addTimer.args[1]) # finish one task (1/2) self.reactor.calledMethods.reset() addTimer.args[1]() addProcess, = self.reactor.calledMethods # finish one task (2/2) self.reactor.calledMethods.reset() addProcess.args[0]() self.assertEquals(['removeProcess', 'addTimer'], self.reactor.calledMethodNames()) @stderr_replaced def testDoNotResumeNotPaused(self): self.newDNA(schedule=Schedule(period=1), autoStart=True) self.pc.resume() self.assertEquals([], self.reactor.calledMethodNames()) list(compose(self.dna.once.observer_init())) self.assertEquals(['addTimer'], self.reactor.calledMethodNames()) addTimer, = self.reactor.calledMethods self.reactor.calledMethods.reset() self.pc.resume() self.assertEquals([], self.reactor.calledMethodNames()) addTimer.args[1]() self.assertEquals(['addProcess'], self.reactor.calledMethodNames()) self.reactor.calledMethods.reset() self.pc.resume() self.assertEquals([], self.reactor.calledMethodNames()) @stderr_replaced def testResumeOnlyAddsATimerWhenNotBusy(self): # Because at the end of the busy / reactor processing it # will add a timer when not paused. addTimer, = self.reactor.calledMethods # "Firing" timer callback self.reactor.calledMethods.reset() addTimer.args[1]() self.assertEquals(['addProcess'], self.reactor.calledMethodNames()) addProcess, = self.reactor.calledMethods busybusy = [] def handleBusyMock(): yield busybusy.append(True) yield busybusy.append(True) self.observer.methods['handle'] = handleBusyMock # Busy processing, so pause won't work immediately self.reactor.calledMethods.reset() self.pc.pause() # <-- pause self.assertEquals([], self.reactor.calledMethodNames()) self.assertEquals([], busybusy) addProcess.args[0]() # <-- doing stuff self.assertEquals([], self.reactor.calledMethodNames()) self.assertEquals([True], busybusy) self.pc.resume() # <-- resume called addProcess.args[0]() # <-- doing more stuff self.assertEquals([True, True], busybusy) self.assertEquals([], self.reactor.calledMethodNames()) addProcess.args[0]() # <-- done doing and delayed resume (addTimer) self.assertEquals(['removeProcess', 'addTimer'], self.reactor.calledMethodNames()) @stderr_replaced def testGetState(self): state = self.pc.getState() self.assertEquals('obs_name', state.name) self.assertEquals(False, state.paused) self.assertEquals(Schedule(period=3600), state.schedule) self.assertEquals({'paused': False, 'name': 'obs_name', 'schedule': Schedule(period=3600)}, state.asDict()) self.pc.observable_setName('dashy') self.assertEquals('dashy', state.name) self.assertEquals(None, state.errorState) self.pc.setSchedule(schedule=Schedule(period=5)) self.assertEquals(Schedule(period=5), state.schedule) self.pc.pause() self.assertEquals(True, state.paused) self.pc.resume() addTimer = self.reactor.calledMethods[-1] self.reactor.calledMethods.reset() self.assertEquals(False, state.paused) addTimer.args[1]() self.pc.pause() self.assertEquals(False, state.paused) addProcess = self.reactor.calledMethods[-1] addProcess.args[0]() self.assertEquals(True, state.paused) def testRepr(self): self.assertEquals("PeriodicCall(name='obs_name', paused=False, schedule=Schedule(period=3600))", repr(self.pc)) def testShorten(self): message = 'x' * (MAX_LENGTH) self.assertEquals(message, message) message = 'x' * (MAX_LENGTH) + 'z' self.assertNotEqual(message, shorten(message)) self.assertEquals(MAX_LENGTH + len(' ... '), len(shorten(message))) self.assertTrue(' ... ' in shorten(message), shorten(message)) self.assertTrue(shorten(message).startswith('xxxx'), shorten(message)) self.assertTrue(shorten(message).endswith('xxxz'), shorten(message)) def testMessageConfigurable(self): self.newDNA(message="aMessage", schedule=Schedule(period=3600), errorSchedule=Schedule(period=15), prio=9, name='obs_name') list(compose(self.dna.once.observer_init())) self.assertEquals(['addTimer'], self.reactor.calledMethodNames()) addTimer, = self.reactor.calledMethods callback = addTimer.args[1] self.reactor.calledMethods.reset() callback() self.assertEquals(['addProcess'], self.reactor.calledMethodNames()) addProcess, = self.reactor.calledMethods callback = addProcess.args[0] self.reactor.calledMethods.reset() callback() self.assertEquals(['aMessage'], self.observer.calledMethodNames())