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

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

mercurial