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