testing/runcppunittests.py

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     1 #!/usr/bin/env python
     2 #
     3 # This Source Code Form is subject to the terms of the Mozilla Public
     4 # License, v. 2.0. If a copy of the MPL was not distributed with this
     5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
     7 from __future__ import with_statement
     8 import sys, os, tempfile, shutil
     9 from optparse import OptionParser
    10 import mozprocess, mozinfo, mozlog, mozcrash, mozfile
    11 from contextlib import contextmanager
    13 log = mozlog.getLogger('cppunittests')
    15 class CPPUnitTests(object):
    16     # Time (seconds) to wait for test process to complete
    17     TEST_PROC_TIMEOUT = 900
    18     # Time (seconds) in which process will be killed if it produces no output.
    19     TEST_PROC_NO_OUTPUT_TIMEOUT = 300
    21     def run_one_test(self, prog, env, symbols_path=None):
    22         """
    23         Run a single C++ unit test program.
    25         Arguments:
    26         * prog: The path to the test program to run.
    27         * env: The environment to use for running the program.
    28         * symbols_path: A path to a directory containing Breakpad-formatted
    29                         symbol files for producing stack traces on crash.
    31         Return True if the program exits with a zero status, False otherwise.
    32         """
    33         basename = os.path.basename(prog)
    34         log.info("Running test %s", basename)
    35         with mozfile.TemporaryDirectory() as tempdir:
    36             proc = mozprocess.ProcessHandler([prog],
    37                                              cwd=tempdir,
    38                                              env=env)
    39             #TODO: After bug 811320 is fixed, don't let .run() kill the process,
    40             # instead use a timeout in .wait() and then kill to get a stack.
    41             proc.run(timeout=CPPUnitTests.TEST_PROC_TIMEOUT,
    42                      outputTimeout=CPPUnitTests.TEST_PROC_NO_OUTPUT_TIMEOUT)
    43             proc.wait()
    44             if proc.timedOut:
    45                 log.testFail("%s | timed out after %d seconds",
    46                              basename, CPPUnitTests.TEST_PROC_TIMEOUT)
    47                 return False
    48             if mozcrash.check_for_crashes(tempdir, symbols_path,
    49                                           test_name=basename):
    50                 log.testFail("%s | test crashed", basename)
    51                 return False
    52             result = proc.proc.returncode == 0
    53             if not result:
    54                 log.testFail("%s | test failed with return code %d",
    55                              basename, proc.proc.returncode)
    56             return result
    58     def build_core_environment(self, env = {}):
    59         """
    60         Add environment variables likely to be used across all platforms, including remote systems.
    61         """
    62         env["MOZILLA_FIVE_HOME"] = self.xre_path
    63         env["MOZ_XRE_DIR"] = self.xre_path
    64         #TODO: switch this to just abort once all C++ unit tests have
    65         # been fixed to enable crash reporting
    66         env["XPCOM_DEBUG_BREAK"] = "stack-and-abort"
    67         env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
    68         env["MOZ_CRASHREPORTER"] = "1"
    69         return env
    71     def build_environment(self):
    72         """
    73         Create and return a dictionary of all the appropriate env variables and values.
    74         On a remote system, we overload this to set different values and are missing things like os.environ and PATH.
    75         """
    76         if not os.path.isdir(self.xre_path):
    77             raise Exception("xre_path does not exist: %s", self.xre_path)
    78         env = dict(os.environ)
    79         env = self.build_core_environment(env)
    80         pathvar = ""
    81         if mozinfo.os == "linux":
    82             pathvar = "LD_LIBRARY_PATH"
    83         elif mozinfo.os == "mac":
    84             pathvar = "DYLD_LIBRARY_PATH"
    85         elif mozinfo.os == "win":
    86             pathvar = "PATH"
    87         if pathvar:
    88             if pathvar in env:
    89                 env[pathvar] = "%s%s%s" % (self.xre_path, os.pathsep, env[pathvar])
    90             else:
    91                 env[pathvar] = self.xre_path
    93         # Use llvm-symbolizer for ASan if available/required
    94         llvmsym = os.path.join(self.xre_path, "llvm-symbolizer")
    95         if os.path.isfile(llvmsym):
    96           env["ASAN_SYMBOLIZER_PATH"] = llvmsym
    98         return env
   100     def run_tests(self, programs, xre_path, symbols_path=None):
   101         """
   102         Run a set of C++ unit test programs.
   104         Arguments:
   105         * programs: An iterable containing paths to test programs.
   106         * xre_path: A path to a directory containing a XUL Runtime Environment.
   107         * symbols_path: A path to a directory containing Breakpad-formatted
   108                         symbol files for producing stack traces on crash.
   110         Returns True if all test programs exited with a zero status, False
   111         otherwise.
   112         """
   113         self.xre_path = xre_path
   114         env = self.build_environment()
   115         pass_count = 0
   116         fail_count = 0
   117         for prog in programs:
   118             single_result = self.run_one_test(prog, env, symbols_path)
   119             if single_result:
   120                 pass_count += 1
   121             else:
   122                 fail_count += 1
   124         log.info("Result summary:")
   125         log.info("Passed: %d" % pass_count)
   126         log.info("Failed: %d" % fail_count)
   127         return fail_count == 0
   129 class CPPUnittestOptions(OptionParser):
   130     def __init__(self):
   131         OptionParser.__init__(self)
   132         self.add_option("--xre-path",
   133                         action = "store", type = "string", dest = "xre_path",
   134                         default = None,
   135                         help = "absolute path to directory containing XRE (probably xulrunner)")
   136         self.add_option("--symbols-path",
   137                         action = "store", type = "string", dest = "symbols_path",
   138                         default = None,
   139                         help = "absolute path to directory containing breakpad symbols, or the URL of a zip file containing symbols")
   140         self.add_option("--skip-manifest",
   141                         action = "store", type = "string", dest = "manifest_file",
   142                         default = None,
   143                         help = "absolute path to a manifest file")
   145 def extract_unittests_from_args(args, manifest_file):
   146     """Extract unittests from args, expanding directories as needed"""
   147     progs = []
   149     # Known files commonly packaged with the cppunittests that are not tests
   150     skipped_progs = set(['.mkdir.done', 'remotecppunittests.py', 'runcppunittests.py', 'runcppunittests.pyc'])
   152     if manifest_file:
   153         skipped_progs.add(os.path.basename(manifest_file))
   154         with open(manifest_file) as f:
   155             for line in f:
   156                 # strip out comment, if any
   157                 prog = line.split('#')[0]
   158                 if prog:
   159                     skipped_progs.add(prog.strip())
   161     for p in args:
   162         if os.path.isdir(p):
   163             progs.extend([os.path.abspath(os.path.join(p, x)) for x in os.listdir(p) if not x in skipped_progs])
   164         elif p not in skipped_progs:
   165             progs.append(os.path.abspath(p))
   167     return progs
   169 def main():
   170     parser = CPPUnittestOptions()
   171     options, args = parser.parse_args()
   172     if not args:
   173         print >>sys.stderr, """Usage: %s <test binary> [<test binary>...]""" % sys.argv[0]
   174         sys.exit(1)
   175     if not options.xre_path:
   176         print >>sys.stderr, """Error: --xre-path is required"""
   177         sys.exit(1)
   179     progs = extract_unittests_from_args(args, options.manifest_file)
   180     options.xre_path = os.path.abspath(options.xre_path)
   181     tester = CPPUnitTests()
   182     try:
   183         result = tester.run_tests(progs, options.xre_path, options.symbols_path)
   184     except Exception, e:
   185         log.error(str(e))
   186         result = False
   187     sys.exit(0 if result else 1)
   189 if __name__ == '__main__':
   190     main()

mercurial