summaryrefslogtreecommitdiffstats
path: root/testing/mozbase/mozrunner/mozrunner/utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'testing/mozbase/mozrunner/mozrunner/utils.py')
-rwxr-xr-xtesting/mozbase/mozrunner/mozrunner/utils.py279
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