michael@0: #!/usr/bin/env python michael@0: # michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: from __future__ import with_statement michael@0: import sys, os michael@0: from optparse import OptionParser michael@0: import mozprocess, mozinfo, mozlog, mozcrash michael@0: michael@0: log = mozlog.getLogger('gtest') michael@0: michael@0: class GTests(object): michael@0: # Time (seconds) to wait for test process to complete michael@0: TEST_PROC_TIMEOUT = 1200 michael@0: # Time (seconds) in which process will be killed if it produces no output. michael@0: TEST_PROC_NO_OUTPUT_TIMEOUT = 300 michael@0: michael@0: def run_gtest(self, prog, xre_path, symbols_path=None): michael@0: """ michael@0: Run a single C++ unit test program. michael@0: michael@0: Arguments: michael@0: * prog: The path to the test program to run. michael@0: * env: The environment to use for running the program. michael@0: * symbols_path: A path to a directory containing Breakpad-formatted michael@0: symbol files for producing stack traces on crash. michael@0: michael@0: Return True if the program exits with a zero status, False otherwise. michael@0: """ michael@0: self.xre_path = xre_path michael@0: env = self.build_environment() michael@0: log.info("Running gtest") michael@0: proc = mozprocess.ProcessHandler([prog, "-unittest"], michael@0: cwd=os.getcwd(), michael@0: env=env) michael@0: #TODO: After bug 811320 is fixed, don't let .run() kill the process, michael@0: # instead use a timeout in .wait() and then kill to get a stack. michael@0: proc.run(timeout=GTests.TEST_PROC_TIMEOUT, michael@0: outputTimeout=GTests.TEST_PROC_NO_OUTPUT_TIMEOUT) michael@0: proc.wait() michael@0: if proc.timedOut: michael@0: log.testFail("gtest | timed out after %d seconds", GTests.TEST_PROC_TIMEOUT) michael@0: return False michael@0: if mozcrash.check_for_crashes(os.getcwd(), symbols_path, test_name="gtest"): michael@0: # mozcrash will output the log failure line for us. michael@0: return False michael@0: result = proc.proc.returncode == 0 michael@0: if not result: michael@0: log.testFail("gtest | test failed with return code %d", proc.proc.returncode) michael@0: return result michael@0: michael@0: def build_core_environment(self, env = {}): michael@0: """ michael@0: Add environment variables likely to be used across all platforms, including remote systems. michael@0: """ michael@0: env["MOZILLA_FIVE_HOME"] = self.xre_path michael@0: env["MOZ_XRE_DIR"] = self.xre_path michael@0: env["XPCOM_DEBUG_BREAK"] = "stack-and-abort" michael@0: env["MOZ_CRASHREPORTER_NO_REPORT"] = "1" michael@0: env["MOZ_CRASHREPORTER"] = "1" michael@0: env["MOZ_RUN_GTEST"] = "1" michael@0: # Normally we run with GTest default output, override this to use the TBPL test format. michael@0: env["MOZ_TBPL_PARSER"] = "1" michael@0: return env michael@0: michael@0: def build_environment(self): michael@0: """ michael@0: Create and return a dictionary of all the appropriate env variables and values. michael@0: On a remote system, we overload this to set different values and are missing things like os.environ and PATH. michael@0: """ michael@0: if not os.path.isdir(self.xre_path): michael@0: raise Exception("xre_path does not exist: %s", self.xre_path) michael@0: env = dict(os.environ) michael@0: env = self.build_core_environment(env) michael@0: pathvar = "" michael@0: if mozinfo.os == "linux": michael@0: pathvar = "LD_LIBRARY_PATH" michael@0: elif mozinfo.os == "mac": michael@0: pathvar = "DYLD_LIBRARY_PATH" michael@0: elif mozinfo.os == "win": michael@0: pathvar = "PATH" michael@0: if pathvar: michael@0: if pathvar in env: michael@0: env[pathvar] = "%s%s%s" % (self.xre_path, os.pathsep, env[pathvar]) michael@0: else: michael@0: env[pathvar] = self.xre_path michael@0: return env michael@0: michael@0: class gtestOptions(OptionParser): michael@0: def __init__(self): michael@0: OptionParser.__init__(self) michael@0: self.add_option("--xre-path", michael@0: action = "store", type = "string", dest = "xre_path", michael@0: default = None, michael@0: help = "absolute path to directory containing XRE (probably xulrunner)") michael@0: self.add_option("--symbols-path", michael@0: action = "store", type = "string", dest = "symbols_path", michael@0: default = None, michael@0: help = "absolute path to directory containing breakpad symbols, or the URL of a zip file containing symbols") michael@0: michael@0: def main(): michael@0: parser = gtestOptions() michael@0: options, args = parser.parse_args() michael@0: if not args: michael@0: print >>sys.stderr, """Usage: %s """ % sys.argv[0] michael@0: sys.exit(1) michael@0: if not options.xre_path: michael@0: print >>sys.stderr, """Error: --xre-path is required""" michael@0: sys.exit(1) michael@0: prog = os.path.abspath(args[0]) michael@0: options.xre_path = os.path.abspath(options.xre_path) michael@0: tester = GTests() michael@0: try: michael@0: result = tester.run_gtest(prog, options.xre_path, options.symbols_path) michael@0: except Exception, e: michael@0: log.error(str(e)) michael@0: result = False michael@0: sys.exit(0 if result else 1) michael@0: michael@0: if __name__ == '__main__': michael@0: main() michael@0: