job: new parameter --harness_args
[autotest-zwu.git] / client / bin / job_unittest.py
blob88fa2721ef1cada3efbe102a5871790babf44471
1 #!/usr/bin/python
3 import logging, os, shutil, sys, time, StringIO
4 import common
6 from autotest_lib.client.bin import job, boottool, config, sysinfo, harness
7 from autotest_lib.client.bin import test, xen, kernel, utils
8 from autotest_lib.client.common_lib import packages, error, log
9 from autotest_lib.client.common_lib import logging_manager, logging_config
10 from autotest_lib.client.common_lib import base_job_unittest
11 from autotest_lib.client.common_lib.test_utils import mock, unittest
14 class job_test_case(unittest.TestCase):
15 """Generic job TestCase class that defines a standard job setUp and
16 tearDown, with some standard stubs."""
18 job_class = job.base_client_job
20 def setUp(self):
21 self.god = mock.mock_god(ut=self)
22 self.god.stub_with(job.base_client_job, '_get_environ_autodir',
23 classmethod(lambda cls: '/adir'))
24 self.job = self.job_class.__new__(self.job_class)
25 self.job._job_directory = base_job_unittest.stub_job_directory
28 def tearDown(self):
29 self.god.unstub_all()
32 class test_find_base_directories(
33 base_job_unittest.test_find_base_directories.generic_tests,
34 job_test_case):
36 def test_autodir_equals_clientdir(self):
37 autodir, clientdir, _ = self.job._find_base_directories()
38 self.assertEqual(autodir, '/adir')
39 self.assertEqual(clientdir, '/adir')
42 def test_serverdir_is_none(self):
43 _, _, serverdir = self.job._find_base_directories()
44 self.assertEqual(serverdir, None)
47 class abstract_test_init(base_job_unittest.test_init.generic_tests):
48 """Generic client job mixin used when defining variations on the
49 job.__init__ generic tests."""
50 OPTIONAL_ATTRIBUTES = (
51 base_job_unittest.test_init.generic_tests.OPTIONAL_ATTRIBUTES
52 - set(['control', 'bootloader', 'harness']))
55 class test_init_minimal_options(abstract_test_init, job_test_case):
56 def call_init(self):
57 # TODO(jadmanski): refactor more of the __init__ code to not need to
58 # stub out countless random APIs
59 self.god.stub_function_to_return(job.os, 'mkdir', None)
60 self.god.stub_function_to_return(job.os.path, 'exists', True)
61 self.god.stub_function_to_return(self.job, '_load_state', None)
62 self.god.stub_function_to_return(self.job, 'record', None)
63 self.god.stub_function_to_return(job.shutil, 'copyfile', None)
64 self.god.stub_function_to_return(job.logging_manager,
65 'configure_logging', None)
66 class manager:
67 def start_logging(self):
68 return None
69 self.god.stub_function_to_return(job.logging_manager,
70 'get_logging_manager', manager())
71 class stub_sysinfo:
72 def log_per_reboot_data(self):
73 return None
74 self.god.stub_function_to_return(job.sysinfo, 'sysinfo',
75 stub_sysinfo())
76 class stub_harness:
77 run_start = lambda self: None
78 self.god.stub_function_to_return(job.harness, 'select', stub_harness())
79 self.god.stub_function_to_return(job.boottool, 'boottool', object())
80 class options:
81 tag = ''
82 verbose = False
83 cont = False
84 harness = 'stub'
85 harness_args = None
86 hostname = None
87 user = None
88 log = False
89 args = ''
90 tap_report = None
91 self.god.stub_function_to_return(job.utils, 'drop_caches', None)
93 self.job._job_state = base_job_unittest.stub_job_state
94 self.job.__init__('/control', options)
97 class dummy(object):
98 """A simple placeholder for attributes"""
99 pass
102 class first_line_comparator(mock.argument_comparator):
103 def __init__(self, first_line):
104 self.first_line = first_line
107 def is_satisfied_by(self, parameter):
108 return self.first_line == parameter.splitlines()[0]
111 class test_base_job(unittest.TestCase):
112 def setUp(self):
113 # make god
114 self.god = mock.mock_god(ut=self)
116 # need to set some environ variables
117 self.autodir = "autodir"
118 os.environ['AUTODIR'] = self.autodir
120 # set up some variables
121 self.control = "control"
122 self.jobtag = "jobtag"
124 # get rid of stdout and logging
125 sys.stdout = StringIO.StringIO()
126 logging_manager.configure_logging(logging_config.TestingConfig())
127 logging.disable(logging.CRITICAL)
128 def dummy_configure_logging(*args, **kwargs):
129 pass
130 self.god.stub_with(logging_manager, 'configure_logging',
131 dummy_configure_logging)
132 real_get_logging_manager = logging_manager.get_logging_manager
133 def get_logging_manager_no_fds(manage_stdout_and_stderr=False,
134 redirect_fds=False):
135 return real_get_logging_manager(manage_stdout_and_stderr, False)
136 self.god.stub_with(logging_manager, 'get_logging_manager',
137 get_logging_manager_no_fds)
139 # stub out some stuff
140 self.god.stub_function(os.path, 'exists')
141 self.god.stub_function(os.path, 'isdir')
142 self.god.stub_function(os, 'makedirs')
143 self.god.stub_function(os, 'mkdir')
144 self.god.stub_function(os, 'remove')
145 self.god.stub_function(shutil, 'rmtree')
146 self.god.stub_function(shutil, 'copyfile')
147 self.god.stub_function(job, 'open')
148 self.god.stub_function(utils, 'system')
149 self.god.stub_function(utils, 'drop_caches')
150 self.god.stub_function(harness, 'select')
151 self.god.stub_function(sysinfo, 'log_per_reboot_data')
153 self.god.stub_class(config, 'config')
154 self.god.stub_class(job.local_host, 'LocalHost')
155 self.god.stub_class(boottool, 'boottool')
156 self.god.stub_class(sysinfo, 'sysinfo')
158 self.god.stub_class_method(job.base_client_job,
159 '_cleanup_debugdir_files')
160 self.god.stub_class_method(job.base_client_job, '_cleanup_results_dir')
162 self.god.stub_with(job.base_job.job_directory, '_ensure_valid',
163 lambda *_: None)
166 def tearDown(self):
167 sys.stdout = sys.__stdout__
168 self.god.unstub_all()
171 def _setup_pre_record_init(self, cont):
172 self.god.stub_function(self.job, '_load_state')
174 resultdir = os.path.join(self.autodir, 'results', self.jobtag)
175 tmpdir = os.path.join(self.autodir, 'tmp')
176 if not cont:
177 job.base_client_job._cleanup_debugdir_files.expect_call()
178 job.base_client_job._cleanup_results_dir.expect_call()
180 self.job._load_state.expect_call()
182 my_harness = self.god.create_mock_class(harness.harness,
183 'my_harness')
184 harness.select.expect_call(None,
185 self.job,
186 None).and_return(my_harness)
188 return resultdir, my_harness
191 def _setup_post_record_init(self, cont, resultdir, my_harness):
192 # now some specific stubs
193 self.god.stub_function(self.job, 'config_get')
194 self.god.stub_function(self.job, 'config_set')
195 self.god.stub_function(self.job, 'record')
197 # other setup
198 results = os.path.join(self.autodir, 'results')
199 download = os.path.join(self.autodir, 'tests', 'download')
200 pkgdir = os.path.join(self.autodir, 'packages')
202 utils.drop_caches.expect_call()
203 job_sysinfo = sysinfo.sysinfo.expect_new(resultdir)
204 if not cont:
205 os.path.exists.expect_call(download).and_return(False)
206 os.mkdir.expect_call(download)
207 shutil.copyfile.expect_call(mock.is_string_comparator(),
208 os.path.join(resultdir, 'control'))
210 self.config = config.config.expect_new(self.job)
211 self.job.config_get.expect_call(
212 'boottool.executable').and_return(None)
213 bootloader = boottool.boottool.expect_new(None)
214 job.local_host.LocalHost.expect_new(hostname='localhost',
215 bootloader=bootloader)
216 job_sysinfo.log_per_reboot_data.expect_call()
217 if not cont:
218 self.job.record.expect_call('START', None, None)
220 my_harness.run_start.expect_call()
222 self.god.stub_function(utils, 'read_one_line')
223 utils.read_one_line.expect_call('/proc/cmdline').and_return(
224 'blah more-blah root=lala IDENT=81234567 blah-again console=tty1')
225 self.job.config_set.expect_call('boot.default_args',
226 'more-blah console=tty1')
229 def construct_job(self, cont):
230 # will construct class instance using __new__
231 self.job = job.base_client_job.__new__(job.base_client_job)
233 # record
234 resultdir, my_harness = self._setup_pre_record_init(cont)
235 self._setup_post_record_init(cont, resultdir, my_harness)
237 # finish constructor
238 options = dummy()
239 options.tag = self.jobtag
240 options.cont = cont
241 options.harness = None
242 options.harness_args = None
243 options.log = False
244 options.verbose = False
245 options.hostname = 'localhost'
246 options.user = 'my_user'
247 options.args = ''
248 options.tap_report = None
249 self.job.__init__(self.control, options,
250 extra_copy_cmdline=['more-blah'])
252 # check
253 self.god.check_playback()
256 def get_partition_mock(self, devname):
258 Create a mock of a partition object and return it.
260 class mock(object):
261 device = devname
262 get_mountpoint = self.god.create_mock_function('get_mountpoint')
263 return mock
266 def test_constructor_first_run(self):
267 self.construct_job(False)
270 def test_constructor_continuation(self):
271 self.construct_job(True)
274 def test_constructor_post_record_failure(self):
276 Test post record initialization failure.
278 self.job = job.base_client_job.__new__(job.base_client_job)
279 options = dummy()
280 options.tag = self.jobtag
281 options.cont = False
282 options.harness = None
283 options.harness_args = None
284 options.log = False
285 options.verbose = False
286 options.hostname = 'localhost'
287 options.user = 'my_user'
288 options.args = ''
289 options.tap_report = None
290 error = Exception('fail')
292 self.god.stub_function(self.job, '_post_record_init')
293 self.god.stub_function(self.job, 'record')
295 self._setup_pre_record_init(False)
296 self.job._post_record_init.expect_call(
297 self.control, options, True, ['more-blah']).and_raises(error)
298 self.job.record.expect_call(
299 'ABORT', None, None,'client.bin.job.__init__ failed: %s' %
300 str(error))
302 self.assertRaises(
303 Exception, self.job.__init__, self.control, options,
304 drop_caches=True, extra_copy_cmdline=['more-blah'])
306 # check
307 self.god.check_playback()
310 def test_relative_path(self):
311 self.construct_job(True)
312 dummy = "asdf"
313 ret = self.job.relative_path(os.path.join(self.job.resultdir, dummy))
314 self.assertEquals(ret, dummy)
317 def test_control_functions(self):
318 self.construct_job(True)
319 control_file = "blah"
320 self.job.control_set(control_file)
321 self.assertEquals(self.job.control_get(), os.path.abspath(control_file))
324 def test_harness_select(self):
325 self.construct_job(True)
327 # record
328 which = "which"
329 harness_args = ''
330 harness.select.expect_call(which, self.job,
331 harness_args).and_return(None)
333 # run and test
334 self.job.harness_select(which, harness_args)
335 self.god.check_playback()
338 def test_config_set(self):
339 self.construct_job(True)
341 # unstub config_set
342 self.god.unstub(self.job, 'config_set')
343 # record
344 name = "foo"
345 val = 10
346 self.config.set.expect_call(name, val)
348 # run and test
349 self.job.config_set(name, val)
350 self.god.check_playback()
353 def test_config_get(self):
354 self.construct_job(True)
356 # unstub config_get
357 self.god.unstub(self.job, 'config_get')
358 # record
359 name = "foo"
360 val = 10
361 self.config.get.expect_call(name).and_return(val)
363 # run and test
364 self.job.config_get(name)
365 self.god.check_playback()
368 def test_setup_dirs_raise(self):
369 self.construct_job(True)
371 # setup
372 results_dir = 'foo'
373 tmp_dir = 'bar'
375 # record
376 os.path.exists.expect_call(tmp_dir).and_return(True)
377 os.path.isdir.expect_call(tmp_dir).and_return(False)
379 # test
380 self.assertRaises(ValueError, self.job.setup_dirs, results_dir, tmp_dir)
381 self.god.check_playback()
384 def test_setup_dirs(self):
385 self.construct_job(True)
387 # setup
388 results_dir1 = os.path.join(self.job.resultdir, 'build')
389 results_dir2 = os.path.join(self.job.resultdir, 'build.2')
390 results_dir3 = os.path.join(self.job.resultdir, 'build.3')
391 tmp_dir = 'bar'
393 # record
394 os.path.exists.expect_call(tmp_dir).and_return(False)
395 os.mkdir.expect_call(tmp_dir)
396 os.path.isdir.expect_call(tmp_dir).and_return(True)
397 os.path.exists.expect_call(results_dir1).and_return(True)
398 os.path.exists.expect_call(results_dir2).and_return(True)
399 os.path.exists.expect_call(results_dir3).and_return(False)
400 os.path.exists.expect_call(results_dir3).and_return(False)
401 os.mkdir.expect_call(results_dir3)
403 # test
404 self.assertEqual(self.job.setup_dirs(None, tmp_dir),
405 (results_dir3, tmp_dir))
406 self.god.check_playback()
409 def test_xen(self):
410 self.construct_job(True)
412 # setup
413 self.god.stub_function(self.job, "setup_dirs")
414 self.god.stub_class(xen, "xen")
415 results = 'results_dir'
416 tmp = 'tmp'
417 build = 'xen'
418 base_tree = object()
420 # record
421 self.job.setup_dirs.expect_call(results,
422 tmp).and_return((results, tmp))
423 myxen = xen.xen.expect_new(self.job, base_tree, results, tmp, build,
424 False, None)
426 # run job and check
427 axen = self.job.xen(base_tree, results, tmp)
428 self.god.check_playback()
429 self.assertEquals(myxen, axen)
432 def test_kernel_rpm(self):
433 self.construct_job(True)
435 # setup
436 self.god.stub_function(self.job, "setup_dirs")
437 self.god.stub_class(kernel, "rpm_kernel")
438 self.god.stub_function(kernel, "preprocess_path")
439 self.god.stub_function(self.job.pkgmgr, "fetch_pkg")
440 self.god.stub_function(utils, "get_os_vendor")
441 results = 'results_dir'
442 tmp = 'tmp'
443 build = 'xen'
444 path = "somepath.rpm"
445 packages_dir = os.path.join("autodir/packages", path)
447 # record
448 self.job.setup_dirs.expect_call(results,
449 tmp).and_return((results, tmp))
450 kernel.preprocess_path.expect_call(path).and_return(path)
451 os.path.exists.expect_call(path).and_return(False)
452 self.job.pkgmgr.fetch_pkg.expect_call(path, packages_dir, repo_url='')
453 utils.get_os_vendor.expect_call()
454 mykernel = kernel.rpm_kernel.expect_new(self.job, [packages_dir],
455 results)
457 # check
458 akernel = self.job.kernel(path, results, tmp)
459 self.god.check_playback()
460 self.assertEquals(mykernel, akernel)
463 def test_kernel(self):
464 self.construct_job(True)
466 # setup
467 self.god.stub_function(self.job, "setup_dirs")
468 self.god.stub_class(kernel, "kernel")
469 self.god.stub_function(kernel, "preprocess_path")
470 results = 'results_dir'
471 tmp = 'tmp'
472 build = 'linux'
473 path = "somepath.deb"
475 # record
476 self.job.setup_dirs.expect_call(results,
477 tmp).and_return((results, tmp))
478 kernel.preprocess_path.expect_call(path).and_return(path)
479 mykernel = kernel.kernel.expect_new(self.job, path, results, tmp,
480 build, False)
482 # check
483 akernel = self.job.kernel(path, results, tmp)
484 self.god.check_playback()
485 self.assertEquals(mykernel, akernel)
488 def test_run_test_logs_test_error_from_unhandled_error(self):
489 self.construct_job(True)
491 # set up stubs
492 self.god.stub_function(self.job.pkgmgr, 'get_package_name')
493 self.god.stub_function(self.job, "_runtest")
495 # create an unhandled error object
496 class MyError(error.TestError):
497 pass
498 real_error = MyError("this is the real error message")
499 unhandled_error = error.UnhandledTestError(real_error)
501 # set up the recording
502 testname = "error_test"
503 outputdir = os.path.join(self.job.resultdir, testname)
504 self.job.pkgmgr.get_package_name.expect_call(
505 testname, 'test').and_return(("", testname))
506 os.path.exists.expect_call(outputdir).and_return(False)
507 self.job.record.expect_call("START", testname, testname)
508 self.job._runtest.expect_call(testname, "", (), {}).and_raises(
509 unhandled_error)
510 self.job.record.expect_call("ERROR", testname, testname,
511 first_line_comparator(str(real_error)))
512 self.job.record.expect_call("END ERROR", testname, testname)
513 self.job.harness.run_test_complete.expect_call()
514 utils.drop_caches.expect_call()
516 # run and check
517 self.job.run_test(testname)
518 self.god.check_playback()
521 def test_run_test_logs_non_test_error_from_unhandled_error(self):
522 self.construct_job(True)
524 # set up stubs
525 self.god.stub_function(self.job.pkgmgr, 'get_package_name')
526 self.god.stub_function(self.job, "_runtest")
528 # create an unhandled error object
529 class MyError(Exception):
530 pass
531 real_error = MyError("this is the real error message")
532 unhandled_error = error.UnhandledTestError(real_error)
533 reason = first_line_comparator("Unhandled MyError: %s" % real_error)
535 # set up the recording
536 testname = "error_test"
537 outputdir = os.path.join(self.job.resultdir, testname)
538 self.job.pkgmgr.get_package_name.expect_call(
539 testname, 'test').and_return(("", testname))
540 os.path.exists.expect_call(outputdir).and_return(False)
541 self.job.record.expect_call("START", testname, testname)
542 self.job._runtest.expect_call(testname, "", (), {}).and_raises(
543 unhandled_error)
544 self.job.record.expect_call("ERROR", testname, testname, reason)
545 self.job.record.expect_call("END ERROR", testname, testname)
546 self.job.harness.run_test_complete.expect_call()
547 utils.drop_caches.expect_call()
549 # run and check
550 self.job.run_test(testname)
551 self.god.check_playback()
554 def test_report_reboot_failure(self):
555 self.construct_job(True)
557 # record
558 self.job.record.expect_call("ABORT", "sub", "reboot.verify",
559 "boot failure")
560 self.job.record.expect_call("END ABORT", "sub", "reboot",
561 optional_fields={"kernel": "2.6.15-smp"})
563 # playback
564 self.job._record_reboot_failure("sub", "reboot.verify", "boot failure",
565 running_id="2.6.15-smp")
566 self.god.check_playback()
569 def _setup_check_post_reboot(self, mount_info, cpu_count):
570 # setup
571 self.god.stub_function(job.partition_lib, "get_partition_list")
572 self.god.stub_function(utils, "count_cpus")
574 part_list = [self.get_partition_mock("/dev/hda1"),
575 self.get_partition_mock("/dev/hdb1")]
576 mount_list = ["/mnt/hda1", "/mnt/hdb1"]
578 # record
579 job.partition_lib.get_partition_list.expect_call(
580 self.job, exclude_swap=False).and_return(part_list)
581 for i in xrange(len(part_list)):
582 part_list[i].get_mountpoint.expect_call().and_return(mount_list[i])
583 if cpu_count is not None:
584 utils.count_cpus.expect_call().and_return(cpu_count)
585 self.job._state.set('client', 'mount_info', mount_info)
586 self.job._state.set('client', 'cpu_count', 8)
589 def test_check_post_reboot_success(self):
590 self.construct_job(True)
592 mount_info = set([("/dev/hda1", "/mnt/hda1"),
593 ("/dev/hdb1", "/mnt/hdb1")])
594 self._setup_check_post_reboot(mount_info, 8)
596 # playback
597 self.job._check_post_reboot("sub")
598 self.god.check_playback()
601 def test_check_post_reboot_mounts_failure(self):
602 self.construct_job(True)
604 mount_info = set([("/dev/hda1", "/mnt/hda1")])
605 self._setup_check_post_reboot(mount_info, None)
607 self.god.stub_function(self.job, "_record_reboot_failure")
608 self.job._record_reboot_failure.expect_call("sub",
609 "reboot.verify_config", "mounted partitions are different after"
610 " reboot (old entries: set([]), new entries: set([('/dev/hdb1',"
611 " '/mnt/hdb1')]))", running_id=None)
613 # playback
614 self.assertRaises(error.JobError, self.job._check_post_reboot, "sub")
615 self.god.check_playback()
618 def test_check_post_reboot_cpu_failure(self):
619 self.construct_job(True)
621 mount_info = set([("/dev/hda1", "/mnt/hda1"),
622 ("/dev/hdb1", "/mnt/hdb1")])
623 self._setup_check_post_reboot(mount_info, 4)
625 self.god.stub_function(self.job, "_record_reboot_failure")
626 self.job._record_reboot_failure.expect_call(
627 'sub', 'reboot.verify_config',
628 'Number of CPUs changed after reboot (old count: 8, new count: 4)',
629 running_id=None)
631 # playback
632 self.assertRaises(error.JobError, self.job._check_post_reboot, "sub")
633 self.god.check_playback()
636 def test_end_boot(self):
637 self.construct_job(True)
638 self.god.stub_function(self.job, "_check_post_reboot")
640 # set up the job class
641 self.job._record_prefix = '\t\t'
643 self.job._check_post_reboot.expect_call("sub", running_id=None)
644 self.job.record.expect_call("END GOOD", "sub", "reboot",
645 optional_fields={"kernel": "2.6.15-smp",
646 "patch0": "patchname"})
648 # run test
649 self.job.end_reboot("sub", "2.6.15-smp", ["patchname"])
650 self.god.check_playback()
653 def test_end_boot_and_verify_success(self):
654 self.construct_job(True)
655 self.god.stub_function(self.job, "_check_post_reboot")
657 # set up the job class
658 self.job._record_prefix = '\t\t'
660 self.god.stub_function(utils, "running_os_ident")
661 utils.running_os_ident.expect_call().and_return("2.6.15-smp")
663 utils.read_one_line.expect_call("/proc/cmdline").and_return(
664 "blah more-blah root=lala IDENT=81234567 blah-again")
666 self.god.stub_function(utils, "running_os_full_version")
667 running_id = "2.6.15-smp"
668 utils.running_os_full_version.expect_call().and_return(running_id)
670 self.job.record.expect_call("GOOD", "sub", "reboot.verify",
671 running_id)
672 self.job._check_post_reboot.expect_call("sub", running_id=running_id)
673 self.job.record.expect_call("END GOOD", "sub", "reboot",
674 optional_fields={"kernel": running_id})
676 # run test
677 self.job.end_reboot_and_verify(81234567, "2.6.15-smp", "sub")
678 self.god.check_playback()
681 def test_end_boot_and_verify_failure(self):
682 self.construct_job(True)
683 self.god.stub_function(self.job, "_record_reboot_failure")
685 # set up the job class
686 self.job._record_prefix = '\t\t'
688 self.god.stub_function(utils, "running_os_ident")
689 utils.running_os_ident.expect_call().and_return("2.6.15-smp")
691 utils.read_one_line.expect_call("/proc/cmdline").and_return(
692 "blah more-blah root=lala IDENT=81234567 blah-again")
694 self.job._record_reboot_failure.expect_call("sub", "reboot.verify",
695 "boot failure", running_id="2.6.15-smp")
697 # run test
698 self.assertRaises(error.JobError, self.job.end_reboot_and_verify,
699 91234567, "2.6.16-smp", "sub")
700 self.god.check_playback()
703 def test_parse_args(self):
704 test_set = {"a='foo bar baz' b='moo apt'":
705 ["a='foo bar baz'", "b='moo apt'"],
706 "a='foo bar baz' only=gah":
707 ["a='foo bar baz'", "only=gah"],
708 "a='b c d' no=argh":
709 ["a='b c d'", "no=argh"]}
710 for t in test_set:
711 parsed_args = job.base_client_job._parse_args(t)
712 expected_args = test_set[t]
713 self.assertEqual(parsed_args, expected_args)
716 if __name__ == "__main__":
717 unittest.main()