Skip to content
This repository
Browse code

Resolve #202 and add a unit test and acceptance test.

  • Loading branch information...
commit b7e33acbc2e1ed54ba16a2792d5176abff585e4f 1 parent 9de44d4
Daniel Nephin authored February 19, 2013
79  tests/core/jobrun_test.py
... ...
@@ -1,11 +1,12 @@
1 1
 import datetime
  2
+import mock
2 3
 import pytz
3 4
 from testify import TestCase, setup, assert_equal
4 5
 from testify.assertions import assert_in
5 6
 from tests.assertions import assert_length, assert_raises, assert_call
6 7
 from tests.mocks import MockNode
7 8
 from tron.core import jobrun, actionrun
8  
-from tests.testingutils import Turtle
  9
+from tests.testingutils import Turtle, autospec_method
9 10
 from tests import testingutils
10 11
 
11 12
 
@@ -38,11 +39,10 @@ def setup_jobrun(self):
38 39
         self.job_run = jobrun.JobRun('jobname', 7, self.run_time, node,
39 40
                 action_runs=Turtle(
40 41
                     action_runs_with_cleanup=[],
41  
-                    get_startable_action_runs=lambda: [],
42  
-                    is_active=False
43  
-                ))
  42
+                    get_startable_action_runs=lambda: []))
44 43
         self.job_run.watch = Turtle()
45 44
         self.job_run.notify = Turtle()
  45
+        self.action_run = mock.create_autospec(actionrun.ActionRun)
46 46
 
47 47
     def test__init__(self):
48 48
         assert_equal(self.job_run.job_name, 'jobname')
@@ -184,50 +184,61 @@ def failing():
184 184
         assert_equal(started_runs, [])
185 185
 
186 186
     def test_handler_not_end_state_event(self):
187  
-        self.job_run.finalize = Turtle()
188  
-        self.job_run.handler(None, actionrun.ActionRun.STATE_STARTING)
189  
-        assert_length(self.job_run.finalize.calls, 0)
  187
+        autospec_method(self.job_run.finalize)
  188
+        autospec_method(self.job_run._start_action_runs)
  189
+        self.action_run.is_done = False
  190
+        self.job_run.handler(self.action_run, mock.Mock())
  191
+        assert not self.job_run.finalize.mock_calls
  192
+        assert not self.job_run._start_action_runs.mock_calls
190 193
 
191 194
     def test_handler_with_startable(self):
192  
-        self.job_run.action_runs.get_startable_action_runs = lambda: True
193  
-        startable_run = Turtle()
  195
+        startable_run = mock.create_autospec(actionrun.ActionRun)
194 196
         self.job_run.action_runs.get_startable_action_runs = lambda: [startable_run]
195  
-        self.job_run.finalize = Turtle()
  197
+        autospec_method(self.job_run.finalize)
  198
+        self.action_run.is_broken = False
196 199
 
197  
-        self.job_run.handler(None, actionrun.ActionRun.STATE_SUCCEEDED)
  200
+        self.job_run.handler(self.action_run, mock.Mock())
198 201
         assert_call(self.job_run.notify, 0, self.job_run.NOTIFY_STATE_CHANGED)
199  
-        assert_call(startable_run.start, 0)
200  
-        assert_length(self.job_run.finalize.calls, 0)
  202
+        startable_run.start.assert_called_with()
  203
+        assert not self.job_run.finalize.mock_calls
201 204
 
202  
-    def test_handler_not_done(self):
  205
+    def test_handler_is_active(self):
203 206
         self.job_run.action_runs.is_active = True
204  
-        self.job_run._start_action_runs = lambda: []
205  
-        self.job_run.finalize = Turtle()
206  
-
207  
-        self.job_run.handler(None, actionrun.ActionRun.STATE_SUCCEEDED)
208  
-        assert_length(self.job_run.finalize.calls, 0)
  207
+        autospec_method(self.job_run._start_action_runs, return_value=[])
  208
+        autospec_method(self.job_run.finalize)
  209
+        self.job_run.handler(self.action_run, mock.Mock())
  210
+        assert not self.job_run.finalize.mock_calls
209 211
 
210 212
     def test_handler_finished_without_cleanup(self):
  213
+        self.job_run.action_runs.is_active = False
  214
+        self.job_run.action_runs.is_scheduled = False
211 215
         self.job_run.action_runs.cleanup_action_run = None
212  
-        self.job_run.finalize = Turtle()
213  
-
214  
-        self.job_run.handler(None, actionrun.ActionRun.STATE_SUCCEEDED)
215  
-        assert_call(self.job_run.finalize, 0)
  216
+        autospec_method(self.job_run.finalize)
  217
+        self.job_run.handler(self.action_run, mock.Mock())
  218
+        self.job_run.finalize.assert_called_with()
216 219
 
217 220
     def test_handler_finished_with_cleanup_done(self):
218  
-        self.job_run.action_runs.cleanup_action_run = Turtle(is_done=True)
219  
-        self.job_run.finalize = Turtle()
220  
-
221  
-        self.job_run.handler(None, actionrun.ActionRun.STATE_SUCCEEDED)
222  
-        assert_call(self.job_run.finalize, 0)
  221
+        self.job_run.action_runs.is_active = False
  222
+        self.job_run.action_runs.is_scheduled = False
  223
+        self.job_run.action_runs.cleanup_action_run = mock.Mock(is_done=True)
  224
+        autospec_method(self.job_run.finalize)
  225
+        self.job_run.handler(self.action_run, mock.Mock())
  226
+        self.job_run.finalize.assert_called_with()
223 227
 
