unittest_suite.py revision cd7a81a58c7a1a16105dcd9dca5e9f1680eff65b
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")
16parser.add_option("--skip-tests", dest="skip_tests",  default=[],
17                  help="A space separated list of tests to skip")
18
19
20REQUIRES_DJANGO = set((
21        'monitor_db_unittest.py',
22        'monitor_db_functional_test.py',
23        'monitor_db_cleanup_test.py',
24        'frontend_unittest.py',
25        'csv_encoder_unittest.py',
26        'rpc_interface_unittest.py',
27        'models_test.py',
28        'scheduler_models_unittest.py',
29        'metahost_scheduler_unittest.py',
30        'site_metahost_scheduler_unittest.py',
31        'rpc_utils_unittest.py',
32        'site_rpc_utils_unittest.py',
33        'execution_engine_unittest.py',
34        'service_proxy_lib_test.py',
35        ))
36
37REQUIRES_MYSQLDB = set((
38        'migrate_unittest.py',
39        'db_utils_unittest.py',
40        ))
41
42REQUIRES_GWT = set((
43        'client_compilation_unittest.py',
44        ))
45
46REQUIRES_SIMPLEJSON = set((
47        'resources_test.py',
48        'serviceHandler_unittest.py',
49        ))
50
51REQUIRES_AUTH = set ((
52    'trigger_unittest.py',
53    ))
54
55REQUIRES_HTTPLIB2 = set((
56        ))
57
58LONG_RUNTIME = set((
59    'barrier_unittest.py',
60    'logging_manager_test.py',
61    ))
62
63LONG_TESTS = (REQUIRES_DJANGO |
64              REQUIRES_MYSQLDB |
65              REQUIRES_GWT |
66              REQUIRES_SIMPLEJSON |
67              REQUIRES_HTTPLIB2 |
68              REQUIRES_AUTH |
69              LONG_RUNTIME)
70
71
72ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
73
74
75class TestFailure(Exception): pass
76
77
78def run_test(mod_names, options):
79    """
80    @param mod_names: A list of individual parts of the module name to import
81            and run as a test suite.
82    @param options: optparse options.
83    """
84    if not options.debug:
85        parallel.redirect_io()
86
87    print "Running %s" % '.'.join(mod_names)
88    mod = common.setup_modules.import_module(mod_names[-1],
89                                             '.'.join(mod_names[:-1]))
90    for ut_module in [unittest, custom_unittest]:
91        test = ut_module.defaultTestLoader.loadTestsFromModule(mod)
92        suite = ut_module.TestSuite(test)
93        runner = ut_module.TextTestRunner(verbosity=2)
94        result = runner.run(suite)
95        if result.errors or result.failures:
96            msg = '%s had %d failures and %d errors.'
97            msg %= '.'.join(mod_names), len(result.failures), len(result.errors)
98            raise TestFailure(msg)
99
100
101def find_and_run_tests(start, options):
102    """
103    Find and run Python unittest suites below the given directory.  Only look
104    in subdirectories of start that are actual importable Python modules.
105
106    @param start: The absolute directory to look for tests under.
107    @param options: optparse options.
108    """
109    modules = []
110    skip_tests = set()
111    if options.skip_tests:
112        skip_tests.update(options.skip_tests.split())
113
114    for dirpath, subdirs, filenames in os.walk(start):
115        # Only look in and below subdirectories that are python modules.
116        if '__init__.py' not in filenames:
117            if options.full:
118                for filename in filenames:
119                    if filename.endswith('.pyc'):
120                        os.unlink(os.path.join(dirpath, filename))
121            # Skip all subdirectories below this one, it is not a module.
122            del subdirs[:]
123            if options.debug:
124                print 'Skipping', dirpath
125            continue  # Skip this directory.
126
127        # Look for unittest files.
128        for fname in filenames:
129            if fname.endswith('_unittest.py') or fname.endswith('_test.py'):
130                if not options.full and fname in LONG_TESTS:
131                    continue
132                if fname in skip_tests:
133                    continue
134                path_no_py = os.path.join(dirpath, fname).rstrip('.py')
135                assert path_no_py.startswith(ROOT)
136                names = path_no_py[len(ROOT)+1:].split('/')
137                modules.append(['autotest_lib'] + names)
138                if options.debug:
139                    print 'testing', path_no_py
140
141    if options.debug:
142        print 'Number of test modules found:', len(modules)
143
144    functions = {}
145    for module_names in modules:
146        # Create a function that'll test a particular module.  module=module
147        # is a hack to force python to evaluate the params now.  We then
148        # rename the function to make error reporting nicer.
149        run_module = lambda module=module_names: run_test(module, options)
150        name = '.'.join(module_names)
151        run_module.__name__ = name
152        functions[run_module] = set()
153
154    try:
155        dargs = {}
156        if options.debug:
157            dargs['max_simultaneous_procs'] = 1
158        pe = parallel.ParallelExecute(functions, **dargs)
159        pe.run_until_completion()
160    except parallel.ParallelError, err:
161        return err.errors
162    return []
163
164
165def main():
166    options, args = parser.parse_args()
167    if args:
168        parser.error('Unexpected argument(s): %s' % args)
169        parser.print_help()
170        sys.exit(1)
171
172    # Strip the arguments off the command line, so that the unit tests do not
173    # see them.
174    del sys.argv[1:]
175
176    absolute_start = os.path.join(ROOT, options.start)
177    errors = find_and_run_tests(absolute_start, options)
178    if errors:
179        print "%d tests resulted in an error/failure:" % len(errors)
180        for error in errors:
181            print "\t%s" % error
182        print "Rerun", sys.argv[0], "--debug to see the failure details."
183        sys.exit(1)
184    else:
185        print "All passed!"
186        sys.exit(0)
187
188
189if __name__ == "__main__":
190    main()
191