blob: bf8f0f504b325a571ac34e6392e907e8dc947a54 [file] [log] [blame]
Chris Masone6fed6462011-10-20 16:36:43 -07001#!/usr/bin/python
2#
3# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Unit tests for server/cros/dynamic_suite.py."""
8
9import logging
10import mox
11import shutil
12import tempfile
13import time
14import unittest
15
Chris Masone5552dd72012-02-15 15:01:04 -080016from autotest_lib.client.common_lib import base_job, control_data, global_config
Chris Masone6fed6462011-10-20 16:36:43 -070017from autotest_lib.server.cros import control_file_getter, dynamic_suite
18from autotest_lib.server import frontend
19
20class FakeJob(object):
21 """Faked out RPC-client-side Job object."""
22 def __init__(self, id=0, statuses=[]):
23 self.id = id
24 self.name = 'Fake Job %d' % self.id
25 self.statuses = statuses
26
27
28class ReimagerTest(mox.MoxTestBase):
29 """Unit tests for dynamic_suite.Reimager.
30
31 @var _URL: fake image url
Chris Masone8b7cd422012-02-22 13:16:11 -080032 @var _BUILD: fake build
Chris Masone6fed6462011-10-20 16:36:43 -070033 @var _NUM: fake number of machines to run on
34 @var _BOARD: fake board to reimage
35 """
36
Chris Masone2ef1d4e2011-12-20 11:06:53 -080037 _URL = 'http://nothing/%s'
Chris Masone8b7cd422012-02-22 13:16:11 -080038 _BUILD = 'build'
Chris Masone6fed6462011-10-20 16:36:43 -070039 _NUM = 4
40 _BOARD = 'board'
Chris Masone5552dd72012-02-15 15:01:04 -080041 _CONFIG = global_config.global_config
Chris Masone6fed6462011-10-20 16:36:43 -070042
43
44 def setUp(self):
45 super(ReimagerTest, self).setUp()
46 self.afe = self.mox.CreateMock(frontend.AFE)
47 self.tko = self.mox.CreateMock(frontend.TKO)
48 self.reimager = dynamic_suite.Reimager('', afe=self.afe, tko=self.tko)
Chris Masone5552dd72012-02-15 15:01:04 -080049 self._CONFIG.override_config_value('CROS',
50 'sharding_factor',
51 "%d" % self._NUM)
Chris Masone6fed6462011-10-20 16:36:43 -070052
53
54 def testEnsureVersionLabelAlreadyExists(self):
55 """Should not create a label if it already exists."""
56 name = 'label'
57 self.afe.get_labels(name=name).AndReturn([name])
58 self.mox.ReplayAll()
59 self.reimager._ensure_version_label(name)
60
61
62 def testEnsureVersionLabel(self):
63 """Should create a label if it doesn't already exist."""
64 name = 'label'
65 self.afe.get_labels(name=name).AndReturn([])
66 self.afe.create_label(name=name)
67 self.mox.ReplayAll()
68 self.reimager._ensure_version_label(name)
69
70
71 def testInjectVars(self):
72 """Should inject dict of varibles into provided strings."""
73 def find_all_in(d, s):
74 """Returns true if all key-value pairs in |d| are printed in |s|."""
75 return reduce(lambda b,i: "%s='%s'\n" % i in s, d.iteritems(), True)
76
77 v = {'v1': 'one', 'v2': 'two'}
Chris Masone8b764252012-01-17 11:12:51 -080078 self.assertTrue(find_all_in(v, dynamic_suite.inject_vars(v, '')))
79 self.assertTrue(find_all_in(v, dynamic_suite.inject_vars(v, 'ctrl')))
Chris Masone6fed6462011-10-20 16:36:43 -070080
81
82 def testReportResultsGood(self):
83 """Should report results in the case where all jobs passed."""
84 job = self.mox.CreateMock(frontend.Job)
85 job.name = 'RPC Client job'
86 job.result = True
87 recorder = self.mox.CreateMock(base_job.base_job)
88 recorder.record('GOOD', mox.IgnoreArg(), job.name)
89 self.mox.ReplayAll()
90 self.reimager._report_results(job, recorder.record)
91
92
93 def testReportResultsBad(self):
94 """Should report results in various job failure cases.
95
96 In this test scenario, there are five hosts, all the 'netbook' platform.
97
98 h1: Did not run
99 h2: Two failed tests
100 h3: Two aborted tests
101 h4: completed, GOOD
102 h5: completed, GOOD
103 """
104 H1 = 'host1'
105 H2 = 'host2'
106 H3 = 'host3'
107 H4 = 'host4'
108 H5 = 'host5'
109
110 class FakeResult(object):
111 def __init__(self, reason):
112 self.reason = reason
113
114
115 # The RPC-client-side Job object that is annotated with results.
116 job = FakeJob()
117 job.result = None # job failed, there are results to report.
118
119 # The semantics of |results_platform_map| and |test_results| are
120 # drawn from frontend.AFE.poll_all_jobs()
121 job.results_platform_map = {'netbook': {'Aborted' : [H3],
Chris Masone8b7cd422012-02-22 13:16:11 -0800122 'Completed' : [H1, H4, H5],
123 'Failed': [H2]
Chris Masone6fed6462011-10-20 16:36:43 -0700124 }
125 }
126 # Gin up fake results for H2 and H3 failure cases.
127 h2 = frontend.TestResults()
128 h2.fail = [FakeResult('a'), FakeResult('b')]
129 h3 = frontend.TestResults()
130 h3.fail = [FakeResult('a'), FakeResult('b')]
131 # Skipping H1 in |test_status| dict means that it did not get run.
132 job.test_status = {H2: h2, H3: h3, H4: {}, H5: {}}
133
134 # Set up recording expectations.
135 rjob = self.mox.CreateMock(base_job.base_job)
136 for res in h2.fail:
137 rjob.record('FAIL', mox.IgnoreArg(), H2, res.reason).InAnyOrder()
138 for res in h3.fail:
139 rjob.record('ABORT', mox.IgnoreArg(), H3, res.reason).InAnyOrder()
140 rjob.record('GOOD', mox.IgnoreArg(), H4).InAnyOrder()
141 rjob.record('GOOD', mox.IgnoreArg(), H5).InAnyOrder()
142 rjob.record(
143 'ERROR', mox.IgnoreArg(), H1, mox.IgnoreArg()).InAnyOrder()
144
145 self.mox.ReplayAll()
146 self.reimager._report_results(job, rjob.record)
147
148
149 def testScheduleJob(self):
150 """Should be able to create a job with the AFE."""
151 # Fake out getting the autoupdate control file contents.
152 cf_getter = self.mox.CreateMock(control_file_getter.ControlFileGetter)
153 cf_getter.get_control_file_contents_by_name('autoupdate').AndReturn('')
154 self.reimager._cf_getter = cf_getter
155
Chris Masone6dca10a2012-02-15 15:41:42 -0800156 self._CONFIG.override_config_value('CROS',
157 'image_url_pattern',
158 self._URL)
Chris Masone6fed6462011-10-20 16:36:43 -0700159 self.afe.create_job(
Chris Masone8b7cd422012-02-22 13:16:11 -0800160 control_file=mox.And(mox.StrContains(self._BUILD),
161 mox.StrContains(self._URL % self._BUILD)),
162 name=mox.StrContains(self._BUILD),
Chris Masone6fed6462011-10-20 16:36:43 -0700163 control_type='Server',
Chris Masone8b7cd422012-02-22 13:16:11 -0800164 meta_hosts=['board:'+self._BOARD] * self._NUM,
165 dependencies=[])
Chris Masone6fed6462011-10-20 16:36:43 -0700166 self.mox.ReplayAll()
Chris Masone8b7cd422012-02-22 13:16:11 -0800167 self.reimager._schedule_reimage_job(self._BUILD, self._NUM, self._BOARD)
Chris Masone6fed6462011-10-20 16:36:43 -0700168
169
170 def expect_attempt(self, success):
171 """Sets up |self.reimager| to expect an attempt() that returns |success|
172
173 @param success the value returned by poll_job_results()
174 @return a FakeJob configured with appropriate expectations
175 """
176 canary = FakeJob()
177 self.mox.StubOutWithMock(self.reimager, '_ensure_version_label')
Chris Masone8b7cd422012-02-22 13:16:11 -0800178 self.reimager._ensure_version_label(mox.StrContains(self._BUILD))
Chris Masone6fed6462011-10-20 16:36:43 -0700179
180 self.mox.StubOutWithMock(self.reimager, '_schedule_reimage_job')
Chris Masone8b7cd422012-02-22 13:16:11 -0800181 self.reimager._schedule_reimage_job(self._BUILD,
Chris Masone6fed6462011-10-20 16:36:43 -0700182 self._NUM,
183 self._BOARD).AndReturn(canary)
184 if success is not None:
185 self.mox.StubOutWithMock(self.reimager, '_report_results')
186 self.reimager._report_results(canary, mox.IgnoreArg())
187
188 self.afe.get_jobs(id=canary.id, not_yet_run=True).AndReturn([])
189 self.afe.get_jobs(id=canary.id, finished=True).AndReturn([canary])
190 self.afe.poll_job_results(mox.IgnoreArg(), canary, 0).AndReturn(success)
191
192 return canary
193
194
195 def testSuccessfulReimage(self):
196 """Should attempt a reimage and record success."""
197 canary = self.expect_attempt(True)
198
199 rjob = self.mox.CreateMock(base_job.base_job)
200 rjob.record('START', mox.IgnoreArg(), mox.IgnoreArg())
201 rjob.record('END GOOD', mox.IgnoreArg(), mox.IgnoreArg())
202 self.mox.ReplayAll()
Chris Masone8b7cd422012-02-22 13:16:11 -0800203 self.reimager.attempt(self._BUILD, self._BOARD, rjob.record)
Chris Masone6fed6462011-10-20 16:36:43 -0700204
205
206 def testFailedReimage(self):
207 """Should attempt a reimage and record failure."""
208 canary = self.expect_attempt(False)
209
210 rjob = self.mox.CreateMock(base_job.base_job)
211 rjob.record('START', mox.IgnoreArg(), mox.IgnoreArg())
212 rjob.record('END FAIL', mox.IgnoreArg(), mox.IgnoreArg())
213 self.mox.ReplayAll()
Chris Masone8b7cd422012-02-22 13:16:11 -0800214 self.reimager.attempt(self._BUILD, self._BOARD, rjob.record)
Chris Masone6fed6462011-10-20 16:36:43 -0700215
216
217 def testReimageThatNeverHappened(self):
218 """Should attempt a reimage and record that it didn't run."""
219 canary = self.expect_attempt(None)
220
221 rjob = self.mox.CreateMock(base_job.base_job)
222 rjob.record('START', mox.IgnoreArg(), mox.IgnoreArg())
223 rjob.record('FAIL', mox.IgnoreArg(), canary.name, mox.IgnoreArg())
224 rjob.record('END FAIL', mox.IgnoreArg(), mox.IgnoreArg())
225 self.mox.ReplayAll()
Chris Masone8b7cd422012-02-22 13:16:11 -0800226 self.reimager.attempt(self._BUILD, self._BOARD, rjob.record)
Chris Masone6fed6462011-10-20 16:36:43 -0700227
228
229class SuiteTest(mox.MoxTestBase):
230 """Unit tests for dynamic_suite.Suite.
231
Chris Masone8b7cd422012-02-22 13:16:11 -0800232 @var _BUILD: fake build
Chris Masone6fed6462011-10-20 16:36:43 -0700233 @var _TAG: fake suite tag
234 """
235
Chris Masone8b7cd422012-02-22 13:16:11 -0800236 _BUILD = 'build'
237 _TAG = 'suite_tag'
Chris Masone6fed6462011-10-20 16:36:43 -0700238
239
240 def setUp(self):
241 class FakeControlData(object):
242 """A fake parsed control file data structure."""
243 def __init__(self, data, expr=False):
244 self.string = 'text-' + data
245 self.name = 'name-' + data
246 self.data = data
247 self.test_type = 'Client'
248 self.experimental = expr
249
250
251 super(SuiteTest, self).setUp()
252 self.afe = self.mox.CreateMock(frontend.AFE)
253 self.tko = self.mox.CreateMock(frontend.TKO)
254
255 self.tmpdir = tempfile.mkdtemp(suffix=type(self).__name__)
256
257 self.getter = self.mox.CreateMock(control_file_getter.ControlFileGetter)
258
259 self.files = {'one': FakeControlData('data_one', expr=True),
260 'two': FakeControlData('data_two'),
261 'three': FakeControlData('data_three')}
262
263
264 def tearDown(self):
265 super(SuiteTest, self).tearDown()
266 shutil.rmtree(self.tmpdir, ignore_errors=True)
267
268
269 def expect_control_file_parsing(self):
270 """Expect an attempt to parse the 'control files' in |self.files|."""
271 self.getter.get_control_file_list().AndReturn(self.files.keys())
272 self.mox.StubOutWithMock(control_data, 'parse_control_string')
273 for file, data in self.files.iteritems():
274 self.getter.get_control_file_contents(file).AndReturn(data.string)
275 control_data.parse_control_string(
276 data.string, raise_warnings=True).AndReturn(data)
277
278
279 def testFindAndParseStableTests(self):
280 """Should find only non-experimental tests that match a predicate."""
281 self.expect_control_file_parsing()
282 self.mox.ReplayAll()
283
284 predicate = lambda d: d.text == self.files['two'].string
285 tests = dynamic_suite.Suite.find_and_parse_tests(self.getter, predicate)
286 self.assertEquals(len(tests), 1)
287 self.assertEquals(tests[0], self.files['two'])
288
289
290 def testFindAndParseTests(self):
291 """Should find all tests that match a predicate."""
292 self.expect_control_file_parsing()
293 self.mox.ReplayAll()
294
295 predicate = lambda d: d.text != self.files['two'].string
296 tests = dynamic_suite.Suite.find_and_parse_tests(self.getter,
297 predicate,
298 add_experimental=True)
299 self.assertEquals(len(tests), 2)
300 self.assertTrue(self.files['one'] in tests)
301 self.assertTrue(self.files['three'] in tests)
302
303
304 def mock_control_file_parsing(self):
305 """Fake out find_and_parse_tests(), returning content from |self.files|.
306 """
307 for test in self.files.values():
308 test.text = test.string # mimic parsing.
309 self.mox.StubOutWithMock(dynamic_suite.Suite, 'find_and_parse_tests')
310 dynamic_suite.Suite.find_and_parse_tests(
311 mox.IgnoreArg(),
312 mox.IgnoreArg(),
313 add_experimental=True).AndReturn(self.files.values())
314
315
316 def testStableUnstableFilter(self):
317 """Should distinguish between experimental and stable tests."""
318 self.mock_control_file_parsing()
319 self.mox.ReplayAll()
320 suite = dynamic_suite.Suite.create_from_name(self._TAG, self.tmpdir,
321 self.afe, self.tko)
322
323 self.assertTrue(self.files['one'] in suite.tests)
324 self.assertTrue(self.files['two'] in suite.tests)
325 self.assertTrue(self.files['one'] in suite.unstable_tests())
326 self.assertTrue(self.files['two'] in suite.stable_tests())
327 self.assertFalse(self.files['one'] in suite.stable_tests())
328 self.assertFalse(self.files['two'] in suite.unstable_tests())
329
330
331 def expect_job_scheduling(self, add_experimental):
332 """Expect jobs to be scheduled for 'tests' in |self.files|.
333
334 @param add_experimental: expect jobs for experimental tests as well.
335 """
336 for test in self.files.values():
337 if not add_experimental and test.experimental:
338 continue
339 self.afe.create_job(
340 control_file=test.text,
Chris Masone8b7cd422012-02-22 13:16:11 -0800341 name=mox.And(mox.StrContains(self._BUILD),
Chris Masone6fed6462011-10-20 16:36:43 -0700342 mox.StrContains(test.name)),
343 control_type=mox.IgnoreArg(),
Chris Masone8b7cd422012-02-22 13:16:11 -0800344 meta_hosts=[dynamic_suite.VERSION_PREFIX + self._BUILD],
345 dependencies=[])
Chris Masone6fed6462011-10-20 16:36:43 -0700346
347
348 def testScheduleTests(self):
349 """Should schedule stable and experimental tests with the AFE."""
350 self.mock_control_file_parsing()
351 self.expect_job_scheduling(add_experimental=True)
352
353 self.mox.ReplayAll()
Chris Masone8b7cd422012-02-22 13:16:11 -0800354 suite = dynamic_suite.Suite.create_from_name(self._TAG, self._BUILD,
Chris Masone6fed6462011-10-20 16:36:43 -0700355 self.afe, self.tko)
Chris Masone8b7cd422012-02-22 13:16:11 -0800356 suite.schedule()
Chris Masone6fed6462011-10-20 16:36:43 -0700357
358
359 def testScheduleStableTests(self):
360 """Should schedule only stable tests with the AFE."""
361 self.mock_control_file_parsing()
362 self.expect_job_scheduling(add_experimental=False)
363
364 self.mox.ReplayAll()
Chris Masone8b7cd422012-02-22 13:16:11 -0800365 suite = dynamic_suite.Suite.create_from_name(self._TAG, self._BUILD,
Chris Masone6fed6462011-10-20 16:36:43 -0700366 self.afe, self.tko)
Chris Masone8b7cd422012-02-22 13:16:11 -0800367 suite.schedule(add_experimental=False)
Chris Masone6fed6462011-10-20 16:36:43 -0700368
369
370 def expect_result_gathering(self, job):
371 self.afe.get_jobs(id=job.id, finished=True).AndReturn(job)
372 entries = map(lambda s: s.entry, job.statuses)
373 self.afe.run('get_host_queue_entries',
374 job=job.id).AndReturn(entries)
375 if True not in map(lambda e: 'aborted' in e and e['aborted'], entries):
376 self.tko.get_status_counts(job=job.id).AndReturn(job.statuses)
377
378
Chris Masonefef21382012-01-17 11:16:32 -0800379 def _createSuiteWithMockedTestsAndControlFiles(self):
380 """Create a Suite, using mocked tests and control file contents.
381
382 @return Suite object, after mocking out behavior needed to create it.
383 """
Chris Masone8b7cd422012-02-22 13:16:11 -0800384 self.mox.StubOutWithMock(dynamic_suite.Suite, 'create_cf_getter')
385 dynamic_suite.Suite.create_cf_getter(self.tmpdir).AndReturn(self.getter)
Chris Masonefef21382012-01-17 11:16:32 -0800386 self.expect_control_file_parsing()
387 self.mox.ReplayAll()
388 suite = dynamic_suite.Suite.create_from_name(self._TAG, self.tmpdir,
389 self.afe, self.tko)
390 self.mox.ResetAll()
391 return suite
392
393
Chris Masone6fed6462011-10-20 16:36:43 -0700394 def testWaitForResults(self):
395 """Should gather status and return records for job summaries."""
396 class FakeStatus(object):
397 """Fake replacement for server-side job status objects.
398
399 @var status: 'GOOD', 'FAIL', 'ERROR', etc.
400 @var test_name: name of the test this is status for
401 @var reason: reason for failure, if any
402 @var aborted: present and True if the job was aborted. Optional.
403 """
404 def __init__(self, code, name, reason, aborted=None):
405 self.status = code
406 self.test_name = name
407 self.reason = reason
408 self.entry = {}
409 if aborted:
410 self.entry['aborted'] = True
411
412 def equals_record(self, args):
413 """Compares this object to a recorded status."""
414 return self._equals_record(*args)
415
Chris Masone2ef1d4e2011-12-20 11:06:53 -0800416 def _equals_record(self, status, subdir, name, reason=None):
Chris Masone6fed6462011-10-20 16:36:43 -0700417 """Compares this object and fields of recorded status."""
418 if 'aborted' in self.entry and self.entry['aborted']:
419 return status == 'ABORT'
420 return (self.status == status and
421 self.test_name == name and
422 self.reason == reason)
423
Chris Masonefef21382012-01-17 11:16:32 -0800424 suite = self._createSuiteWithMockedTestsAndControlFiles()
Chris Masone6fed6462011-10-20 16:36:43 -0700425
426 jobs = [FakeJob(0, [FakeStatus('GOOD', 'T0', ''),
427 FakeStatus('GOOD', 'T1', '')]),
428 FakeJob(1, [FakeStatus('ERROR', 'T0', 'err', False),
429 FakeStatus('GOOD', 'T1', '')]),
430 FakeJob(2, [FakeStatus('TEST_NA', 'T0', 'no')]),
431 FakeJob(2, [FakeStatus('FAIL', 'T0', 'broken')]),
432 FakeJob(3, [FakeStatus('ERROR', 'T0', 'gah', True)])]
433 # To simulate a job that isn't ready the first time we check.
434 self.afe.get_jobs(id=jobs[0].id, finished=True).AndReturn([])
435 # Expect all the rest of the jobs to be good to go the first time.
436 for job in jobs[1:]:
437 self.expect_result_gathering(job)
438 # Then, expect job[0] to be ready.
439 self.expect_result_gathering(jobs[0])
440 # Expect us to poll twice.
441 self.mox.StubOutWithMock(time, 'sleep')
442 time.sleep(5)
443 time.sleep(5)
444 self.mox.ReplayAll()
445
Chris Masone6fed6462011-10-20 16:36:43 -0700446 suite._jobs = list(jobs)
Scott Zawalskiab25bd62012-02-10 18:29:12 -0500447 results = [result for result in suite.wait_for_results()]
Chris Masone6fed6462011-10-20 16:36:43 -0700448 for job in jobs:
449 for status in job.statuses:
450 self.assertTrue(True in map(status.equals_record, results))
451
452
453 def testRunAndWaitSuccess(self):
454 """Should record successful results."""
Chris Masonefef21382012-01-17 11:16:32 -0800455 suite = self._createSuiteWithMockedTestsAndControlFiles()
Chris Masone6fed6462011-10-20 16:36:43 -0700456
Chris Masonefef21382012-01-17 11:16:32 -0800457 results = [('GOOD', None, 'good'), ('FAIL', None, 'bad', 'reason')]
Chris Masone6fed6462011-10-20 16:36:43 -0700458 recorder = self.mox.CreateMock(base_job.base_job)
Scott Zawalskiab25bd62012-02-10 18:29:12 -0500459 recorder.record('INFO', None, 'Start %s' % self._TAG)
Chris Masone6fed6462011-10-20 16:36:43 -0700460 for result in results:
Scott Zawalskiab25bd62012-02-10 18:29:12 -0500461 status = result[0]
462 test_name = result[2]
463 recorder.record('START', None, test_name)
Chris Masone6fed6462011-10-20 16:36:43 -0700464 recorder.record(*result).InAnyOrder('results')
Scott Zawalskiab25bd62012-02-10 18:29:12 -0500465 recorder.record('END %s' % status, None, test_name)
Chris Masone6fed6462011-10-20 16:36:43 -0700466
Chris Masone6fed6462011-10-20 16:36:43 -0700467 self.mox.StubOutWithMock(suite, 'schedule')
Chris Masone8b7cd422012-02-22 13:16:11 -0800468 suite.schedule(True)
Chris Masone6fed6462011-10-20 16:36:43 -0700469 self.mox.StubOutWithMock(suite, 'wait_for_results')
470 suite.wait_for_results().AndReturn(results)
471 self.mox.ReplayAll()
472
Chris Masone8b7cd422012-02-22 13:16:11 -0800473 suite.run_and_wait(recorder.record, True)
Chris Masone6fed6462011-10-20 16:36:43 -0700474
475
476 def testRunAndWaitFailure(self):
477 """Should record failure to gather results."""
Chris Masonefef21382012-01-17 11:16:32 -0800478 suite = self._createSuiteWithMockedTestsAndControlFiles()
479
Chris Masone6fed6462011-10-20 16:36:43 -0700480 recorder = self.mox.CreateMock(base_job.base_job)
Scott Zawalskiab25bd62012-02-10 18:29:12 -0500481 recorder.record('INFO', None, 'Start %s' % self._TAG)
482 recorder.record('FAIL', None, self._TAG,
483 mox.StrContains('waiting'))
Chris Masone6fed6462011-10-20 16:36:43 -0700484
Chris Masone6fed6462011-10-20 16:36:43 -0700485 self.mox.StubOutWithMock(suite, 'schedule')
Chris Masone8b7cd422012-02-22 13:16:11 -0800486 suite.schedule(True)
Chris Masone6fed6462011-10-20 16:36:43 -0700487 self.mox.StubOutWithMock(suite, 'wait_for_results')
488 suite.wait_for_results().AndRaise(Exception())
489 self.mox.ReplayAll()
490
Chris Masone8b7cd422012-02-22 13:16:11 -0800491 suite.run_and_wait(recorder.record, True)
Chris Masone6fed6462011-10-20 16:36:43 -0700492
493
494 def testRunAndWaitScheduleFailure(self):
Chris Masonefef21382012-01-17 11:16:32 -0800495 """Should record failure to schedule jobs."""
496 suite = self._createSuiteWithMockedTestsAndControlFiles()
497
Chris Masone6fed6462011-10-20 16:36:43 -0700498 recorder = self.mox.CreateMock(base_job.base_job)
Scott Zawalskiab25bd62012-02-10 18:29:12 -0500499 recorder.record('INFO', None, 'Start %s' % self._TAG)
500 recorder.record('FAIL', None, self._TAG,
501 mox.StrContains('scheduling'))
Chris Masone6fed6462011-10-20 16:36:43 -0700502
Chris Masone6fed6462011-10-20 16:36:43 -0700503 self.mox.StubOutWithMock(suite, 'schedule')
Chris Masone8b7cd422012-02-22 13:16:11 -0800504 suite.schedule(True).AndRaise(Exception())
Chris Masone6fed6462011-10-20 16:36:43 -0700505 self.mox.ReplayAll()
506
Chris Masone8b7cd422012-02-22 13:16:11 -0800507 suite.run_and_wait(recorder.record, True)
Chris Masone6fed6462011-10-20 16:36:43 -0700508
509
510if __name__ == '__main__':
511 unittest.main()