unittest_suite.py revision 656b3b8e1092a08bb60a059b09d1dbafadcbd037
1#!/usr/bin/python -u
2
3import os, sys, unittest, optparse
4import common
5from autotest_lib.utils import parallel
6from autotest_lib.client.common_lib.test_utils import unittest as custom_unittest
7
8parser = optparse.OptionParser()
9parser.add_option("-r", action="store", type="string", dest="start",
10                  default='',
11                  help="root directory to start running unittests")
12parser.add_option("--full", action="store_true", dest="full", default=False,
13                  help="whether to run the shortened version of the test")
14parser.add_option("--debug", action="store_true", dest="debug", default=False,
15                  help="run in debug mode")
16
17LONG_TESTS = set((
18    'monitor_db_unittest.py',
19    'monitor_db_functional_test.py',
20    'monitor_db_cleanup_test.py',
21    'barrier_unittest.py',
22    'migrate_unittest.py',
23    'frontend_unittest.py',
24    'client_compilation_unittest.py',
25    'csv_encoder_unittest.py',
26    'rpc_interface_unittest.py',
27    'resources_test.py',
28    'logging_manager_test.py',
29    'models_test.py',
30    'serviceHandler_unittest.py',
31    ))
32
33ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
34
35
36class TestFailure(Exception): pass
37
38
39def run_test(mod_names, options):
40    """
41    @param mod_names: A list of individual parts of the module name to import
42            and run as a test suite.
43    @param options: optparse options.
44    """
45    if not options.debug:
46        parallel.redirect_io()
47
48    print "Running %s" % '.'.join(mod_names)
49    mod = common.setup_modules.import_module(mod_names[-1],
50                                             '.'.join(mod_names[:-1]))
51    for ut_module in [unittest, custom_unittest]:
52        test = ut_module.defaultTestLoader.loadTestsFromModule(mod)
53        suite = ut_module.TestSuite(test)
54        runner = ut_module.TextTestRunner(verbosity=2)
55        result = runner.run(suite)
56        if result.errors or result.failures:
57            msg = '%s had %d failures and %d errors.'
58            msg %= '.'.join(mod_names), len(result.failures), len(result.errors)
59            raise TestFailure(msg)
60
61
62def find_and_run_tests(start, options):
63    """
64    Find and run Python unittest suites below the given directory.  Only look
65    in subdirectories of start that are actual importable Python modules.
66
67    @param start: The absolute directory to look for tests under.
68    @param options: optparse options.
69    """
70    modules = []
71
72    for dirpath, subdirs, filenames in os.walk(start):
73        # Only look in and below subdirectories that are python modules.
74        if '__init__.py' not in filenames:
75            if options.full:
76                for filename in filenames:
77                    if filename.endswith('.pyc'):
78                        os.unlink(os.path.join(dirpath, filename))
79            # Skip all subdirectories below this one, it is not a module.
80            del subdirs[:]
81            if options.debug:
82                print 'Skipping', dirpath
83            continue  # Skip this directory.
84
85        # Look for unittest files.
86        for fname in filenames:
87            if fname.endswith('_unittest.py') or fname.endswith('_test.py'):
88                if not options.full and fname in LONG_TESTS:
89                    continue
90                path_no_py = os.path.join(dirpath, fname).rstrip('.py')
91                assert path_no_py.startswith(ROOT)
92                names = path_no_py[len(ROOT)+1:].split('/')
93                modules.append(['autotest_lib'] + names)
94                if options.debug:
95                    print 'testing', path_no_py
96
97    if options.debug:
98        print 'Number of test modules found:', len(modules)
99
100    functions = {}
101    for module_names in modules:
102        # Create a function that'll test a particular module.  module=module
103        # is a hack to force python to evaluate the params now.  We then
104        # rename the function to make error reporting nicer.
105        run_module = lambda module=module_names: run_test(module, options)
106        name = '.'.join(module_names)
107        run_module.__name__ = name
108        functions[run_module] = set()
109
110    try:
111        dargs = {}
112        if options.debug:
113            dargs['max_simultaneous_procs'] = 1
114        pe = parallel.ParallelExecute(functions, **dargs)
115        pe.run_until_completion()
116    except parallel.ParallelError, err:
117        return err.errors
118    return []
119
120
121def main():
122    options, args = parser.parse_args()
123    if args:
124        parser.error('Unexpected argument(s): %s' % args)
125        parser.print_help()
126        sys.exit(1)
127
128    # Strip the arguments off the command line, so that the unit tests do not
129    # see them.
130    del sys.argv[1:]
131
132    absolute_start = os.path.join(ROOT, options.start)
133    errors = find_and_run_tests(absolute_start, options)
134    if errors:
135        print "%d tests resulted in an error/failure:" % len(errors)
136        for error in errors:
137            print "\t%s" % error
138        print "Rerun", sys.argv[0], "--debug to see the failure details."
139        sys.exit(1)
140    else:
141        print "All passed!"
142        sys.exit(0)
143
144
145if __name__ == "__main__":
146    main()
147