• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python
2# pylint: disable=missing-docstring
3
4import logging
5import os
6import shutil
7import StringIO
8import sys
9import unittest
10
11import common
12from autotest_lib.client.bin import job, sysinfo, harness
13from autotest_lib.client.bin import utils
14from autotest_lib.client.common_lib import error
15from autotest_lib.client.common_lib import logging_manager, logging_config
16from autotest_lib.client.common_lib import base_job_unittest
17from autotest_lib.client.common_lib.test_utils import mock
18
19
20class job_test_case(unittest.TestCase):
21    """Generic job TestCase class that defines a standard job setUp and
22    tearDown, with some standard stubs."""
23
24    job_class = job.base_client_job
25
26    def setUp(self):
27        self.god = mock.mock_god(ut=self)
28        self.god.stub_with(job.base_client_job, '_get_environ_autodir',
29                           classmethod(lambda cls: '/adir'))
30        self.job = self.job_class.__new__(self.job_class)
31        self.job._job_directory = base_job_unittest.stub_job_directory
32
33
34    def tearDown(self):
35        self.god.unstub_all()
36
37
38class test_find_base_directories(
39        base_job_unittest.test_find_base_directories.generic_tests,
40        job_test_case):
41
42    def test_autodir_equals_clientdir(self):
43        autodir, clientdir, _ = self.job._find_base_directories()
44        self.assertEqual(autodir, '/adir')
45        self.assertEqual(clientdir, '/adir')
46
47
48    def test_serverdir_is_none(self):
49        _, _, serverdir = self.job._find_base_directories()
50        self.assertEqual(serverdir, None)
51
52
53class abstract_test_init(base_job_unittest.test_init.generic_tests):
54    """Generic client job mixin used when defining variations on the
55    job.__init__ generic tests."""
56    OPTIONAL_ATTRIBUTES = (
57        base_job_unittest.test_init.generic_tests.OPTIONAL_ATTRIBUTES
58        - set(['control', 'harness']))
59
60
61class test_init_minimal_options(abstract_test_init, job_test_case):
62    def call_init(self):
63        # TODO(jadmanski): refactor more of the __init__ code to not need to
64        # stub out countless random APIs
65        self.god.stub_function_to_return(job.os, 'mkdir', None)
66        self.god.stub_function_to_return(job.os.path, 'exists', True)
67        self.god.stub_function_to_return(self.job, '_load_state', None)
68        self.god.stub_function_to_return(self.job, 'record', None)
69        self.god.stub_function_to_return(job.shutil, 'copyfile', None)
70        self.god.stub_function_to_return(job.logging_manager,
71                                         'configure_logging', None)
72        class manager:
73            def start_logging(self):
74                return None
75        self.god.stub_function_to_return(job.logging_manager,
76                                         'get_logging_manager', manager())
77        class stub_sysinfo:
78            def log_per_reboot_data(self):
79                return None
80        self.god.stub_function_to_return(job.sysinfo, 'sysinfo',
81                                         stub_sysinfo())
82        class stub_harness:
83            run_start = lambda self: None
84        self.god.stub_function_to_return(job.harness, 'select', stub_harness())
85        class options:
86            tag = ''
87            verbose = False
88            cont = False
89            harness = 'stub'
90            harness_args = None
91            hostname = None
92            user = None
93            log = False
94            args = ''
95            output_dir = ''
96            tap_report = None
97        self.god.stub_function_to_return(job.utils, 'drop_caches', None)
98
99        self.job._job_state = base_job_unittest.stub_job_state
100        self.job.__init__('/control', options)
101
102
103class dummy(object):
104    """A simple placeholder for attributes"""
105    pass
106
107
108class first_line_comparator(mock.argument_comparator):
109    def __init__(self, first_line):
110        self.first_line = first_line
111
112
113    def is_satisfied_by(self, parameter):
114        return self.first_line == parameter.splitlines()[0]
115
116
117class test_base_job(unittest.TestCase):
118    def setUp(self):
119        # make god
120        self.god = mock.mock_god(ut=self)
121
122        # need to set some environ variables
123        self.autodir = "autodir"
124        os.environ['AUTODIR'] = self.autodir
125
126        # set up some variables
127        self.control = "control"
128        self.jobtag = "jobtag"
129
130        # get rid of stdout and logging
131        sys.stdout = StringIO.StringIO()
132        logging_manager.configure_logging(logging_config.TestingConfig())
133        logging.disable(logging.CRITICAL)
134        def dummy_configure_logging(*args, **kwargs):
135            pass
136        self.god.stub_with(logging_manager, 'configure_logging',
137                           dummy_configure_logging)
138        real_get_logging_manager = logging_manager.get_logging_manager
139        def get_logging_manager_no_fds(manage_stdout_and_stderr=False,
140                                       redirect_fds=False):
141            return real_get_logging_manager(manage_stdout_and_stderr, False)
142        self.god.stub_with(logging_manager, 'get_logging_manager',
143                           get_logging_manager_no_fds)
144
145        # stub out some stuff
146        self.god.stub_function(os.path, 'exists')
147        self.god.stub_function(os.path, 'isdir')
148        self.god.stub_function(os, 'makedirs')
149        self.god.stub_function(os, 'mkdir')
150        self.god.stub_function(os, 'remove')
151        self.god.stub_function(shutil, 'rmtree')
152        self.god.stub_function(shutil, 'copyfile')
153        self.god.stub_function(job, 'open')
154        self.god.stub_function(utils, 'system')
155        self.god.stub_function(utils, 'drop_caches')
156        self.god.stub_function(harness, 'select')
157        self.god.stub_function(sysinfo, 'log_per_reboot_data')
158
159        self.god.stub_class(job.local_host, 'LocalHost')
160        self.god.stub_class(sysinfo, 'sysinfo')
161
162        self.god.stub_class_method(job.base_client_job,
163                                   '_cleanup_debugdir_files')
164        self.god.stub_class_method(job.base_client_job, '_cleanup_results_dir')
165
166        self.god.stub_with(job.base_job.job_directory, '_ensure_valid',
167                           lambda *_: None)
168
169
170    def tearDown(self):
171        sys.stdout = sys.__stdout__
172        self.god.unstub_all()
173
174
175    def _setup_pre_record_init(self, cont):
176        self.god.stub_function(self.job, '_load_state')
177
178        resultdir = os.path.join(self.autodir, 'results', self.jobtag)
179        tmpdir = os.path.join(self.autodir, 'tmp')
180        if not cont:
181            job.base_client_job._cleanup_debugdir_files.expect_call()
182            job.base_client_job._cleanup_results_dir.expect_call()
183
184        self.job._load_state.expect_call()
185
186        my_harness = self.god.create_mock_class(harness.harness,
187                                                'my_harness')
188        harness.select.expect_call(None,
189                                   self.job,
190                                   None).and_return(my_harness)
191
192        return resultdir, my_harness
193
194
195    def _setup_post_record_init(self, cont, resultdir, my_harness):
196        # now some specific stubs
197        self.god.stub_function(self.job, 'config_get')
198        self.god.stub_function(self.job, 'config_set')
199        self.god.stub_function(self.job, 'record')
200
201        # other setup
202        results = os.path.join(self.autodir, 'results')
203        download = os.path.join(self.autodir, 'tests', 'download')
204        pkgdir = os.path.join(self.autodir, 'packages')
205
206        utils.drop_caches.expect_call()
207        job_sysinfo = sysinfo.sysinfo.expect_new(resultdir)
208        if not cont:
209            os.path.exists.expect_call(download).and_return(False)
210            os.mkdir.expect_call(download)
211            shutil.copyfile.expect_call(mock.is_string_comparator(),
212                                 os.path.join(resultdir, 'control'))
213
214        job.local_host.LocalHost.expect_new(hostname='localhost')
215        job_sysinfo.log_per_reboot_data.expect_call()
216        if not cont:
217            self.job.record.expect_call('START', None, None)
218
219        my_harness.run_start.expect_call()
220
221
222    def construct_job(self, cont):
223        # will construct class instance using __new__
224        self.job = job.base_client_job.__new__(job.base_client_job)
225
226        # record
227        resultdir, my_harness = self._setup_pre_record_init(cont)
228        self._setup_post_record_init(cont, resultdir, my_harness)
229
230        # finish constructor
231        options = dummy()
232        options.tag = self.jobtag
233        options.cont = cont
234        options.harness = None
235        options.harness_args = None
236        options.log = False
237        options.verbose = False
238        options.hostname = 'localhost'
239        options.user = 'my_user'
240        options.args = ''
241        options.output_dir = ''
242        options.tap_report = None
243        self.job.__init__(self.control, options)
244
245        # check
246        self.god.check_playback()
247
248
249    def get_partition_mock(self, devname):
250        """
251        Create a mock of a partition object and return it.
252        """
253        class mock(object):
254            device = devname
255            get_mountpoint = self.god.create_mock_function('get_mountpoint')
256        return mock
257
258
259    def test_constructor_first_run(self):
260        self.construct_job(False)
261
262
263    def test_constructor_continuation(self):
264        self.construct_job(True)
265
266
267    def test_constructor_post_record_failure(self):
268        """
269        Test post record initialization failure.
270        """
271        self.job = job.base_client_job.__new__(job.base_client_job)
272        options = dummy()
273        options.tag = self.jobtag
274        options.cont = False
275        options.harness = None
276        options.harness_args = None
277        options.log = False
278        options.verbose = False
279        options.hostname = 'localhost'
280        options.user = 'my_user'
281        options.args = ''
282        options.output_dir = ''
283        options.tap_report = None
284        error = Exception('fail')
285
286        self.god.stub_function(self.job, '_post_record_init')
287        self.god.stub_function(self.job, 'record')
288
289        self._setup_pre_record_init(False)
290        self.job._post_record_init.expect_call(
291                self.control, options, True).and_raises(error)
292        self.job.record.expect_call(
293                'ABORT', None, None,'client.bin.job.__init__ failed: %s' %
294                str(error))
295
296        self.assertRaises(
297                Exception, self.job.__init__, self.control, options,
298                drop_caches=True)
299
300        # check
301        self.god.check_playback()
302
303
304    def test_control_functions(self):
305        self.construct_job(True)
306        control_file = "blah"
307        self.job.control_set(control_file)
308        self.assertEquals(self.job.control_get(), os.path.abspath(control_file))
309
310
311    def test_harness_select(self):
312        self.construct_job(True)
313
314        # record
315        which = "which"
316        harness_args = ''
317        harness.select.expect_call(which, self.job,
318                                   harness_args).and_return(None)
319
320        # run and test
321        self.job.harness_select(which, harness_args)
322        self.god.check_playback()
323
324
325    def test_setup_dirs_raise(self):
326        self.construct_job(True)
327
328        # setup
329        results_dir = 'foo'
330        tmp_dir = 'bar'
331
332        # record
333        os.path.exists.expect_call(tmp_dir).and_return(True)
334        os.path.isdir.expect_call(tmp_dir).and_return(False)
335
336        # test
337        self.assertRaises(ValueError, self.job.setup_dirs, results_dir, tmp_dir)
338        self.god.check_playback()
339
340
341    def test_setup_dirs(self):
342        self.construct_job(True)
343
344        # setup
345        results_dir1 = os.path.join(self.job.resultdir, 'build')
346        results_dir2 = os.path.join(self.job.resultdir, 'build.2')
347        results_dir3 = os.path.join(self.job.resultdir, 'build.3')
348        tmp_dir = 'bar'
349
350        # record
351        os.path.exists.expect_call(tmp_dir).and_return(False)
352        os.mkdir.expect_call(tmp_dir)
353        os.path.isdir.expect_call(tmp_dir).and_return(True)
354        os.path.exists.expect_call(results_dir1).and_return(True)
355        os.path.exists.expect_call(results_dir2).and_return(True)
356        os.path.exists.expect_call(results_dir3).and_return(False)
357        os.path.exists.expect_call(results_dir3).and_return(False)
358        os.mkdir.expect_call(results_dir3)
359
360        # test
361        self.assertEqual(self.job.setup_dirs(None, tmp_dir),
362                         (results_dir3, tmp_dir))
363        self.god.check_playback()
364
365
366    def test_run_test_logs_test_error_from_unhandled_error(self):
367        self.construct_job(True)
368
369        # set up stubs
370        self.god.stub_function(self.job.pkgmgr, 'get_package_name')
371        self.god.stub_function(self.job, "_runtest")
372
373        # create an unhandled error object
374        class MyError(error.TestError):
375            pass
376        real_error = MyError("this is the real error message")
377        unhandled_error = error.UnhandledTestError(real_error)
378
379        # set up the recording
380        testname = "error_test"
381        outputdir = os.path.join(self.job.resultdir, testname)
382        self.job.pkgmgr.get_package_name.expect_call(
383            testname, 'test').and_return(("", testname))
384        os.path.exists.expect_call(outputdir).and_return(False)
385        self.job.record.expect_call("START", testname, testname,
386                                    optional_fields=None)
387        self.job._runtest.expect_call(testname, "", None, (), {}).and_raises(
388            unhandled_error)
389        self.job.record.expect_call("ERROR", testname, testname,
390                                    first_line_comparator(str(real_error)))
391        self.job.record.expect_call("END ERROR", testname, testname)
392        self.job.harness.run_test_complete.expect_call()
393        utils.drop_caches.expect_call()
394
395        # run and check
396        self.job.run_test(testname)
397        self.god.check_playback()
398
399
400    def test_run_test_logs_non_test_error_from_unhandled_error(self):
401        self.construct_job(True)
402
403        # set up stubs
404        self.god.stub_function(self.job.pkgmgr, 'get_package_name')
405        self.god.stub_function(self.job, "_runtest")
406
407        # create an unhandled error object
408        class MyError(Exception):
409            pass
410        real_error = MyError("this is the real error message")
411        unhandled_error = error.UnhandledTestError(real_error)
412        reason = first_line_comparator("Unhandled MyError: %s" % real_error)
413
414        # set up the recording
415        testname = "error_test"
416        outputdir = os.path.join(self.job.resultdir, testname)
417        self.job.pkgmgr.get_package_name.expect_call(
418            testname, 'test').and_return(("", testname))
419        os.path.exists.expect_call(outputdir).and_return(False)
420        self.job.record.expect_call("START", testname, testname,
421                                    optional_fields=None)
422        self.job._runtest.expect_call(testname, "", None, (), {}).and_raises(
423            unhandled_error)
424        self.job.record.expect_call("ERROR", testname, testname, reason)
425        self.job.record.expect_call("END ERROR", testname, testname)
426        self.job.harness.run_test_complete.expect_call()
427        utils.drop_caches.expect_call()
428
429        # run and check
430        self.job.run_test(testname)
431        self.god.check_playback()
432
433
434    def test_report_reboot_failure(self):
435        self.construct_job(True)
436
437        # record
438        self.job.record.expect_call("ABORT", "sub", "reboot.verify",
439                                    "boot failure")
440        self.job.record.expect_call("END ABORT", "sub", "reboot",
441                                    optional_fields={"kernel": "2.6.15-smp"})
442
443        # playback
444        self.job._record_reboot_failure("sub", "reboot.verify", "boot failure",
445                                        running_id="2.6.15-smp")
446        self.god.check_playback()
447
448
449    def _setup_check_post_reboot(self, mount_info, cpu_count):
450        # setup
451        self.god.stub_function(job.partition_lib, "get_partition_list")
452        self.god.stub_function(utils, "count_cpus")
453
454        part_list = [self.get_partition_mock("/dev/hda1"),
455                     self.get_partition_mock("/dev/hdb1")]
456        mount_list = ["/mnt/hda1", "/mnt/hdb1"]
457
458        # record
459        job.partition_lib.get_partition_list.expect_call(
460                self.job, exclude_swap=False).and_return(part_list)
461        for i in xrange(len(part_list)):
462            part_list[i].get_mountpoint.expect_call().and_return(mount_list[i])
463        if cpu_count is not None:
464            utils.count_cpus.expect_call().and_return(cpu_count)
465        self.job._state.set('client', 'mount_info', mount_info)
466        self.job._state.set('client', 'cpu_count', 8)
467
468
469    def test_check_post_reboot_success(self):
470        self.construct_job(True)
471
472        mount_info = set([("/dev/hda1", "/mnt/hda1"),
473                          ("/dev/hdb1", "/mnt/hdb1")])
474        self._setup_check_post_reboot(mount_info, 8)
475
476        # playback
477        self.job._check_post_reboot("sub")
478        self.god.check_playback()
479
480
481    def test_check_post_reboot_mounts_failure(self):
482        self.construct_job(True)
483
484        mount_info = set([("/dev/hda1", "/mnt/hda1")])
485        self._setup_check_post_reboot(mount_info, None)
486
487        self.god.stub_function(self.job, "_record_reboot_failure")
488        self.job._record_reboot_failure.expect_call("sub",
489                "reboot.verify_config", "mounted partitions are different after"
490                " reboot (old entries: set([]), new entries: set([('/dev/hdb1',"
491                " '/mnt/hdb1')]))", running_id=None)
492
493        # playback
494        self.assertRaises(error.JobError, self.job._check_post_reboot, "sub")
495        self.god.check_playback()
496
497
498    def test_check_post_reboot_cpu_failure(self):
499        self.construct_job(True)
500
501        mount_info = set([("/dev/hda1", "/mnt/hda1"),
502                          ("/dev/hdb1", "/mnt/hdb1")])
503        self._setup_check_post_reboot(mount_info, 4)
504
505        self.god.stub_function(self.job, "_record_reboot_failure")
506        self.job._record_reboot_failure.expect_call(
507            'sub', 'reboot.verify_config',
508            'Number of CPUs changed after reboot (old count: 8, new count: 4)',
509            running_id=None)
510
511        # playback
512        self.assertRaises(error.JobError, self.job._check_post_reboot, "sub")
513        self.god.check_playback()
514
515
516    def test_parse_args(self):
517        test_set = {"a='foo bar baz' b='moo apt'":
518                    ["a='foo bar baz'", "b='moo apt'"],
519                    "a='foo bar baz' only=gah":
520                    ["a='foo bar baz'", "only=gah"],
521                    "a='b c d' no=argh":
522                    ["a='b c d'", "no=argh"]}
523        for t in test_set:
524            parsed_args = job.base_client_job._parse_args(t)
525            expected_args = test_set[t]
526            self.assertEqual(parsed_args, expected_args)
527
528
529    def test_run_test_timeout_parameter_is_propagated(self):
530        self.construct_job(True)
531
532        # set up stubs
533        self.god.stub_function(self.job.pkgmgr, 'get_package_name')
534        self.god.stub_function(self.job, "_runtest")
535
536        # create an unhandled error object
537        #class MyError(error.TestError):
538        #    pass
539        #real_error = MyError("this is the real error message")
540        #unhandled_error = error.UnhandledTestError(real_error)
541
542        # set up the recording
543        testname = "test"
544        outputdir = os.path.join(self.job.resultdir, testname)
545        self.job.pkgmgr.get_package_name.expect_call(
546            testname, 'test').and_return(("", testname))
547        os.path.exists.expect_call(outputdir).and_return(False)
548        timeout = 60
549        optional_fields = {}
550        optional_fields['timeout'] = timeout
551        self.job.record.expect_call("START", testname, testname,
552                                    optional_fields=optional_fields)
553        self.job._runtest.expect_call(testname, "", timeout, (), {})
554        self.job.record.expect_call("GOOD", testname, testname,
555                                    "completed successfully")
556        self.job.record.expect_call("END GOOD", testname, testname)
557        self.job.harness.run_test_complete.expect_call()
558        utils.drop_caches.expect_call()
559
560        # run and check
561        self.job.run_test(testname, timeout=timeout)
562        self.god.check_playback()
563
564
565if __name__ == "__main__":
566    unittest.main()
567