makes unittest running parallel. The code which does the parallel processing understands the concept of dependencies and rate limiting.  The former is a feature that's unused by this code, but I'll be sending something out soon that makes use of it

Signed-off-by: Jeremy Orlow <jorlow@google.com>



git-svn-id: http://test.kernel.org/svn/autotest/trunk@1928 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/utils/unittest_suite.py b/utils/unittest_suite.py
index dfdb7f2..6051120 100644
--- a/utils/unittest_suite.py
+++ b/utils/unittest_suite.py
@@ -1,24 +1,35 @@
 #!/usr/bin/python
 
-import os, sys, unittest
+import os, sys, unittest, optparse
 import common
+from autotest_lib.utils import parallel
+
+
+debug = False
+root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+
+parser = optparse.OptionParser()
+parser.add_option("-r", action="store", type="string", dest="start",
+                  default='',
+                  help="root directory to start running unittests")
+parser.add_option("--full", action="store_true", dest="full", default=False,
+                  help="whether to run the shortened version of the test")
 
 
 LONG_TESTS = set((
     'monitor_db_unittest.py',
     'barrier_unittest.py',
-    'migrate_unittest.py', 
+    'migrate_unittest.py',
     'frontend_unittest.py',
     ))
 
-
-root = os.path.abspath(os.path.dirname(__file__))
 modules = []
 
-def lister(short, dirname, files):
+
+def lister(full, dirname, files):
     for f in files:
         if f.endswith('_unittest.py'):
-            if short and f in LONG_TESTS:
+            if not full and f in LONG_TESTS:
                 continue
             temp = os.path.join(dirname, f).strip('.py')
             mod_name = ['autotest_lib'] + temp[len(root)+1:].split('/')
@@ -26,40 +37,51 @@
 
 
 def run_test(mod_name):
+    if not debug:
+        parallel.redirect_io()
+
+    print "Running %s" % '.'.join(mod_name)
     mod = common.setup_modules.import_module(mod_name[-1],
                                              '.'.join(mod_name[:-1]))
     test = unittest.defaultTestLoader.loadTestsFromModule(mod)
     suite = unittest.TestSuite(test)
     runner = unittest.TextTestRunner(verbosity=2)
     result = runner.run(suite)
-    return (result.errors, result.failures)
+    if result.errors or result.failures:
+        raise Exception("%s failed" % '.'.join(mod_name))
 
 
-def run_tests(start, short=False):
-    os.path.walk(start, lister, short)
+def run_tests(start, full=False):
+    os.path.walk(start, lister, full)
 
-    errors = []
+    functions = []
     for module in modules:
-        pid = os.fork()
-        if pid == 0:
-            errors, failures = run_test(module)
-            if errors or failures:
-                os._exit(1)
-            os._exit(0)
+        # Create a function that'll test a particular module.  module=module
+        # is a hack to force python to evaluate the params now.  We then
+        # rename the function to make error reporting nicer.
+        run_module = lambda module=module: run_test(module)
+        run_module.__name__ = '.'.join(module)
+        functions.append(run_module)
 
-        _, status = os.waitpid(pid, 0)
-        if status != 0:
-            errors.append('.'.join(module))
-    return errors
+    try:
+        dargs = {}
+        if debug:
+            dargs['max_simultaneous_procs'] = 1
+        pe = parallel.ParallelExecute(functions, **dargs)
+        pe.run_until_completion()
+    except parallel.ParallelError, err:
+        return err.errors
+    return []
 
 
-if __name__ == "__main__":
-    if len(sys.argv) == 2:
-        start = os.path.join(root, sys.argv[1])
-    else:
-        start = root
+def main():
+    options, args = parser.parse_args()
+    if args:
+        parser.error('Unexpected argument(s): %s' % args)
+        parser.print_help()
+        sys.exit(1)
 
-    errors = run_tests(start)
+    errors = run_tests(os.path.join(root, options.start), options.full)
     if errors:
         print "%d tests resulted in an error/failure:" % len(errors)
         for error in errors:
@@ -68,3 +90,6 @@
     else:
         print "All passed!"
         sys.exit(0)
+
+if __name__ == "__main__":
+    main()