|
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/. |
|
6 |
|
7 from __future__ import with_statement |
|
8 import sys, os |
|
9 from optparse import OptionParser |
|
10 import mozprocess, mozinfo, mozlog, mozcrash |
|
11 |
|
12 log = mozlog.getLogger('gtest') |
|
13 |
|
14 class GTests(object): |
|
15 # Time (seconds) to wait for test process to complete |
|
16 TEST_PROC_TIMEOUT = 1200 |
|
17 # Time (seconds) in which process will be killed if it produces no output. |
|
18 TEST_PROC_NO_OUTPUT_TIMEOUT = 300 |
|
19 |
|
20 def run_gtest(self, prog, xre_path, symbols_path=None): |
|
21 """ |
|
22 Run a single C++ unit test program. |
|
23 |
|
24 Arguments: |
|
25 * prog: The path to the test program to run. |
|
26 * env: The environment to use for running the program. |
|
27 * symbols_path: A path to a directory containing Breakpad-formatted |
|
28 symbol files for producing stack traces on crash. |
|
29 |
|
30 Return True if the program exits with a zero status, False otherwise. |
|
31 """ |
|
32 self.xre_path = xre_path |
|
33 env = self.build_environment() |
|
34 log.info("Running gtest") |
|
35 proc = mozprocess.ProcessHandler([prog, "-unittest"], |
|
36 cwd=os.getcwd(), |
|
37 env=env) |
|
38 #TODO: After bug 811320 is fixed, don't let .run() kill the process, |
|
39 # instead use a timeout in .wait() and then kill to get a stack. |
|
40 proc.run(timeout=GTests.TEST_PROC_TIMEOUT, |
|
41 outputTimeout=GTests.TEST_PROC_NO_OUTPUT_TIMEOUT) |
|
42 proc.wait() |
|
43 if proc.timedOut: |
|
44 log.testFail("gtest | timed out after %d seconds", GTests.TEST_PROC_TIMEOUT) |
|
45 return False |
|
46 if mozcrash.check_for_crashes(os.getcwd(), symbols_path, test_name="gtest"): |
|
47 # mozcrash will output the log failure line for us. |
|
48 return False |
|
49 result = proc.proc.returncode == 0 |
|
50 if not result: |
|
51 log.testFail("gtest | test failed with return code %d", proc.proc.returncode) |
|
52 return result |
|
53 |
|
54 def build_core_environment(self, env = {}): |
|
55 """ |
|
56 Add environment variables likely to be used across all platforms, including remote systems. |
|
57 """ |
|
58 env["MOZILLA_FIVE_HOME"] = self.xre_path |
|
59 env["MOZ_XRE_DIR"] = self.xre_path |
|
60 env["XPCOM_DEBUG_BREAK"] = "stack-and-abort" |
|
61 env["MOZ_CRASHREPORTER_NO_REPORT"] = "1" |
|
62 env["MOZ_CRASHREPORTER"] = "1" |
|
63 env["MOZ_RUN_GTEST"] = "1" |
|
64 # Normally we run with GTest default output, override this to use the TBPL test format. |
|
65 env["MOZ_TBPL_PARSER"] = "1" |
|
66 return env |
|
67 |
|
68 def build_environment(self): |
|
69 """ |
|
70 Create and return a dictionary of all the appropriate env variables and values. |
|
71 On a remote system, we overload this to set different values and are missing things like os.environ and PATH. |
|
72 """ |
|
73 if not os.path.isdir(self.xre_path): |
|
74 raise Exception("xre_path does not exist: %s", self.xre_path) |
|
75 env = dict(os.environ) |
|
76 env = self.build_core_environment(env) |
|
77 pathvar = "" |
|
78 if mozinfo.os == "linux": |
|
79 pathvar = "LD_LIBRARY_PATH" |
|
80 elif mozinfo.os == "mac": |
|
81 pathvar = "DYLD_LIBRARY_PATH" |
|
82 elif mozinfo.os == "win": |
|
83 pathvar = "PATH" |
|
84 if pathvar: |
|
85 if pathvar in env: |
|
86 env[pathvar] = "%s%s%s" % (self.xre_path, os.pathsep, env[pathvar]) |
|
87 else: |
|
88 env[pathvar] = self.xre_path |
|
89 return env |
|
90 |
|
91 class gtestOptions(OptionParser): |
|
92 def __init__(self): |
|
93 OptionParser.__init__(self) |
|
94 self.add_option("--xre-path", |
|
95 action = "store", type = "string", dest = "xre_path", |
|
96 default = None, |
|
97 help = "absolute path to directory containing XRE (probably xulrunner)") |
|
98 self.add_option("--symbols-path", |
|
99 action = "store", type = "string", dest = "symbols_path", |
|
100 default = None, |
|
101 help = "absolute path to directory containing breakpad symbols, or the URL of a zip file containing symbols") |
|
102 |
|
103 def main(): |
|
104 parser = gtestOptions() |
|
105 options, args = parser.parse_args() |
|
106 if not args: |
|
107 print >>sys.stderr, """Usage: %s <binary>""" % sys.argv[0] |
|
108 sys.exit(1) |
|
109 if not options.xre_path: |
|
110 print >>sys.stderr, """Error: --xre-path is required""" |
|
111 sys.exit(1) |
|
112 prog = os.path.abspath(args[0]) |
|
113 options.xre_path = os.path.abspath(options.xre_path) |
|
114 tester = GTests() |
|
115 try: |
|
116 result = tester.run_gtest(prog, options.xre_path, options.symbols_path) |
|
117 except Exception, e: |
|
118 log.error(str(e)) |
|
119 result = False |
|
120 sys.exit(0 if result else 1) |
|
121 |
|
122 if __name__ == '__main__': |
|
123 main() |
|
124 |