From c7d51ec92cc16323266fc2963bacdac615aca220 Mon Sep 17 00:00:00 2001 From: Lakshmi Kannan Date: Thu, 18 Dec 2014 23:15:59 +0000 Subject: [PATCH 1/3] Add test cases for timer --- st2reactor/st2reactor/timer/base.py | 8 +++- st2reactor/tests/test_timer.py | 74 +++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 st2reactor/tests/test_timer.py diff --git a/st2reactor/st2reactor/timer/base.py b/st2reactor/st2reactor/timer/base.py index fb53c7eced..d83cb163ba 100644 --- a/st2reactor/st2reactor/timer/base.py +++ b/st2reactor/st2reactor/timer/base.py @@ -57,7 +57,10 @@ def cleanup(self): self._scheduler.shutdown(wait=True) def add_trigger(self, trigger): - self._add_job_to_scheduler(trigger) + try: + self._add_job_to_scheduler(trigger) + except: + LOG.exception('Unable to add timer for trigger: %s', trigger) def update_trigger(self, trigger): self.remove_trigger(trigger) @@ -111,6 +114,8 @@ def _add_job_to_scheduler(self, trigger): self._add_job(trigger, time_type) def _add_job(self, trigger, time_type, replace=True): + if self._jobs.get(trigger['id'], None): + raise Exception('Should not try to register timer for trigger %s' % trigger) try: job = self._scheduler.add_job(self._emit_trigger_instance, trigger=time_type, @@ -127,7 +132,6 @@ def _emit_trigger_instance(self, trigger): payload = { 'executed_at': str(datetime.utcnow()), - 'schedule': trigger['parameters'].get('time') } self._trigger_dispatcher.dispatch(trigger, payload) diff --git a/st2reactor/tests/test_timer.py b/st2reactor/tests/test_timer.py new file mode 100644 index 0000000000..313c083618 --- /dev/null +++ b/st2reactor/tests/test_timer.py @@ -0,0 +1,74 @@ +import copy + +import bson +import unittest2 + +from st2common.constants.pack import SYSTEM_PACK_NAME +from st2common.models.db.reactor import TriggerDB +from st2common.models.system.common import ResourceReference +from st2reactor.timer.base import St2Timer +import st2tests.config as tests_config + + +class TestDispatcher(object): + def __init__(self): + self.trigger = None + self.payload = None + + def dispatch(self, trigger, payload): + self.trigger = trigger + self.payload = payload + + def assert_payload(self): + return self.payload is not None and self.payload.get('executed_at', None) + + +class TimerTest(unittest2.TestCase): + test_trigger = None + + @classmethod + def setUpClass(cls): + super(TimerTest, cls).setUpClass() + tests_config.parse_args() + parameters = {} + parameters['unit'] = 'seconds' + parameters['delta'] = 30 + ref = ResourceReference.to_string_reference(SYSTEM_PACK_NAME, 'st2.IntervalTimer') + TimerTest.test_trigger = TriggerDB(name='testtimer', pack='test', parameters=parameters, + type=ref) + TimerTest.test_trigger.id = str(bson.ObjectId()) + + def test_add_remove_timer_trigger(self): + timer = St2Timer(local_timezone='America/Los_Angeles') + self.assertTrue(len(timer._scheduler.get_jobs()) == 0) + timer.add_trigger(TimerTest.test_trigger) + self.assertTrue(len(timer._scheduler.get_jobs()) == 1) + timer.remove_trigger(TimerTest.test_trigger) + self.assertTrue(len(timer._scheduler.get_jobs()) == 0) + + def test_emit_trigger_instance(self): + timer = St2Timer(local_timezone='America/Los_Angeles') + mock_dispatcher = TestDispatcher() + setattr(timer, '_trigger_dispatcher', mock_dispatcher) + timer._emit_trigger_instance(TimerTest.test_trigger) + self.assertTrue(mock_dispatcher.assert_payload()) + + def test_invalid_schema_timer(self): + timer = St2Timer(local_timezone='America/Los_Angeles') + fail_timer = copy.copy(TimerTest.test_trigger) + del fail_timer.parameters['unit'] + timer.add_trigger(fail_timer) + self.assertTrue(len(timer._scheduler.get_jobs()) == 0) + + def test_duplicate_timer_trigger(self): + timer = St2Timer(local_timezone='America/Los_Angeles') + self.assertTrue(len(timer._scheduler.get_jobs()) == 0) + timer.add_trigger(TimerTest.test_trigger) + self.assertTrue(len(timer._scheduler.get_jobs()) == 1) + try: + timer.add_trigger(TimerTest.test_trigger) + except: + self.assertTrue(len(timer._scheduler.get_jobs()) == 1) + pass + timer.remove_trigger(TimerTest.test_trigger) + self.assertTrue(len(timer._scheduler.get_jobs()) == 0) From a6c530134e97f6e555fea2fff27efd5d399e0902 Mon Sep 17 00:00:00 2001 From: Lakshmi Kannan Date: Thu, 18 Dec 2014 23:38:23 +0000 Subject: [PATCH 2/3] Add end-to-end test case --- st2reactor/tests/test_timer.py | 38 +++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/st2reactor/tests/test_timer.py b/st2reactor/tests/test_timer.py index 313c083618..551898865f 100644 --- a/st2reactor/tests/test_timer.py +++ b/st2reactor/tests/test_timer.py @@ -1,12 +1,29 @@ +# Licensed to the StackStorm, Inc ('StackStorm') under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + import copy import bson -import unittest2 +import eventlet +import mock from st2common.constants.pack import SYSTEM_PACK_NAME from st2common.models.db.reactor import TriggerDB from st2common.models.system.common import ResourceReference from st2reactor.timer.base import St2Timer +from st2tests.base import EventletTestCase import st2tests.config as tests_config @@ -23,7 +40,7 @@ def assert_payload(self): return self.payload is not None and self.payload.get('executed_at', None) -class TimerTest(unittest2.TestCase): +class TimerTest(EventletTestCase): test_trigger = None @classmethod @@ -32,7 +49,7 @@ def setUpClass(cls): tests_config.parse_args() parameters = {} parameters['unit'] = 'seconds' - parameters['delta'] = 30 + parameters['delta'] = 1 ref = ResourceReference.to_string_reference(SYSTEM_PACK_NAME, 'st2.IntervalTimer') TimerTest.test_trigger = TriggerDB(name='testtimer', pack='test', parameters=parameters, type=ref) @@ -72,3 +89,18 @@ def test_duplicate_timer_trigger(self): pass timer.remove_trigger(TimerTest.test_trigger) self.assertTrue(len(timer._scheduler.get_jobs()) == 0) + + @mock.patch.object(St2Timer, '_register_timer_trigger_types', mock.MagicMock()) + def test_timer_end_to_end(self): + timer = St2Timer(local_timezone='America/Los_Angeles') + mock_dispatcher = TestDispatcher() + setattr(timer, '_trigger_dispatcher', mock_dispatcher) + timer.add_trigger(TimerTest.test_trigger) + + def kickoff_timer(timer): + timer.start() + + eventlet.spawn(kickoff_timer, timer) + eventlet.sleep(2) + self.assertTrue(mock_dispatcher.assert_payload()) + timer.cleanup() From fe5332ba2ef6e92a5780e5d0b7268bd171956e95 Mon Sep 17 00:00:00 2001 From: Lakshmi Kannan Date: Thu, 18 Dec 2014 23:45:25 +0000 Subject: [PATCH 3/3] assert that mock dispatcher is not called before timer start --- st2reactor/tests/test_timer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/st2reactor/tests/test_timer.py b/st2reactor/tests/test_timer.py index 551898865f..b7052cd3e6 100644 --- a/st2reactor/tests/test_timer.py +++ b/st2reactor/tests/test_timer.py @@ -101,6 +101,7 @@ def kickoff_timer(timer): timer.start() eventlet.spawn(kickoff_timer, timer) + self.assertFalse(mock_dispatcher.assert_payload()) eventlet.sleep(2) self.assertTrue(mock_dispatcher.assert_payload()) timer.cleanup()