DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (5350524bb654)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
#!/usr/bin/env python
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

from __future__ import with_statement

from optparse import OptionParser
import os
import sys

import mozcrash
import mozinfo
import mozlog
import mozprocess
from mozrunner.utils import get_stack_fixer_function

log = mozlog.unstructured.getLogger('gtest')

class GTests(object):
    # Time (seconds) to wait for test process to complete
    TEST_PROC_TIMEOUT = 1200
    # Time (seconds) in which process will be killed if it produces no output.
    TEST_PROC_NO_OUTPUT_TIMEOUT = 300

    def run_gtest(self, prog, xre_path, cwd, symbols_path=None,
                  utility_path=None):
        """
        Run a single C++ unit test program.

        Arguments:
        * prog: The path to the test program to run.
        * env: The environment to use for running the program.
        * cwd: The directory to run tests from (support files will be found
               in this direcotry).
        * symbols_path: A path to a directory containing Breakpad-formatted
                        symbol files for producing stack traces on crash.
        * utility_path: A path to a directory containing utility programs.
                        currently used to locate a stack fixer to provide
                        symbols symbols for assertion stacks.

        Return True if the program exits with a zero status, False otherwise.
        """
        self.xre_path = xre_path
        env = self.build_environment()
        log.info("Running gtest")

        if cwd and not os.path.isdir(cwd):
            os.makedirs(cwd)

        stream_output = mozprocess.StreamOutput(sys.stdout)
        process_output = stream_output
        if utility_path:
            stack_fixer = get_stack_fixer_function(utility_path, symbols_path)
            if stack_fixer:
                process_output = lambda line: stream_output(stack_fixer(line))


        proc = mozprocess.ProcessHandler([prog, "-unittest"],
                                         cwd=cwd,
                                         env=env,
                                         processOutputLine=process_output)
        #TODO: After bug 811320 is fixed, don't let .run() kill the process,
        # instead use a timeout in .wait() and then kill to get a stack.
        proc.run(timeout=GTests.TEST_PROC_TIMEOUT,
                 outputTimeout=GTests.TEST_PROC_NO_OUTPUT_TIMEOUT)
        proc.wait()
        if proc.timedOut:
            log.testFail("gtest | timed out after %d seconds", GTests.TEST_PROC_TIMEOUT)
            return False
        if mozcrash.check_for_crashes(cwd, symbols_path, test_name="gtest"):
            # mozcrash will output the log failure line for us.
            return False
        result = proc.proc.returncode == 0
        if not result:
            log.testFail("gtest | test failed with return code %d", proc.proc.returncode)
        return result

    def build_core_environment(self, env = {}):
        """
        Add environment variables likely to be used across all platforms, including remote systems.
        """
        env["MOZILLA_FIVE_HOME"] = self.xre_path
        env["MOZ_XRE_DIR"] = self.xre_path
        env["MOZ_GMP_PATH"] = os.pathsep.join(
            os.path.join(self.xre_path, p, "1.0")
            for p in ('gmp-fake', 'gmp-fakeopenh264')
        )
        env["XPCOM_DEBUG_BREAK"] = "stack-and-abort"
        env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
        env["MOZ_CRASHREPORTER"] = "1"
        env["MOZ_RUN_GTEST"] = "1"
        # Normally we run with GTest default output, override this to use the TBPL test format.
        env["MOZ_TBPL_PARSER"] = "1"

        if not mozinfo.has_sandbox:
          # Bug 1082193 - This is horrible. Our linux build boxes run CentOS 6,
          # which is too old to support sandboxing. Disable sandbox for gtests
          # on machines which don't support sandboxing until they can be
          # upgraded, or gtests are run on test machines instead.
          env["MOZ_DISABLE_GMP_SANDBOX"] = "1"

        return env

    def build_environment(self):
        """
        Create and return a dictionary of all the appropriate env variables and values.
        On a remote system, we overload this to set different values and are missing things like os.environ and PATH.
        """
        if not os.path.isdir(self.xre_path):
            raise Exception("xre_path does not exist: %s", self.xre_path)
        env = dict(os.environ)
        env = self.build_core_environment(env)
        pathvar = ""
        if mozinfo.os == "linux":
            pathvar = "LD_LIBRARY_PATH"
        elif mozinfo.os == "mac":
            pathvar = "DYLD_LIBRARY_PATH"
        elif mozinfo.os == "win":
            pathvar = "PATH"
        if pathvar:
            if pathvar in env:
                env[pathvar] = "%s%s%s" % (self.xre_path, os.pathsep, env[pathvar])
            else:
                env[pathvar] = self.xre_path

        # ASan specific environment stuff
        if mozinfo.info["asan"]:
            # Symbolizer support
            llvmsym = os.path.join(self.xre_path, "llvm-symbolizer")
            if os.path.isfile(llvmsym):
                env["ASAN_SYMBOLIZER_PATH"] = llvmsym
                log.info("gtest | ASan using symbolizer at %s", llvmsym)
            else:
                # This should be |testFail| instead of |info|. See bug 1050891.
                log.info("gtest | Failed to find ASan symbolizer at %s", llvmsym)

        return env

class gtestOptions(OptionParser):
    def __init__(self):
        OptionParser.__init__(self)
        self.add_option("--cwd",
                        dest="cwd",
                        default=os.getcwd(),
                        help="absolute path to directory from which to run the binary")
        self.add_option("--xre-path",
                        dest="xre_path",
                        default=None,
                        help="absolute path to directory containing XRE (probably xulrunner)")
        self.add_option("--symbols-path",
                        dest="symbols_path",
                        default=None,
                        help="absolute path to directory containing breakpad symbols, or the URL of a zip file containing symbols")
        self.add_option("--utility-path",
                        dest="utility_path",
                        default=None,
                        help="path to a directory containing utility program binaries")

def update_mozinfo():
    """walk up directories to find mozinfo.json update the info"""
    path = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
    dirs = set()
    while path != os.path.expanduser('~'):
        if path in dirs:
            break
        dirs.add(path)
        path = os.path.split(path)[0]
    mozinfo.find_and_update_from_json(*dirs)

def main():
    parser = gtestOptions()
    options, args = parser.parse_args()
    if not args:
        print >>sys.stderr, """Usage: %s <binary>""" % sys.argv[0]
        sys.exit(1)
    if not options.xre_path:
        print >>sys.stderr, """Error: --xre-path is required"""
        sys.exit(1)
    if not options.utility_path:
        print >>sys.stderr, """Warning: --utility-path is required to process assertion stacks"""

    update_mozinfo()
    prog = os.path.abspath(args[0])
    options.xre_path = os.path.abspath(options.xre_path)
    tester = GTests()
    try:
        result = tester.run_gtest(prog, options.xre_path,
                                  options.cwd,
                                  symbols_path=options.symbols_path,
                                  utility_path=options.utility_path)
    except Exception, e:
        log.error(str(e))
        result = False
    sys.exit(0 if result else 1)

if __name__ == '__main__':
    main()