~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/services/job/tests/test_runner.py

Merge db-devel.

Show diffs side-by-side

added added

removed removed

Lines of Context:
8
8
from textwrap import dedent
9
9
from time import sleep
10
10
 
 
11
from testtools.testcase import ExpectedException
11
12
import transaction
12
13
from zope.interface import implements
13
14
 
21
22
from lp.services.job.interfaces.job import (
22
23
    IRunnableJob,
23
24
    JobStatus,
 
25
    SuspendJobException,
24
26
    )
25
27
from lp.services.job.model.job import Job
26
28
from lp.services.job.runner import (
37
39
    TestCaseWithFactory,
38
40
    ZopeTestInSubProcess,
39
41
    )
 
42
from lp.testing.fakemethod import FakeMethod
40
43
from lp.testing.mail_helpers import pop_notifications
41
44
 
42
45
 
116
119
        raise RaisingJobException('oops notifying users')
117
120
 
118
121
 
 
122
class RetryError(Exception):
 
123
    pass
 
124
 
 
125
 
 
126
class RaisingRetryJob(NullJob):
 
127
 
 
128
    retry_error_types = (RetryError,)
 
129
 
 
130
    max_retries = 1
 
131
 
 
132
    def run(self):
 
133
        raise RetryError()
 
134
 
 
135
 
119
136
class TestJobRunner(TestCaseWithFactory):
120
137
    """Ensure JobRunner behaves as expected."""
121
138
 
161
178
    def test_runAll_reports_oopses(self):
162
179
        """When an error is encountered, report an oops and continue."""
163
180
        job_1, job_2 = self.makeTwoJobs()
 
181
 
164
182
        def raiseError():
165
183
            # Ensure that jobs which call transaction.abort work, too.
166
184
            transaction.abort()
181
199
    def test_oops_messages_used_when_handling(self):
182
200
        """Oops messages should appear even when exceptions are handled."""
183
201
        job_1, job_2 = self.makeTwoJobs()
 
202
 
184
203
        def handleError():
185
204
            reporter = errorlog.globalErrorUtility
186
205
            try:
216
235
    def test_runAll_mails_oopses(self):
217
236
        """Email interested parties about OOPses."""
218
237
        job_1, job_2 = self.makeTwoJobs()
 
238
 
219
239
        def raiseError():
220
240
            # Ensure that jobs which call transaction.abort work, too.
221
241
            transaction.abort()
243
263
        error messages are mailed to interested parties verbatim.
244
264
        """
245
265
        job_1, job_2 = self.makeTwoJobs()
 
266
 
246
267
        class ExampleError(Exception):
247
268
            pass
 
269
 
248
270
        def raiseError():
249
271
            raise ExampleError('Fake exception.  Foobar, I say!')
250
272
        job_1.run = raiseError
272
294
        """
273
295
        runner = JobRunner([object()])
274
296
        self.assertRaises(TypeError, runner.runAll)
 
297
 
275
298
        class Runnable:
276
299
            implements(IRunnableJob)
277
300
        runner = JobRunner([Runnable()])
301
324
        runner.runJobHandleError(job)
302
325
        self.assertEqual(0, len(self.oopses))
303
326
 
 
327
    def test_runJob_raising_retry_error(self):
 
328
        """If a job raises a retry_error, it should be re-queued."""
 
329
        job = RaisingRetryJob('completion')
 
330
        runner = JobRunner([job])
 
331
        with self.expectedLog('Scheduling retry due to RetryError'):
 
332
            runner.runJob(job)
 
333
        self.assertEqual(JobStatus.WAITING, job.status)
 
334
        self.assertNotIn(job, runner.completed_jobs)
 
335
        self.assertIn(job, runner.incomplete_jobs)
 
336
 
 
337
    def test_runJob_exceeding_max_retries(self):
 
338
        """If a job exceeds maximum retries, it should raise normally."""
 
339
        job = RaisingRetryJob('completion')
 
340
        JobRunner([job]).runJob(job)
 
341
        self.assertEqual(JobStatus.WAITING, job.status)
 
342
        runner = JobRunner([job])
 
343
        with self.expectedLog('Job execution raised an exception.'):
 
344
            with ExpectedException(RetryError, ''):
 
345
                runner.runJob(job)
 
346
        self.assertEqual(JobStatus.FAILED, job.status)
 
347
        self.assertNotIn(job, runner.completed_jobs)
 
348
        self.assertIn(job, runner.incomplete_jobs)
 
349
 
304
350
    def test_runJobHandleErrors_oops_generated_notify_fails(self):
305
351
        """A second oops is logged if the notification of the oops fails."""
306
352
        job = RaisingJobRaisingNotifyOops('boom')
319
365
        runner.runJobHandleError(job)
320
366
        self.assertEqual(1, len(self.oopses))
321
367
 
 
368
    def test_runJob_with_SuspendJobException(self):
 
369
        # A job that raises SuspendJobError should end up suspended.
 
370
        job = NullJob('suspended')
 
371
        job.run = FakeMethod(failure=SuspendJobException())
 
372
        runner = JobRunner([job])
 
373
        runner.runJob(job)
 
374
 
 
375
        self.assertEqual(JobStatus.SUSPENDED, job.status)
 
376
        self.assertNotIn(job, runner.completed_jobs)
 
377
        self.assertIn(job, runner.incomplete_jobs)
 
378
 
322
379
 
323
380
class StuckJob(BaseRunnableJob):
324
381
    """Simulation of a job that stalls."""