3 import logging
, os
, shutil
, sys
, time
, StringIO
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
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
32 class test_find_base_directories(
33 base_job_unittest
.test_find_base_directories
.generic_tests
,
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
):
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)
67 def start_logging(self
):
69 self
.god
.stub_function_to_return(job
.logging_manager
,
70 'get_logging_manager', manager())
72 def log_per_reboot_data(self
):
74 self
.god
.stub_function_to_return(job
.sysinfo
, 'sysinfo',
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())
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
)
98 """A simple placeholder for attributes"""
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
):
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
):
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,
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',
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')
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
,
184 harness
.select
.expect_call(None,
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')
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
)
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()
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
)
234 resultdir
, my_harness
= self
._setup
_pre
_record
_init
(cont
)
235 self
._setup
_post
_record
_init
(cont
, resultdir
, my_harness
)
239 options
.tag
= self
.jobtag
241 options
.harness
= None
242 options
.harness_args
= None
244 options
.verbose
= False
245 options
.hostname
= 'localhost'
246 options
.user
= 'my_user'
248 options
.tap_report
= None
249 self
.job
.__init
__(self
.control
, options
,
250 extra_copy_cmdline
=['more-blah'])
253 self
.god
.check_playback()
256 def get_partition_mock(self
, devname
):
258 Create a mock of a partition object and return it.
262 get_mountpoint
= self
.god
.create_mock_function('get_mountpoint')
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
)
280 options
.tag
= self
.jobtag
282 options
.harness
= None
283 options
.harness_args
= None
285 options
.verbose
= False
286 options
.hostname
= 'localhost'
287 options
.user
= 'my_user'
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' %
303 Exception, self
.job
.__init
__, self
.control
, options
,
304 drop_caches
=True, extra_copy_cmdline
=['more-blah'])
307 self
.god
.check_playback()
310 def test_relative_path(self
):
311 self
.construct_job(True)
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)
330 harness
.select
.expect_call(which
, self
.job
,
331 harness_args
).and_return(None)
334 self
.job
.harness_select(which
, harness_args
)
335 self
.god
.check_playback()
338 def test_config_set(self
):
339 self
.construct_job(True)
342 self
.god
.unstub(self
.job
, 'config_set')
346 self
.config
.set.expect_call(name
, val
)
349 self
.job
.config_set(name
, val
)
350 self
.god
.check_playback()
353 def test_config_get(self
):
354 self
.construct_job(True)
357 self
.god
.unstub(self
.job
, 'config_get')
361 self
.config
.get
.expect_call(name
).and_return(val
)
364 self
.job
.config_get(name
)
365 self
.god
.check_playback()
368 def test_setup_dirs_raise(self
):
369 self
.construct_job(True)
376 os
.path
.exists
.expect_call(tmp_dir
).and_return(True)
377 os
.path
.isdir
.expect_call(tmp_dir
).and_return(False)
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)
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')
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
)
404 self
.assertEqual(self
.job
.setup_dirs(None, tmp_dir
),
405 (results_dir3
, tmp_dir
))
406 self
.god
.check_playback()
410 self
.construct_job(True)
413 self
.god
.stub_function(self
.job
, "setup_dirs")
414 self
.god
.stub_class(xen
, "xen")
415 results
= 'results_dir'
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
,
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)
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'
444 path
= "somepath.rpm"
445 packages_dir
= os
.path
.join("autodir/packages", path
)
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
],
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)
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'
473 path
= "somepath.deb"
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
,
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)
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
):
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(
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()
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)
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):
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(
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()
550 self
.job
.run_test(testname
)
551 self
.god
.check_playback()
554 def test_report_reboot_failure(self
):
555 self
.construct_job(True)
558 self
.job
.record
.expect_call("ABORT", "sub", "reboot.verify",
560 self
.job
.record
.expect_call("END ABORT", "sub", "reboot",
561 optional_fields
={"kernel": "2.6.15-smp"})
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
):
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"]
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)
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)
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)',
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"})
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",
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
})
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")
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"],
709 ["a='b c d'", "no=argh"]}
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__":