224 228
     def test_handler_finished_with_cleanup(self):
225  
-        self.job_run.action_runs.cleanup_action_run = Turtle(is_done=False)
226  
-        self.job_run.finalize = Turtle()
227  
-
228  
-        self.job_run.handler(None, actionrun.ActionRun.STATE_SUCCEEDED)
229  
-        assert_length(self.job_run.finalize.calls, 0)
230  
-        assert_call(self.job_run.action_runs.cleanup_action_run.start, 0)
  229
+        self.job_run.action_runs.is_active = False
  230
+        self.job_run.action_runs.is_scheduled = False
  231
+        self.job_run.action_runs.cleanup_action_run = mock.Mock(is_done=False)
  232
+        autospec_method(self.job_run.finalize)
  233
+        self.job_run.handler(self.action_run, mock.Mock())
  234
+        assert not self.job_run.finalize.mock_calls
  235
+        self.job_run.action_runs.cleanup_action_run.start.assert_called_with()
  236
+
  237
+    def test_handler_action_run_cancelled(self):
  238
+        self.action_run.is_broken = True
  239
+        autospec_method(self.job_run._start_action_runs)
  240
+        self.job_run.handler(self.action_run, mock.Mock())
  241
+        assert not self.job_run._start_action_runs.mock_calls
231 242
 
232 243
     def test_state(self):
233 244
         assert_equal(self.job_run.state, actionrun.ActionRun.STATE_SUCCEEDED)
36  tests/trond_test.py
@@ -339,3 +339,39 @@ def wait_on_service_start():
339 339
         def wait_on_service_stop():
340 340
             return client.service(service_url)['state'] == 'DOWN'
341 341
         sandbox.wait_on_sandbox(wait_on_service_stop)
  342
+
  343
+    def test_job_queueing_false_with_overlap(self):
  344
+        """Test that a job that has queueing false properly cancels an
  345
+        overlapping job run.
  346
+        """
  347
+        config = BASIC_CONFIG + dedent("""
  348
+            jobs:
  349
+                -   name: "cancel_overlap"
  350
+                    schedule: "interval 1s"
  351
+                    queueing: False
  352
+                    node: local
  353
+                    actions:
  354
+                        -   name: "do_something"
  355
+                            command: "sleep 3s"
  356
+                        -   name: "do_other"
  357
+                            command: "sleep 3s"
  358
+                    cleanup_action:
  359
+                        command: "echo done"
  360
+        """)
  361
+        client = self.sandbox.client
  362
+        self.sandbox.save_config(config)
  363
+        self.sandbox.trond()
  364
+        job_run = client.get_url('MASTER.cancel_overlap')
  365
+        job_run_url = client.get_url('MASTER.cancel_overlap.1')
  366
+
  367
+        def wait_on_job_schedule():
  368
+            return len(client.job(job_run)['runs']) == 2
  369
+        sandbox.wait_on_sandbox(wait_on_job_schedule)
  370
+
  371
+        def wait_on_job_cancel():
  372
+            return client.job(job_run_url)['state'] == 'CANC'
  373
+        sandbox.wait_on_sandbox(wait_on_job_cancel)
  374
+
  375
+        action_run_states = [action_run['state'] for action_run in
  376
+                             client.job(job_run_url)['runs']]
  377
+        assert_equal(action_run_states, ['CANC'] * len(action_run_states))
15  tron/core/jobrun.py
@@ -184,8 +184,7 @@ def _do_start(self):
184 184
         log.info("Starting JobRun %s", self.id)
185 185
 
186 186
         self.action_runs.ready()
187  
-        started_runs = self._start_action_runs()
188  
-        if any(started_runs):
  187
+        if any(self._start_action_runs()):
189 188
             self.notify(self.EVENT_STARTED)
190 189
             return True
191 190
 
@@ -203,21 +202,20 @@ def _start_action_runs(self):
203 202
 
204 203
         return started_actions
205 204
 
206  
-    def handle_action_run_state_change(self, _action_run, event):
  205
+    def handle_action_run_state_change(self, action_run, _):
207 206
         """Handle events triggered by JobRuns."""
208 207
         # propagate all state changes (from action runs) up to state serializer
209 208
         self.notify(self.NOTIFY_STATE_CHANGED)
210 209
 
211  
-        if event not in ActionRun.END_STATES:
  210
+        if not action_run.is_done:
212 211
             return
213 212
 
214  
-        started_actions = self._start_action_runs()
215  
-        if any(started_actions):
  213
+        if not action_run.is_broken and any(self._start_action_runs()):
216 214
             log.info("Action runs started for %s." % self)
217 215
             return
218 216
 
219  
-        if self.action_runs.is_active:
220  
-            log.info("%s still has running actions." % self)
  217
+        if self.action_runs.is_active or self.action_runs.is_scheduled:
  218
+            log.info("%s still has running or scheduled actions." % self)
221 219
             return
222 220
 
223 221
         # If we can't make any progress, we're done
@@ -225,6 +223,7 @@ def handle_action_run_state_change(self, _action_run, event):
225 223
         if not cleanup_run or cleanup_run.is_done:
226 224
             return self.finalize()
227 225
 
  226
+        # TODO: remove in (0.6), start() no longer raises an exception
228 227
         # When a job is being disabled, or the daemon is being shut down a bunch
229 228
         # of ActionRuns will be cancelled/failed. This would cause cleanup
230 229
         # action to be triggered more then once. Guard against that.

0 notes on commit b7e33ac

Please sign in to comment.
Something went wrong with that request. Please try again.