blob: 81bb8483a07187b5e44b9856c12f0a6050fcedd8 [file] [log] [blame]
Chris Masone6fed6462011-10-20 16:36:43 -07001#!/usr/bin/python
2#
Chris Masoneab3e7332012-02-29 18:54:58 -08003# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Chris Masone6fed6462011-10-20 16:36:43 -07004# 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
Scott Zawalskie5bb1c52012-02-29 13:15:50 -050024 self.owner = 'tester'
Chris Masone6fed6462011-10-20 16:36:43 -070025 self.name = 'Fake Job %d' % self.id
26 self.statuses = statuses
27
28
Chris Masoneab3e7332012-02-29 18:54:58 -080029class DynamicSuiteTest(mox.MoxTestBase):
30 """Unit tests for dynamic_suite module methods.
31
32 @var _DARGS: default args to vet.
33 """
34
35
36 def setUp(self):
37 super(DynamicSuiteTest, self).setUp()
38 self._DARGS = {'name': 'name',
39 'build': 'build',
40 'board': 'board',
41 'job': self.mox.CreateMock(base_job.base_job),
42 'num': 1,
43 'pool': 'pool',
44 'skip_reimage': True,
45 'add_experimental': False}
46
47
48 def testVetRequiredReimageAndRunArgs(self):
49 """Should verify only that required args are present and correct."""
50 build, board, name, job, _, _, _, _ = \
51 dynamic_suite._vet_reimage_and_run_args(**self._DARGS)
52 self.assertEquals(build, self._DARGS['build'])
53 self.assertEquals(board, self._DARGS['board'])
54 self.assertEquals(name, self._DARGS['name'])
55 self.assertEquals(job, self._DARGS['job'])
56
57
58 def testVetReimageAndRunBuildArgFail(self):
59 """Should fail verification because |build| arg is bad."""
60 self._DARGS['build'] = None
61 self.assertRaises(dynamic_suite.SuiteArgumentException,
62 dynamic_suite._vet_reimage_and_run_args,
63 **self._DARGS)
64
65
66 def testVetReimageAndRunBoardArgFail(self):
67 """Should fail verification because |board| arg is bad."""
68 self._DARGS['board'] = None
69 self.assertRaises(dynamic_suite.SuiteArgumentException,
70 dynamic_suite._vet_reimage_and_run_args,
71 **self._DARGS)
72
73
74 def testVetReimageAndRunNameArgFail(self):
75 """Should fail verification because |name| arg is bad."""
76 self._DARGS['name'] = None
77 self.assertRaises(dynamic_suite.SuiteArgumentException,
78 dynamic_suite._vet_reimage_and_run_args,
79 **self._DARGS)
80
81
82 def testVetReimageAndRunJobArgFail(self):
83 """Should fail verification because |job| arg is bad."""
84 self._DARGS['job'] = None
85 self.assertRaises(dynamic_suite.SuiteArgumentException,
86 dynamic_suite._vet_reimage_and_run_args,
87 **self._DARGS)
88
89
90 def testOverrideOptionalReimageAndRunArgs(self):
91 """Should verify that optional args can be overridden."""
92 _, _, _, _, pool, num, skip, expr = \
93 dynamic_suite._vet_reimage_and_run_args(**self._DARGS)
94 self.assertEquals(pool, self._DARGS['pool'])
95 self.assertEquals(num, self._DARGS['num'])
96 self.assertEquals(skip, self._DARGS['skip_reimage'])
97 self.assertEquals(expr, self._DARGS['add_experimental'])
98
99
100 def testDefaultOptionalReimageAndRunArgs(self):
101 """Should verify that optional args get defaults."""
102 del(self._DARGS['pool'])
103 del(self._DARGS['skip_reimage'])
104 del(self._DARGS['add_experimental'])
105 del(self._DARGS['num'])
106 _, _, _, _, pool, num, skip, expr = \
107 dynamic_suite._vet_reimage_and_run_args(**self._DARGS)
108 self.assertEquals(pool, None)
109 self.assertEquals(num, None)
110 self.assertEquals(skip, False)
111 self.assertEquals(expr, True)
112
113
Chris Masone6fed6462011-10-20 16:36:43 -0700114class ReimagerTest(mox.MoxTestBase):
115 """Unit tests for dynamic_suite.Reimager.
116
117 @var _URL: fake image url
Chris Masone8b7cd422012-02-22 13:16:11 -0800118 @var _BUILD: fake build
Chris Masone6fed6462011-10-20 16:36:43 -0700119 @var _NUM: fake number of machines to run on
120 @var _BOARD: fake board to reimage
121 """
122
Chris Masone2ef1d4e2011-12-20 11:06:53 -0800123 _URL = 'http://nothing/%s'
Chris Masone8b7cd422012-02-22 13:16:11 -0800124 _BUILD = 'build'
Chris Masone6fed6462011-10-20 16:36:43 -0700125 _NUM = 4
126 _BOARD = 'board'
Chris Masone5552dd72012-02-15 15:01:04 -0800127 _CONFIG = global_config.global_config
Chris Masone6fed6462011-10-20 16:36:43 -0700128
129
130 def setUp(self):
131 super(ReimagerTest, self).setUp()
132 self.afe = self.mox.CreateMock(frontend.AFE)
133 self.tko = self.mox.CreateMock(frontend.TKO)
134 self.reimager = dynamic_suite.Reimager('', afe=self.afe, tko=self.tko)
Chris Masone5552dd72012-02-15 15:01:04 -0800135 self._CONFIG.override_config_value('CROS',
136 'sharding_factor',
137 "%d" % self._NUM)
Chris Masone6fed6462011-10-20 16:36:43 -0700138
139
140 def testEnsureVersionLabelAlreadyExists(self):
141 """Should not create a label if it already exists."""
142 name = 'label'
143 self.afe.get_labels(name=name).AndReturn([name])
144 self.mox.ReplayAll()
145 self.reimager._ensure_version_label(name)
146
147
148 def testEnsureVersionLabel(self):
149 """Should create a label if it doesn't already exist."""
150 name = 'label'
151 self.afe.get_labels(name=name).AndReturn([])
152 self.afe.create_label(name=name)
153 self.mox.ReplayAll()
154 self.reimager._ensure_version_label(name)
155
156
157 def testInjectVars(self):
158 """Should inject dict of varibles into provided strings."""
159 def find_all_in(d, s):
160 """Returns true if all key-value pairs in |d| are printed in |s|."""
161 return reduce(lambda b,i: "%s='%s'\n" % i in s, d.iteritems(), True)
162
163 v = {'v1': 'one', 'v2': 'two'}
Chris Masone8b764252012-01-17 11:12:51 -0800164 self.assertTrue(find_all_in(v, dynamic_suite.inject_vars(v, '')))
165 self.assertTrue(find_all_in(v, dynamic_suite.inject_vars(v, 'ctrl')))
Chris Masone6fed6462011-10-20 16:36:43 -0700166
167
168 def testReportResultsGood(self):
169 """Should report results in the case where all jobs passed."""
170 job = self.mox.CreateMock(frontend.Job)
171 job.name = 'RPC Client job'
172 job.result = True
173 recorder = self.mox.CreateMock(base_job.base_job)
174 recorder.record('GOOD', mox.IgnoreArg(), job.name)
175 self.mox.ReplayAll()
176 self.reimager._report_results(job, recorder.record)
177
178
179 def testReportResultsBad(self):
180 """Should report results in various job failure cases.
181
182 In this test scenario, there are five hosts, all the 'netbook' platform.
183
184 h1: Did not run
185 h2: Two failed tests
186 h3: Two aborted tests
187 h4: completed, GOOD
188 h5: completed, GOOD
189 """
190 H1 = 'host1'
191 H2 = 'host2'
192 H3 = 'host3'
193 H4 = 'host4'
194 H5 = 'host5'
195
196 class FakeResult(object):
197 def __init__(self, reason):
198 self.reason = reason
199
200
201 # The RPC-client-side Job object that is annotated with results.
202 job = FakeJob()
203 job.result = None # job failed, there are results to report.
204
205 # The semantics of |results_platform_map| and |test_results| are
206 # drawn from frontend.AFE.poll_all_jobs()
207 job.results_platform_map = {'netbook': {'Aborted' : [H3],
Chris Masone8b7cd422012-02-22 13:16:11 -0800208 'Completed' : [H1, H4, H5],
209 'Failed': [H2]
Chris Masone6fed6462011-10-20 16:36:43 -0700210 }
211 }
212 # Gin up fake results for H2 and H3 failure cases.
213 h2 = frontend.TestResults()
214 h2.fail = [FakeResult('a'), FakeResult('b')]
215 h3 = frontend.TestResults()
216 h3.fail = [FakeResult('a'), FakeResult('b')]
217 # Skipping H1 in |test_status| dict means that it did not get run.
218 job.test_status = {H2: h2, H3: h3, H4: {}, H5: {}}
219
220 # Set up recording expectations.
221 rjob = self.mox.CreateMock(base_job.base_job)
222 for res in h2.fail:
223 rjob.record('FAIL', mox.IgnoreArg(), H2, res.reason).InAnyOrder()
224 for res in h3.fail:
225 rjob.record('ABORT', mox.IgnoreArg(), H3, res.reason).InAnyOrder()
226 rjob.record('GOOD', mox.IgnoreArg(), H4).InAnyOrder()
227 rjob.record('GOOD', mox.IgnoreArg(), H5).InAnyOrder()
228 rjob.record(
229 'ERROR', mox.IgnoreArg(), H1, mox.IgnoreArg()).InAnyOrder()
230
231 self.mox.ReplayAll()
232 self.reimager._report_results(job, rjob.record)
233
234
235 def testScheduleJob(self):
236 """Should be able to create a job with the AFE."""
237 # Fake out getting the autoupdate control file contents.
238 cf_getter = self.mox.CreateMock(control_file_getter.ControlFileGetter)
239 cf_getter.get_control_file_contents_by_name('autoupdate').AndReturn('')
240 self.reimager._cf_getter = cf_getter
241
Chris Masone6dca10a2012-02-15 15:41:42 -0800242 self._CONFIG.override_config_value('CROS',
243 'image_url_pattern',
244 self._URL)
Chris Masone6fed6462011-10-20 16:36:43 -0700245 self.afe.create_job(
Chris Masone8b7cd422012-02-22 13:16:11 -0800246 control_file=mox.And(mox.StrContains(self._BUILD),
247 mox.StrContains(self._URL % self._BUILD)),
248 name=mox.StrContains(self._BUILD),
Chris Masone6fed6462011-10-20 16:36:43 -0700249 control_type='Server',
Chris Masone8b7cd422012-02-22 13:16:11 -0800250 meta_hosts=['board:'+self._BOARD] * self._NUM,
251 dependencies=[])
Chris Masone6fed6462011-10-20 16:36:43 -0700252 self.mox.ReplayAll()
Chris Masone8b7cd422012-02-22 13:16:11 -0800253 self.reimager._schedule_reimage_job(self._BUILD, self._NUM, self._BOARD)
Chris Masone6fed6462011-10-20 16:36:43 -0700254
255
Chris Masone796fcf12012-02-22 16:53:31 -0800256 def expect_attempt(self, success, ex=None):
Chris Masone6fed6462011-10-20 16:36:43 -0700257 """Sets up |self.reimager| to expect an attempt() that returns |success|
258
Chris Masone796fcf12012-02-22 16:53:31 -0800259 @param success: the value returned by poll_job_results()
260 @param ex: if not None, |ex| is raised by get_jobs()
Chris Masone6fed6462011-10-20 16:36:43 -0700261 @return a FakeJob configured with appropriate expectations
262 """
263 canary = FakeJob()
264 self.mox.StubOutWithMock(self.reimager, '_ensure_version_label')
Chris Masone8b7cd422012-02-22 13:16:11 -0800265 self.reimager._ensure_version_label(mox.StrContains(self._BUILD))
Chris Masone6fed6462011-10-20 16:36:43 -0700266
267 self.mox.StubOutWithMock(self.reimager, '_schedule_reimage_job')
Chris Masone8b7cd422012-02-22 13:16:11 -0800268 self.reimager._schedule_reimage_job(self._BUILD,
Chris Masone6fed6462011-10-20 16:36:43 -0700269 self._NUM,
270 self._BOARD).AndReturn(canary)
271 if success is not None:
272 self.mox.StubOutWithMock(self.reimager, '_report_results')
273 self.reimager._report_results(canary, mox.IgnoreArg())
274
275 self.afe.get_jobs(id=canary.id, not_yet_run=True).AndReturn([])
Chris Masone796fcf12012-02-22 16:53:31 -0800276 if ex is not None:
277 self.afe.get_jobs(id=canary.id, finished=True).AndRaise(ex)
278 else:
279 self.afe.get_jobs(id=canary.id, finished=True).AndReturn([canary])
280 self.afe.poll_job_results(mox.IgnoreArg(),
281 canary, 0).AndReturn(success)
Chris Masone6fed6462011-10-20 16:36:43 -0700282
283 return canary
284
285
286 def testSuccessfulReimage(self):
287 """Should attempt a reimage and record success."""
288 canary = self.expect_attempt(True)
289
290 rjob = self.mox.CreateMock(base_job.base_job)
291 rjob.record('START', mox.IgnoreArg(), mox.IgnoreArg())
292 rjob.record('END GOOD', mox.IgnoreArg(), mox.IgnoreArg())
293 self.mox.ReplayAll()
Chris Masone8b7cd422012-02-22 13:16:11 -0800294 self.reimager.attempt(self._BUILD, self._BOARD, rjob.record)
Chris Masone6fed6462011-10-20 16:36:43 -0700295
296
297 def testFailedReimage(self):
298 """Should attempt a reimage and record failure."""
299 canary = self.expect_attempt(False)
300
301 rjob = self.mox.CreateMock(base_job.base_job)
302 rjob.record('START', mox.IgnoreArg(), mox.IgnoreArg())
303 rjob.record('END FAIL', mox.IgnoreArg(), mox.IgnoreArg())
304 self.mox.ReplayAll()
Chris Masone8b7cd422012-02-22 13:16:11 -0800305 self.reimager.attempt(self._BUILD, self._BOARD, rjob.record)
Chris Masone6fed6462011-10-20 16:36:43 -0700306
307
308 def testReimageThatNeverHappened(self):
309 """Should attempt a reimage and record that it didn't run."""
310 canary = self.expect_attempt(None)
311
312 rjob = self.mox.CreateMock(base_job.base_job)
313 rjob.record('START', mox.IgnoreArg(), mox.IgnoreArg())
314 rjob.record('FAIL', mox.IgnoreArg(), canary.name, mox.IgnoreArg())
315 rjob.record('END FAIL', mox.IgnoreArg(), mox.IgnoreArg())
316 self.mox.ReplayAll()
Chris Masone8b7cd422012-02-22 13:16:11 -0800317 self.reimager.attempt(self._BUILD, self._BOARD, rjob.record)
Chris Masone6fed6462011-10-20 16:36:43 -0700318
319
Chris Masone796fcf12012-02-22 16:53:31 -0800320 def testReimageThatRaised(self):
321 """Should attempt a reimage that raises an exception and record that."""
322 ex_message = 'Oh no!'
323 canary = self.expect_attempt(None, Exception(ex_message))
324
325 rjob = self.mox.CreateMock(base_job.base_job)
326 rjob.record('START', mox.IgnoreArg(), mox.IgnoreArg())
327 rjob.record('END ERROR', mox.IgnoreArg(), mox.IgnoreArg(), ex_message)
328 self.mox.ReplayAll()
329 self.reimager.attempt(self._BUILD, self._BOARD, rjob.record)
330
331
Chris Masone6fed6462011-10-20 16:36:43 -0700332class SuiteTest(mox.MoxTestBase):
333 """Unit tests for dynamic_suite.Suite.
334
Chris Masone8b7cd422012-02-22 13:16:11 -0800335 @var _BUILD: fake build
Chris Masone6fed6462011-10-20 16:36:43 -0700336 @var _TAG: fake suite tag
337 """
338
Chris Masone8b7cd422012-02-22 13:16:11 -0800339 _BUILD = 'build'
340 _TAG = 'suite_tag'
Chris Masone6fed6462011-10-20 16:36:43 -0700341
342
343 def setUp(self):
344 class FakeControlData(object):
345 """A fake parsed control file data structure."""
346 def __init__(self, data, expr=False):
347 self.string = 'text-' + data
348 self.name = 'name-' + data
349 self.data = data
350 self.test_type = 'Client'
351 self.experimental = expr
352
353
354 super(SuiteTest, self).setUp()
355 self.afe = self.mox.CreateMock(frontend.AFE)
356 self.tko = self.mox.CreateMock(frontend.TKO)
357
358 self.tmpdir = tempfile.mkdtemp(suffix=type(self).__name__)
359
360 self.getter = self.mox.CreateMock(control_file_getter.ControlFileGetter)
361
362 self.files = {'one': FakeControlData('data_one', expr=True),
363 'two': FakeControlData('data_two'),
364 'three': FakeControlData('data_three')}
365
366
367 def tearDown(self):
368 super(SuiteTest, self).tearDown()
369 shutil.rmtree(self.tmpdir, ignore_errors=True)
370
371
372 def expect_control_file_parsing(self):
373 """Expect an attempt to parse the 'control files' in |self.files|."""
374 self.getter.get_control_file_list().AndReturn(self.files.keys())
375 self.mox.StubOutWithMock(control_data, 'parse_control_string')
376 for file, data in self.files.iteritems():
377 self.getter.get_control_file_contents(file).AndReturn(data.string)
378 control_data.parse_control_string(
379 data.string, raise_warnings=True).AndReturn(data)
380
381
382 def testFindAndParseStableTests(self):
383 """Should find only non-experimental tests that match a predicate."""
384 self.expect_control_file_parsing()
385 self.mox.ReplayAll()
386
387 predicate = lambda d: d.text == self.files['two'].string
388 tests = dynamic_suite.Suite.find_and_parse_tests(self.getter, predicate)
389 self.assertEquals(len(tests), 1)
390 self.assertEquals(tests[0], self.files['two'])
391
392
393 def testFindAndParseTests(self):
394 """Should find all tests that match a predicate."""
395 self.expect_control_file_parsing()
396 self.mox.ReplayAll()
397
398 predicate = lambda d: d.text != self.files['two'].string
399 tests = dynamic_suite.Suite.find_and_parse_tests(self.getter,
400 predicate,
401 add_experimental=True)
402 self.assertEquals(len(tests), 2)
403 self.assertTrue(self.files['one'] in tests)
404 self.assertTrue(self.files['three'] in tests)
405
406
407 def mock_control_file_parsing(self):
408 """Fake out find_and_parse_tests(), returning content from |self.files|.
409 """
410 for test in self.files.values():
411 test.text = test.string # mimic parsing.
412 self.mox.StubOutWithMock(dynamic_suite.Suite, 'find_and_parse_tests')
413 dynamic_suite.Suite.find_and_parse_tests(
414 mox.IgnoreArg(),
415 mox.IgnoreArg(),
416 add_experimental=True).AndReturn(self.files.values())
417
418
419 def testStableUnstableFilter(self):
420 """Should distinguish between experimental and stable tests."""
421 self.mock_control_file_parsing()
422 self.mox.ReplayAll()
423 suite = dynamic_suite.Suite.create_from_name(self._TAG, self.tmpdir,
Chris Masoned6f38c82012-02-22 14:53:42 -0800424 afe=self.afe, tko=self.tko)
Chris Masone6fed6462011-10-20 16:36:43 -0700425
426 self.assertTrue(self.files['one'] in suite.tests)
427 self.assertTrue(self.files['two'] in suite.tests)
428 self.assertTrue(self.files['one'] in suite.unstable_tests())
429 self.assertTrue(self.files['two'] in suite.stable_tests())
430 self.assertFalse(self.files['one'] in suite.stable_tests())
431 self.assertFalse(self.files['two'] in suite.unstable_tests())
432
433
434 def expect_job_scheduling(self, add_experimental):
435 """Expect jobs to be scheduled for 'tests' in |self.files|.
436
437 @param add_experimental: expect jobs for experimental tests as well.
438 """
439 for test in self.files.values():
440 if not add_experimental and test.experimental:
441 continue
442 self.afe.create_job(
443 control_file=test.text,
Chris Masone8b7cd422012-02-22 13:16:11 -0800444 name=mox.And(mox.StrContains(self._BUILD),
Chris Masone6fed6462011-10-20 16:36:43 -0700445 mox.StrContains(test.name)),
446 control_type=mox.IgnoreArg(),
Chris Masone8b7cd422012-02-22 13:16:11 -0800447 meta_hosts=[dynamic_suite.VERSION_PREFIX + self._BUILD],
Scott Zawalskie5bb1c52012-02-29 13:15:50 -0500448 dependencies=[]).AndReturn(FakeJob())
Chris Masone6fed6462011-10-20 16:36:43 -0700449
450
451 def testScheduleTests(self):
452 """Should schedule stable and experimental tests with the AFE."""
453 self.mock_control_file_parsing()
454 self.expect_job_scheduling(add_experimental=True)
455
456 self.mox.ReplayAll()
Chris Masone8b7cd422012-02-22 13:16:11 -0800457 suite = dynamic_suite.Suite.create_from_name(self._TAG, self._BUILD,
Chris Masoned6f38c82012-02-22 14:53:42 -0800458 afe=self.afe, tko=self.tko)
Chris Masone8b7cd422012-02-22 13:16:11 -0800459 suite.schedule()
Chris Masone6fed6462011-10-20 16:36:43 -0700460
461
Scott Zawalski9ece6532012-02-28 14:10:47 -0500462 def testScheduleTestsAndRecord(self):
463 """Should schedule stable and experimental tests with the AFE."""
464 self.mock_control_file_parsing()
465 self.mox.ReplayAll()
466 suite = dynamic_suite.Suite.create_from_name(self._TAG, self._BUILD,
467 afe=self.afe, tko=self.tko,
468 results_dir=self.tmpdir)
469 self.mox.ResetAll()
470 self.expect_job_scheduling(add_experimental=True)
471 self.mox.StubOutWithMock(suite, '_record_scheduled_jobs')
472 suite._record_scheduled_jobs()
473 self.mox.ReplayAll()
474 suite.schedule()
Scott Zawalskie5bb1c52012-02-29 13:15:50 -0500475 for job in suite._jobs:
476 self.assertTrue(hasattr(job, 'test_name'))
Scott Zawalski9ece6532012-02-28 14:10:47 -0500477
478
Chris Masone6fed6462011-10-20 16:36:43 -0700479 def testScheduleStableTests(self):
480 """Should schedule only stable tests with the AFE."""
481 self.mock_control_file_parsing()
482 self.expect_job_scheduling(add_experimental=False)
483
484 self.mox.ReplayAll()
Chris Masone8b7cd422012-02-22 13:16:11 -0800485 suite = dynamic_suite.Suite.create_from_name(self._TAG, self._BUILD,
Chris Masoned6f38c82012-02-22 14:53:42 -0800486 afe=self.afe, tko=self.tko)
Chris Masone8b7cd422012-02-22 13:16:11 -0800487 suite.schedule(add_experimental=False)
Chris Masone6fed6462011-10-20 16:36:43 -0700488
489
490 def expect_result_gathering(self, job):
491 self.afe.get_jobs(id=job.id, finished=True).AndReturn(job)
492 entries = map(lambda s: s.entry, job.statuses)
493 self.afe.run('get_host_queue_entries',
494 job=job.id).AndReturn(entries)
495 if True not in map(lambda e: 'aborted' in e and e['aborted'], entries):
496 self.tko.get_status_counts(job=job.id).AndReturn(job.statuses)
497
498
Chris Masonefef21382012-01-17 11:16:32 -0800499 def _createSuiteWithMockedTestsAndControlFiles(self):
500 """Create a Suite, using mocked tests and control file contents.
501
502 @return Suite object, after mocking out behavior needed to create it.
503 """
Chris Masonefef21382012-01-17 11:16:32 -0800504 self.expect_control_file_parsing()
505 self.mox.ReplayAll()
Scott Zawalski9ece6532012-02-28 14:10:47 -0500506 suite = dynamic_suite.Suite.create_from_name(self._TAG, self._BUILD,
Chris Masoned6f38c82012-02-22 14:53:42 -0800507 self.getter, self.afe,
508 self.tko)
Chris Masonefef21382012-01-17 11:16:32 -0800509 self.mox.ResetAll()
510 return suite
511
512
Chris Masone6fed6462011-10-20 16:36:43 -0700513 def testWaitForResults(self):
514 """Should gather status and return records for job summaries."""
515 class FakeStatus(object):
516 """Fake replacement for server-side job status objects.
517
518 @var status: 'GOOD', 'FAIL', 'ERROR', etc.
519 @var test_name: name of the test this is status for
520 @var reason: reason for failure, if any
521 @var aborted: present and True if the job was aborted. Optional.
522 """
523 def __init__(self, code, name, reason, aborted=None):
524 self.status = code
525 self.test_name = name
526 self.reason = reason
527 self.entry = {}
528 if aborted:
529 self.entry['aborted'] = True
530
531 def equals_record(self, args):
532 """Compares this object to a recorded status."""
533 return self._equals_record(*args)
534
Chris Masone2ef1d4e2011-12-20 11:06:53 -0800535 def _equals_record(self, status, subdir, name, reason=None):
Chris Masone6fed6462011-10-20 16:36:43 -0700536 """Compares this object and fields of recorded status."""
537 if 'aborted' in self.entry and self.entry['aborted']:
538 return status == 'ABORT'
539 return (self.status == status and
540 self.test_name == name and
541 self.reason == reason)
542
Chris Masonefef21382012-01-17 11:16:32 -0800543 suite = self._createSuiteWithMockedTestsAndControlFiles()
Chris Masone6fed6462011-10-20 16:36:43 -0700544
545 jobs = [FakeJob(0, [FakeStatus('GOOD', 'T0', ''),
546 FakeStatus('GOOD', 'T1', '')]),
547 FakeJob(1, [FakeStatus('ERROR', 'T0', 'err', False),
548 FakeStatus('GOOD', 'T1', '')]),
549 FakeJob(2, [FakeStatus('TEST_NA', 'T0', 'no')]),
550 FakeJob(2, [FakeStatus('FAIL', 'T0', 'broken')]),
551 FakeJob(3, [FakeStatus('ERROR', 'T0', 'gah', True)])]
552 # To simulate a job that isn't ready the first time we check.
553 self.afe.get_jobs(id=jobs[0].id, finished=True).AndReturn([])
554 # Expect all the rest of the jobs to be good to go the first time.
555 for job in jobs[1:]:
556 self.expect_result_gathering(job)
557 # Then, expect job[0] to be ready.
558 self.expect_result_gathering(jobs[0])
559 # Expect us to poll twice.
560 self.mox.StubOutWithMock(time, 'sleep')
561 time.sleep(5)
562 time.sleep(5)
563 self.mox.ReplayAll()
564
Chris Masone6fed6462011-10-20 16:36:43 -0700565 suite._jobs = list(jobs)
Scott Zawalskiab25bd62012-02-10 18:29:12 -0500566 results = [result for result in suite.wait_for_results()]
Chris Masone6fed6462011-10-20 16:36:43 -0700567 for job in jobs:
568 for status in job.statuses:
569 self.assertTrue(True in map(status.equals_record, results))
570
571
572 def testRunAndWaitSuccess(self):
573 """Should record successful results."""
Chris Masonefef21382012-01-17 11:16:32 -0800574 suite = self._createSuiteWithMockedTestsAndControlFiles()
Chris Masone6fed6462011-10-20 16:36:43 -0700575
Chris Masonefef21382012-01-17 11:16:32 -0800576 results = [('GOOD', None, 'good'), ('FAIL', None, 'bad', 'reason')]
Chris Masone6fed6462011-10-20 16:36:43 -0700577 recorder = self.mox.CreateMock(base_job.base_job)
Scott Zawalskiab25bd62012-02-10 18:29:12 -0500578 recorder.record('INFO', None, 'Start %s' % self._TAG)
Chris Masone6fed6462011-10-20 16:36:43 -0700579 for result in results:
Scott Zawalskiab25bd62012-02-10 18:29:12 -0500580 status = result[0]
581 test_name = result[2]
582 recorder.record('START', None, test_name)
Chris Masone6fed6462011-10-20 16:36:43 -0700583 recorder.record(*result).InAnyOrder('results')
Scott Zawalskiab25bd62012-02-10 18:29:12 -0500584 recorder.record('END %s' % status, None, test_name)
Chris Masone6fed6462011-10-20 16:36:43 -0700585
Chris Masone6fed6462011-10-20 16:36:43 -0700586 self.mox.StubOutWithMock(suite, 'schedule')
Chris Masone8b7cd422012-02-22 13:16:11 -0800587 suite.schedule(True)
Chris Masone6fed6462011-10-20 16:36:43 -0700588 self.mox.StubOutWithMock(suite, 'wait_for_results')
589 suite.wait_for_results().AndReturn(results)
590 self.mox.ReplayAll()
591
Chris Masone8b7cd422012-02-22 13:16:11 -0800592 suite.run_and_wait(recorder.record, True)
Chris Masone6fed6462011-10-20 16:36:43 -0700593
594
595 def testRunAndWaitFailure(self):
596 """Should record failure to gather results."""
Chris Masonefef21382012-01-17 11:16:32 -0800597 suite = self._createSuiteWithMockedTestsAndControlFiles()
598
Chris Masone6fed6462011-10-20 16:36:43 -0700599 recorder = self.mox.CreateMock(base_job.base_job)
Scott Zawalskiab25bd62012-02-10 18:29:12 -0500600 recorder.record('INFO', None, 'Start %s' % self._TAG)
601 recorder.record('FAIL', None, self._TAG,
602 mox.StrContains('waiting'))
Chris Masone6fed6462011-10-20 16:36:43 -0700603
Chris Masone6fed6462011-10-20 16:36:43 -0700604 self.mox.StubOutWithMock(suite, 'schedule')
Chris Masone8b7cd422012-02-22 13:16:11 -0800605 suite.schedule(True)
Chris Masone6fed6462011-10-20 16:36:43 -0700606 self.mox.StubOutWithMock(suite, 'wait_for_results')
607 suite.wait_for_results().AndRaise(Exception())
608 self.mox.ReplayAll()
609
Chris Masone8b7cd422012-02-22 13:16:11 -0800610 suite.run_and_wait(recorder.record, True)
Chris Masone6fed6462011-10-20 16:36:43 -0700611
612
613 def testRunAndWaitScheduleFailure(self):
Chris Masonefef21382012-01-17 11:16:32 -0800614 """Should record failure to schedule jobs."""
615 suite = self._createSuiteWithMockedTestsAndControlFiles()
616
Chris Masone6fed6462011-10-20 16:36:43 -0700617 recorder = self.mox.CreateMock(base_job.base_job)
Scott Zawalskiab25bd62012-02-10 18:29:12 -0500618 recorder.record('INFO', None, 'Start %s' % self._TAG)
619 recorder.record('FAIL', None, self._TAG,
620 mox.StrContains('scheduling'))
Chris Masone6fed6462011-10-20 16:36:43 -0700621
Chris Masone6fed6462011-10-20 16:36:43 -0700622 self.mox.StubOutWithMock(suite, 'schedule')
Chris Masone8b7cd422012-02-22 13:16:11 -0800623 suite.schedule(True).AndRaise(Exception())
Chris Masone6fed6462011-10-20 16:36:43 -0700624 self.mox.ReplayAll()
625
Chris Masone8b7cd422012-02-22 13:16:11 -0800626 suite.run_and_wait(recorder.record, True)
Chris Masone6fed6462011-10-20 16:36:43 -0700627
628
629if __name__ == '__main__':
630 unittest.main()