diff options
Diffstat (limited to 'testing/mozbase/mozrunner/mozrunner/utils.py')
-rwxr-xr-x | testing/mozbase/mozrunner/mozrunner/utils.py | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/testing/mozbase/mozrunner/mozrunner/utils.py b/testing/mozbase/mozrunner/mozrunner/utils.py new file mode 100755 index 000000000..f96c94398 --- /dev/null +++ b/testing/mozbase/mozrunner/mozrunner/utils.py @@ -0,0 +1,279 @@ +#!/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/. + +"""Utility functions for mozrunner""" + +import mozinfo +import os +import sys + +__all__ = ['findInPath', 'get_metadata_from_egg'] + + +# python package method metadata by introspection +try: + import pkg_resources + + def get_metadata_from_egg(module): + ret = {} + try: + dist = pkg_resources.get_distribution(module) + except pkg_resources.DistributionNotFound: + return {} + if dist.has_metadata("PKG-INFO"): + key = None + value = "" + for line in dist.get_metadata("PKG-INFO").splitlines(): + # see http://www.python.org/dev/peps/pep-0314/ + if key == 'Description': + # descriptions can be long + if not line or line[0].isspace(): + value += '\n' + line + continue + else: + key = key.strip() + value = value.strip() + ret[key] = value + + key, value = line.split(':', 1) + key = key.strip() + value = value.strip() + ret[key] = value + if dist.has_metadata("requires.txt"): + ret["Dependencies"] = "\n" + dist.get_metadata("requires.txt") + return ret +except ImportError: + # package resources not avaialable + def get_metadata_from_egg(module): + return {} + + +def findInPath(fileName, path=os.environ['PATH']): + """python equivalent of which; should really be in the stdlib""" + dirs = path.split(os.pathsep) + for dir in dirs: + if os.path.isfile(os.path.join(dir, fileName)): + return os.path.join(dir, fileName) + if mozinfo.isWin: + if os.path.isfile(os.path.join(dir, fileName + ".exe")): + return os.path.join(dir, fileName + ".exe") + +if __name__ == '__main__': + for i in sys.argv[1:]: + print findInPath(i) + + +def _find_marionette_in_args(*args, **kwargs): + try: + m = [a for a in args + tuple(kwargs.values()) if hasattr(a, 'session')][0] + except IndexError: + print("Can only apply decorator to function using a marionette object") + raise + return m + + +def _raw_log(): + import logging + return logging.getLogger(__name__) + + +def test_environment(xrePath, env=None, crashreporter=True, debugger=False, + dmdPath=None, lsanPath=None, log=None): + """ + populate OS environment variables for mochitest and reftests. + + Originally comes from automationutils.py. Don't use that for new code. + """ + + env = os.environ.copy() if env is None else env + log = log or _raw_log() + + assert os.path.isabs(xrePath) + + if mozinfo.isMac: + ldLibraryPath = os.path.join(os.path.dirname(xrePath), "MacOS") + else: + ldLibraryPath = xrePath + + envVar = None + dmdLibrary = None + preloadEnvVar = None + if 'toolkit' in mozinfo.info and mozinfo.info['toolkit'] == "gonk": + # Skip all of this, it's only valid for the host. + pass + elif mozinfo.isUnix: + envVar = "LD_LIBRARY_PATH" + env['MOZILLA_FIVE_HOME'] = xrePath + dmdLibrary = "libdmd.so" + preloadEnvVar = "LD_PRELOAD" + elif mozinfo.isMac: + envVar = "DYLD_LIBRARY_PATH" + dmdLibrary = "libdmd.dylib" + preloadEnvVar = "DYLD_INSERT_LIBRARIES" + elif mozinfo.isWin: + envVar = "PATH" + dmdLibrary = "dmd.dll" + preloadEnvVar = "MOZ_REPLACE_MALLOC_LIB" + if envVar: + envValue = ((env.get(envVar), str(ldLibraryPath)) + if mozinfo.isWin + else (ldLibraryPath, dmdPath, env.get(envVar))) + env[envVar] = os.path.pathsep.join([path for path in envValue if path]) + + if dmdPath and dmdLibrary and preloadEnvVar: + env[preloadEnvVar] = os.path.join(dmdPath, dmdLibrary) + + # crashreporter + env['GNOME_DISABLE_CRASH_DIALOG'] = '1' + env['XRE_NO_WINDOWS_CRASH_DIALOG'] = '1' + + if crashreporter and not debugger: + env['MOZ_CRASHREPORTER_NO_REPORT'] = '1' + env['MOZ_CRASHREPORTER'] = '1' + else: + env['MOZ_CRASHREPORTER_DISABLE'] = '1' + + # Crash on non-local network connections by default. + # MOZ_DISABLE_NONLOCAL_CONNECTIONS can be set to "0" to temporarily + # enable non-local connections for the purposes of local testing. Don't + # override the user's choice here. See bug 1049688. + env.setdefault('MOZ_DISABLE_NONLOCAL_CONNECTIONS', '1') + + # Set WebRTC logging in case it is not set yet + env.setdefault( + 'MOZ_LOG', + 'signaling:3,mtransport:4,DataChannel:4,jsep:4,MediaPipelineFactory:4' + ) + env.setdefault('R_LOG_LEVEL', '6') + env.setdefault('R_LOG_DESTINATION', 'stderr') + env.setdefault('R_LOG_VERBOSE', '1') + + # ASan specific environment stuff + asan = bool(mozinfo.info.get("asan")) + if asan and (mozinfo.isLinux or mozinfo.isMac): + try: + # Symbolizer support + llvmsym = os.path.join(xrePath, "llvm-symbolizer") + if os.path.isfile(llvmsym): + env["ASAN_SYMBOLIZER_PATH"] = llvmsym + log.info("INFO | runtests.py | ASan using symbolizer at %s" + % llvmsym) + else: + log.info("TEST-UNEXPECTED-FAIL | runtests.py | Failed to find" + " ASan symbolizer at %s" % llvmsym) + + # Returns total system memory in kilobytes. + # Works only on unix-like platforms where `free` is in the path. + totalMemory = int(os.popen("free").readlines()[1].split()[1]) + + # Only 4 GB RAM or less available? Use custom ASan options to reduce + # the amount of resources required to do the tests. Standard options + # will otherwise lead to OOM conditions on the current test slaves. + message = "INFO | runtests.py | ASan running in %s configuration" + asanOptions = [] + if totalMemory <= 1024 * 1024 * 4: + message = message % 'low-memory' + asanOptions = [ + 'quarantine_size=50331648', 'malloc_context_size=5'] + else: + message = message % 'default memory' + + if lsanPath: + log.info("LSan enabled.") + asanOptions.append('detect_leaks=1') + lsanOptions = ["exitcode=0"] + # Uncomment out the next line to report the addresses of leaked objects. + # lsanOptions.append("report_objects=1") + suppressionsFile = os.path.join( + lsanPath, 'lsan_suppressions.txt') + if os.path.exists(suppressionsFile): + log.info("LSan using suppression file " + suppressionsFile) + lsanOptions.append("suppressions=" + suppressionsFile) + else: + log.info("WARNING | runtests.py | LSan suppressions file" + " does not exist! " + suppressionsFile) + env["LSAN_OPTIONS"] = ':'.join(lsanOptions) + + if len(asanOptions): + env['ASAN_OPTIONS'] = ':'.join(asanOptions) + + except OSError as err: + log.info("Failed determine available memory, disabling ASan" + " low-memory configuration: %s" % err.strerror) + except: + log.info("Failed determine available memory, disabling ASan" + " low-memory configuration") + else: + log.info(message) + + tsan = bool(mozinfo.info.get("tsan")) + if tsan and mozinfo.isLinux: + # Symbolizer support. + llvmsym = os.path.join(xrePath, "llvm-symbolizer") + if os.path.isfile(llvmsym): + env["TSAN_OPTIONS"] = "external_symbolizer_path=%s" % llvmsym + log.info("INFO | runtests.py | TSan using symbolizer at %s" + % llvmsym) + else: + log.info("TEST-UNEXPECTED-FAIL | runtests.py | Failed to find TSan" + " symbolizer at %s" % llvmsym) + + return env + + +def get_stack_fixer_function(utilityPath, symbolsPath): + """ + Return a stack fixing function, if possible, to use on output lines. + + A stack fixing function checks if a line conforms to the output from + MozFormatCodeAddressDetails. If the line does not, the line is returned + unchanged. If the line does, an attempt is made to convert the + file+offset into something human-readable (e.g. a function name). + """ + if not mozinfo.info.get('debug'): + return None + + def import_stack_fixer_module(module_name): + sys.path.insert(0, utilityPath) + module = __import__(module_name, globals(), locals(), []) + sys.path.pop(0) + return module + + if symbolsPath and os.path.exists(symbolsPath): + # Run each line through a function in fix_stack_using_bpsyms.py (uses breakpad + # symbol files). + # This method is preferred for Tinderbox builds, since native + # symbols may have been stripped. + stack_fixer_module = import_stack_fixer_module( + 'fix_stack_using_bpsyms') + + def stack_fixer_function(line): + return stack_fixer_module.fixSymbols(line, symbolsPath) + + elif mozinfo.isMac: + # Run each line through fix_macosx_stack.py (uses atos). + # This method is preferred for developer machines, so we don't + # have to run "make buildsymbols". + stack_fixer_module = import_stack_fixer_module( + 'fix_macosx_stack') + + def stack_fixer_function(line): + return stack_fixer_module.fixSymbols(line) + + elif mozinfo.isLinux: + # Run each line through fix_linux_stack.py (uses addr2line). + # This method is preferred for developer machines, so we don't + # have to run "make buildsymbols". + stack_fixer_module = import_stack_fixer_module( + 'fix_linux_stack') + + def stack_fixer_function(line): + return stack_fixer_module.fixSymbols(line) + + else: + return None + + return stack_fixer_function |