job_unittest.py revision be583c18a66815a3d93fae411c5fd58788aff4c0
1#!/usr/bin/python 2 3import logging, os, shutil, sys, time, StringIO 4import common 5 6from autotest_lib.client.bin import job, boottool, config, sysinfo, harness 7from autotest_lib.client.bin import test, xen, kernel, utils 8from autotest_lib.client.common_lib import packages, error, log 9from autotest_lib.client.common_lib import logging_manager, logging_config 10from autotest_lib.client.common_lib import base_job_unittest 11from autotest_lib.client.common_lib.test_utils import mock, unittest 12 13 14class job_test_case(unittest.TestCase): 15 """Generic job TestCase class that defines a standard job setUp and 16 tearDown, with some standard stubs.""" 17 18 job_class = job.base_client_job 19 20 def setUp(self): 21 self.god = mock.mock_god() 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 26 27 28 def tearDown(self): 29 self.god.unstub_all() 30 31 32class test_find_base_directories( 33 base_job_unittest.test_find_base_directories.generic_tests, 34 job_test_case): 35 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') 40 41 42 def test_serverdir_is_none(self): 43 _, _, serverdir = self.job._find_base_directories() 44 self.assertEqual(serverdir, None) 45 46 47class 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'])) 53 54 55class 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 hostname = None 86 user = None 87 log = False 88 args = '' 89 self.god.stub_function_to_return(job.utils, 'drop_caches', None) 90 91 self.job._job_state = base_job_unittest.stub_job_state 92 self.job.__init__('/control', options) 93 94 95class dummy(object): 96 """A simple placeholder for attributes""" 97 pass 98 99 100class first_line_comparator(mock.argument_comparator): 101 def __init__(self, first_line): 102 self.first_line = first_line 103 104 105 def is_satisfied_by(self, parameter): 106 return self.first_line == parameter.splitlines()[0] 107 108 109class test_base_job(unittest.TestCase): 110 def setUp(self): 111 # make god 112 self.god = mock.mock_god() 113 114 # need to set some environ variables 115 self.autodir = "autodir" 116 os.environ['AUTODIR'] = self.autodir 117 118 # set up some variables 119 self.control = "control" 120 self.jobtag = "jobtag" 121 122 # get rid of stdout and logging 123 sys.stdout = StringIO.StringIO() 124 logging_manager.configure_logging(logging_config.TestingConfig()) 125 logging.disable(logging.CRITICAL) 126 def dummy_configure_logging(*args, **kwargs): 127 pass 128 self.god.stub_with(logging_manager, 'configure_logging', 129 dummy_configure_logging) 130 real_get_logging_manager = logging_manager.get_logging_manager 131 def get_logging_manager_no_fds(manage_stdout_and_stderr=False, 132 redirect_fds=False): 133 return real_get_logging_manager(manage_stdout_and_stderr, False) 134 self.god.stub_with(logging_manager, 'get_logging_manager', 135 get_logging_manager_no_fds) 136 137 # stub out some stuff 138 self.god.stub_function(os.path, 'exists') 139 self.god.stub_function(os.path, 'isdir') 140 self.god.stub_function(os, 'makedirs') 141 self.god.stub_function(os, 'mkdir') 142 self.god.stub_function(os, 'remove') 143 self.god.stub_function(shutil, 'rmtree') 144 self.god.stub_function(shutil, 'copyfile') 145 self.god.stub_function(job, 'open') 146 self.god.stub_function(utils, 'system') 147 self.god.stub_function(utils, 'drop_caches') 148 self.god.stub_function(harness, 'select') 149 self.god.stub_function(sysinfo, 'log_per_reboot_data') 150 151 self.god.stub_class(config, 'config') 152 self.god.stub_class(job.local_host, 'LocalHost') 153 self.god.stub_class(boottool, 'boottool') 154 self.god.stub_class(sysinfo, 'sysinfo') 155 156 self.god.stub_class_method(job.base_client_job, 157 '_cleanup_debugdir_files') 158 self.god.stub_class_method(job.base_client_job, '_cleanup_results_dir') 159 160 self.god.stub_with(job.base_job.job_directory, '_ensure_valid', 161 lambda *_: None) 162 163 164 def tearDown(self): 165 sys.stdout = sys.__stdout__ 166 self.god.unstub_all() 167 168 169 def _setup_pre_record_init(self, cont): 170 self.god.stub_function(self.job, '_load_state') 171 172 resultdir = os.path.join(self.autodir, 'results', self.jobtag) 173 tmpdir = os.path.join(self.autodir, 'tmp') 174 if not cont: 175 job.base_client_job._cleanup_debugdir_files.expect_call() 176 job.base_client_job._cleanup_results_dir.expect_call() 177 178 self.job._load_state.expect_call() 179 180 my_harness = self.god.create_mock_class(harness.harness, 181 'my_harness') 182 harness.select.expect_call(None, 183 self.job).and_return(my_harness) 184 185 return resultdir, my_harness 186 187 188 def _setup_post_record_init(self, cont, resultdir, my_harness): 189 # now some specific stubs 190 self.god.stub_function(self.job, 'config_get') 191 self.god.stub_function(self.job, 'config_set') 192 self.god.stub_function(self.job, 'record') 193 194 # other setup 195 results = os.path.join(self.autodir, 'results') 196 download = os.path.join(self.autodir, 'tests', 'download') 197 pkgdir = os.path.join(self.autodir, 'packages') 198 199 utils.drop_caches.expect_call() 200 job_sysinfo = sysinfo.sysinfo.expect_new(resultdir) 201 if not cont: 202 os.path.exists.expect_call(download).and_return(False) 203 os.mkdir.expect_call(download) 204 shutil.copyfile.expect_call(mock.is_string_comparator(), 205 os.path.join(resultdir, 'control')) 206 207 self.config = config.config.expect_new(self.job) 208 self.job.config_get.expect_call( 209 'boottool.executable').and_return(None) 210 bootloader = boottool.boottool.expect_new(None) 211 job.local_host.LocalHost.expect_new(hostname='localhost', 212 bootloader=bootloader) 213 job_sysinfo.log_per_reboot_data.expect_call() 214 if not cont: 215 self.job.record.expect_call('START', None, None) 216 217 my_harness.run_start.expect_call() 218 219 self.god.stub_function(utils, 'read_one_line') 220 utils.read_one_line.expect_call('/proc/cmdline').and_return( 221 'blah more-blah root=lala IDENT=81234567 blah-again console=tty1') 222 self.job.config_set.expect_call('boot.default_args', 223 'more-blah console=tty1') 224 225 226 def construct_job(self, cont): 227 # will construct class instance using __new__ 228 self.job = job.base_client_job.__new__(job.base_client_job) 229 230 # record 231 resultdir, my_harness = self._setup_pre_record_init(cont) 232 self._setup_post_record_init(cont, resultdir, my_harness) 233 234 # finish constructor 235 options = dummy() 236 options.tag = self.jobtag 237 options.cont = cont 238 options.harness = None 239 options.log = False 240 options.verbose = False 241 options.hostname = 'localhost' 242 options.user = 'my_user' 243 options.args = '' 244 self.job.__init__(self.control, options, 245 extra_copy_cmdline=['more-blah']) 246 247 # check 248 self.god.check_playback() 249 250 251 def get_partition_mock(self, devname): 252 """ 253 Create a mock of a partition object and return it. 254 """ 255 class mock(object): 256 device = devname 257 get_mountpoint = self.god.create_mock_function('get_mountpoint') 258 return mock 259 260 261 def test_constructor_first_run(self): 262 self.construct_job(False) 263 264 265 def test_constructor_continuation(self): 266 self.construct_job(True) 267 268 269 def test_constructor_post_record_failure(self): 270 """ 271 Test post record initialization failure. 272 """ 273 self.job = job.base_client_job.__new__(job.base_client_job) 274 options = dummy() 275 options.tag = self.jobtag 276 options.cont = False 277 options.harness = None 278 options.log = False 279 options.verbose = False 280 options.hostname = 'localhost' 281 options.user = 'my_user' 282 options.args = '' 283 error = Exception('fail') 284 285 self.god.stub_function(self.job, '_post_record_init') 286 self.god.stub_function(self.job, 'record') 287 288 self._setup_pre_record_init(False) 289 self.job._post_record_init.expect_call( 290 self.control, options, True, ['more-blah']).and_raises(error) 291 self.job.record.expect_call( 292 'ABORT', None, None,'client.bin.job.__init__ failed: %s' % 293 str(error)) 294 295 self.assertRaises( 296 Exception, self.job.__init__, self.control, options, 297 drop_caches=True, extra_copy_cmdline=['more-blah']) 298 299 # check 300 self.god.check_playback() 301 302 303 def test_relative_path(self): 304 self.construct_job(True) 305 dummy = "asdf" 306 ret = self.job.relative_path(os.path.join(self.job.resultdir, dummy)) 307 self.assertEquals(ret, dummy) 308 309 310 def test_control_functions(self): 311 self.construct_job(True) 312 control_file = "blah" 313 self.job.control_set(control_file) 314 self.assertEquals(self.job.control_get(), os.path.abspath(control_file)) 315 316 317 def test_harness_select(self): 318 self.construct_job(True) 319 320 # record 321 which = "which" 322 harness.select.expect_call(which, self.job).and_return(None) 323 324 # run and test 325 self.job.harness_select(which) 326 self.god.check_playback() 327 328 329 def test_config_set(self): 330 self.construct_job(True) 331 332 # unstub config_set 333 self.god.unstub(self.job, 'config_set') 334 # record 335 name = "foo" 336 val = 10 337 self.config.set.expect_call(name, val) 338 339 # run and test 340 self.job.config_set(name, val) 341 self.god.check_playback() 342 343 344 def test_config_get(self): 345 self.construct_job(True) 346 347 # unstub config_get 348 self.god.unstub(self.job, 'config_get') 349 # record 350 name = "foo" 351 val = 10 352 self.config.get.expect_call(name).and_return(val) 353 354 # run and test 355 self.job.config_get(name) 356 self.god.check_playback() 357 358 359 def test_setup_dirs_raise(self): 360 self.construct_job(True) 361 362 # setup 363 results_dir = 'foo' 364 tmp_dir = 'bar' 365 366 # record 367 os.path.exists.expect_call(tmp_dir).and_return(True) 368 os.path.isdir.expect_call(tmp_dir).and_return(False) 369 370 # test 371 self.assertRaises(ValueError, self.job.setup_dirs, results_dir, tmp_dir) 372 self.god.check_playback() 373 374 375 def test_setup_dirs(self): 376 self.construct_job(True) 377 378 # setup 379 results_dir1 = os.path.join(self.job.resultdir, 'build') 380 results_dir2 = os.path.join(self.job.resultdir, 'build.2') 381 results_dir3 = os.path.join(self.job.resultdir, 'build.3') 382 tmp_dir = 'bar' 383 384 # record 385 os.path.exists.expect_call(tmp_dir).and_return(False) 386 os.mkdir.expect_call(tmp_dir) 387 os.path.isdir.expect_call(tmp_dir).and_return(True) 388 os.path.exists.expect_call(results_dir1).and_return(True) 389 os.path.exists.expect_call(results_dir2).and_return(True) 390 os.path.exists.expect_call(results_dir3).and_return(False) 391 os.path.exists.expect_call(results_dir3).and_return(False) 392 os.mkdir.expect_call(results_dir3) 393 394 # test 395 self.assertEqual(self.job.setup_dirs(None, tmp_dir), 396 (results_dir3, tmp_dir)) 397 self.god.check_playback() 398 399 400 def test_xen(self): 401 self.construct_job(True) 402 403 # setup 404 self.god.stub_function(self.job, "setup_dirs") 405 self.god.stub_class(xen, "xen") 406 results = 'results_dir' 407 tmp = 'tmp' 408 build = 'xen' 409 base_tree = object() 410 411 # record 412 self.job.setup_dirs.expect_call(results, 413 tmp).and_return((results, tmp)) 414 myxen = xen.xen.expect_new(self.job, base_tree, results, tmp, build, 415 False, None) 416 417 # run job and check 418 axen = self.job.xen(base_tree, results, tmp) 419 self.god.check_playback() 420 self.assertEquals(myxen, axen) 421 422 423 def test_kernel_rpm(self): 424 self.construct_job(True) 425 426 # setup 427 self.god.stub_function(self.job, "setup_dirs") 428 self.god.stub_class(kernel, "rpm_kernel") 429 self.god.stub_function(kernel, "preprocess_path") 430 self.god.stub_function(self.job.pkgmgr, "fetch_pkg") 431 self.god.stub_function(utils, "get_os_vendor") 432 results = 'results_dir' 433 tmp = 'tmp' 434 build = 'xen' 435 path = "somepath.rpm" 436 packages_dir = os.path.join("autodir/packages", path) 437 438 # record 439 self.job.setup_dirs.expect_call(results, 440 tmp).and_return((results, tmp)) 441 kernel.preprocess_path.expect_call(path).and_return(path) 442 os.path.exists.expect_call(path).and_return(False) 443 self.job.pkgmgr.fetch_pkg.expect_call(path, packages_dir, repo_url='') 444 utils.get_os_vendor.expect_call() 445 mykernel = kernel.rpm_kernel.expect_new(self.job, [packages_dir], 446 results) 447 448 # check 449 akernel = self.job.kernel(path, results, tmp) 450 self.god.check_playback() 451 self.assertEquals(mykernel, akernel) 452 453 454 def test_kernel(self): 455 self.construct_job(True) 456 457 # setup 458 self.god.stub_function(self.job, "setup_dirs") 459 self.god.stub_class(kernel, "kernel") 460 self.god.stub_function(kernel, "preprocess_path") 461 results = 'results_dir' 462 tmp = 'tmp' 463 build = 'linux' 464 path = "somepath.deb" 465 466 # record 467 self.job.setup_dirs.expect_call(results, 468 tmp).and_return((results, tmp)) 469 kernel.preprocess_path.expect_call(path).and_return(path) 470 mykernel = kernel.kernel.expect_new(self.job, path, results, tmp, 471 build, False) 472 473 # check 474 akernel = self.job.kernel(path, results, tmp) 475 self.god.check_playback() 476 self.assertEquals(mykernel, akernel) 477 478 479 def test_run_test_logs_test_error_from_unhandled_error(self): 480 self.construct_job(True) 481 482 # set up stubs 483 self.god.stub_function(self.job.pkgmgr, 'get_package_name') 484 self.god.stub_function(self.job, "_runtest") 485 486 # create an unhandled error object 487 class MyError(error.TestError): 488 pass 489 real_error = MyError("this is the real error message") 490 unhandled_error = error.UnhandledTestError(real_error) 491 492 # set up the recording 493 testname = "error_test" 494 outputdir = os.path.join(self.job.resultdir, testname) 495 self.job.pkgmgr.get_package_name.expect_call( 496 testname, 'test').and_return(("", testname)) 497 os.path.exists.expect_call(outputdir).and_return(False) 498 self.job.record.expect_call("START", testname, testname) 499 self.job._runtest.expect_call(testname, "", (), {}).and_raises( 500 unhandled_error) 501 self.job.record.expect_call("ERROR", testname, testname, 502 first_line_comparator(str(real_error))) 503 self.job.record.expect_call("END ERROR", testname, testname) 504 self.job.harness.run_test_complete.expect_call() 505 utils.drop_caches.expect_call() 506 507 # run and check 508 self.job.run_test(testname) 509 self.god.check_playback() 510 511 512 def test_run_test_logs_non_test_error_from_unhandled_error(self): 513 self.construct_job(True) 514 515 # set up stubs 516 self.god.stub_function(self.job.pkgmgr, 'get_package_name') 517 self.god.stub_function(self.job, "_runtest") 518 519 # create an unhandled error object 520 class MyError(Exception): 521 pass 522 real_error = MyError("this is the real error message") 523 unhandled_error = error.UnhandledTestError(real_error) 524 reason = first_line_comparator("Unhandled MyError: %s" % real_error) 525 526 # set up the recording 527 testname = "error_test" 528 outputdir = os.path.join(self.job.resultdir, testname) 529 self.job.pkgmgr.get_package_name.expect_call( 530 testname, 'test').and_return(("", testname)) 531 os.path.exists.expect_call(outputdir).and_return(False) 532 self.job.record.expect_call("START", testname, testname) 533 self.job._runtest.expect_call(testname, "", (), {}).and_raises( 534 unhandled_error) 535 self.job.record.expect_call("ERROR", testname, testname, reason) 536 self.job.record.expect_call("END ERROR", testname, testname) 537 self.job.harness.run_test_complete.expect_call() 538 utils.drop_caches.expect_call() 539 540 # run and check 541 self.job.run_test(testname) 542 self.god.check_playback() 543 544 545 def test_report_reboot_failure(self): 546 self.construct_job(True) 547 548 # record 549 self.job.record.expect_call("ABORT", "sub", "reboot.verify", 550 "boot failure") 551 self.job.record.expect_call("END ABORT", "sub", "reboot", 552 optional_fields={"kernel": "2.6.15-smp"}) 553 554 # playback 555 self.job._record_reboot_failure("sub", "reboot.verify", "boot failure", 556 running_id="2.6.15-smp") 557 self.god.check_playback() 558 559 560 def _setup_check_post_reboot(self, mount_info, cpu_count): 561 # setup 562 self.god.stub_function(job.partition_lib, "get_partition_list") 563 self.god.stub_function(utils, "count_cpus") 564 565 part_list = [self.get_partition_mock("/dev/hda1"), 566 self.get_partition_mock("/dev/hdb1")] 567 mount_list = ["/mnt/hda1", "/mnt/hdb1"] 568 569 # record 570 job.partition_lib.get_partition_list.expect_call( 571 self.job, exclude_swap=False).and_return(part_list) 572 for i in xrange(len(part_list)): 573 part_list[i].get_mountpoint.expect_call().and_return(mount_list[i]) 574 if cpu_count is not None: 575 utils.count_cpus.expect_call().and_return(cpu_count) 576 self.job._state.set('client', 'mount_info', mount_info) 577 self.job._state.set('client', 'cpu_count', 8) 578 579 580 def test_check_post_reboot_success(self): 581 self.construct_job(True) 582 583 mount_info = set([("/dev/hda1", "/mnt/hda1"), 584 ("/dev/hdb1", "/mnt/hdb1")]) 585 self._setup_check_post_reboot(mount_info, 8) 586 587 # playback 588 self.job._check_post_reboot("sub") 589 self.god.check_playback() 590 591 592 def test_check_post_reboot_mounts_failure(self): 593 self.construct_job(True) 594 595 mount_info = set([("/dev/hda1", "/mnt/hda1")]) 596 self._setup_check_post_reboot(mount_info, None) 597 598 self.god.stub_function(self.job, "_record_reboot_failure") 599 self.job._record_reboot_failure.expect_call("sub", 600 "reboot.verify_config", "mounted partitions are different after" 601 " reboot (old entries: set([]), new entries: set([('/dev/hdb1'," 602 " '/mnt/hdb1')]))", running_id=None) 603 604 # playback 605 self.assertRaises(error.JobError, self.job._check_post_reboot, "sub") 606 self.god.check_playback() 607 608 609 def test_check_post_reboot_cpu_failure(self): 610 self.construct_job(True) 611 612 mount_info = set([("/dev/hda1", "/mnt/hda1"), 613 ("/dev/hdb1", "/mnt/hdb1")]) 614 self._setup_check_post_reboot(mount_info, 4) 615 616 self.god.stub_function(self.job, "_record_reboot_failure") 617 self.job._record_reboot_failure.expect_call( 618 'sub', 'reboot.verify_config', 619 'Number of CPUs changed after reboot (old count: 8, new count: 4)', 620 running_id=None) 621 622 # playback 623 self.assertRaises(error.JobError, self.job._check_post_reboot, "sub") 624 self.god.check_playback() 625 626 627 def test_end_boot(self): 628 self.construct_job(True) 629 self.god.stub_function(self.job, "_check_post_reboot") 630 631 # set up the job class 632 self.job._record_prefix = '\t\t' 633 634 self.job._check_post_reboot.expect_call("sub", running_id=None) 635 self.job.record.expect_call("END GOOD", "sub", "reboot", 636 optional_fields={"kernel": "2.6.15-smp", 637 "patch0": "patchname"}) 638 639 # run test 640 self.job.end_reboot("sub", "2.6.15-smp", ["patchname"]) 641 self.god.check_playback() 642 643 644 def test_end_boot_and_verify_success(self): 645 self.construct_job(True) 646 self.god.stub_function(self.job, "_check_post_reboot") 647 648 # set up the job class 649 self.job._record_prefix = '\t\t' 650 651 self.god.stub_function(utils, "running_os_ident") 652 utils.running_os_ident.expect_call().and_return("2.6.15-smp") 653 654 utils.read_one_line.expect_call("/proc/cmdline").and_return( 655 "blah more-blah root=lala IDENT=81234567 blah-again") 656 657 self.god.stub_function(utils, "running_os_full_version") 658 running_id = "2.6.15-smp" 659 utils.running_os_full_version.expect_call().and_return(running_id) 660 661 self.job.record.expect_call("GOOD", "sub", "reboot.verify", 662 running_id) 663 self.job._check_post_reboot.expect_call("sub", running_id=running_id) 664 self.job.record.expect_call("END GOOD", "sub", "reboot", 665 optional_fields={"kernel": running_id}) 666 667 # run test 668 self.job.end_reboot_and_verify(81234567, "2.6.15-smp", "sub") 669 self.god.check_playback() 670 671 672 def test_end_boot_and_verify_failure(self): 673 self.construct_job(True) 674 self.god.stub_function(self.job, "_record_reboot_failure") 675 676 # set up the job class 677 self.job._record_prefix = '\t\t' 678 679 self.god.stub_function(utils, "running_os_ident") 680 utils.running_os_ident.expect_call().and_return("2.6.15-smp") 681 682 utils.read_one_line.expect_call("/proc/cmdline").and_return( 683 "blah more-blah root=lala IDENT=81234567 blah-again") 684 685 self.job._record_reboot_failure.expect_call("sub", "reboot.verify", 686 "boot failure", running_id="2.6.15-smp") 687 688 # run test 689 self.assertRaises(error.JobError, self.job.end_reboot_and_verify, 690 91234567, "2.6.16-smp", "sub") 691 self.god.check_playback() 692 693 694if __name__ == "__main__": 695 unittest.main() 696