summaryrefslogtreecommitdiffstats
path: root/testing/mozbase/mozrunner/mozrunner/utils.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xtesting/mozbase/mozrunner/mozrunner/utils.py299
1 files changed, 299 insertions, 0 deletions
diff --git a/testing/mozbase/mozrunner/mozrunner/utils.py b/testing/mozbase/mozrunner/mozrunner/utils.py
new file mode 100755
index 0000000000..46c20c1997
--- /dev/null
+++ b/testing/mozbase/mozrunner/mozrunner/utils.py
@@ -0,0 +1,299 @@
+#!/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 os
+import sys
+
+import mozinfo
+
+__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, useLSan=False, 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
+ if mozinfo.isUnix:
+ envVar = "LD_LIBRARY_PATH"
+ elif mozinfo.isMac:
+ envVar = "DYLD_LIBRARY_PATH"
+ elif mozinfo.isWin:
+ envVar = "PATH"
+ if envVar:
+ envValue = (
+ (env.get(envVar), str(ldLibraryPath))
+ if mozinfo.isWin
+ else (ldLibraryPath, env.get(envVar))
+ )
+ env[envVar] = os.path.pathsep.join([path for path in envValue if path])
+
+ # Allow non-packaged builds to access symlinked modules in the source dir
+ env["MOZ_DEVELOPER_REPO_DIR"] = mozinfo.info.get("topsrcdir")
+ env["MOZ_DEVELOPER_OBJ_DIR"] = mozinfo.info.get("topobjdir")
+
+ # 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"
+ env["MOZ_CRASHREPORTER_SHUTDOWN"] = "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")
+ env.setdefault("R_LOG_LEVEL", "6")
+ env.setdefault("R_LOG_DESTINATION", "stderr")
+ env.setdefault("R_LOG_VERBOSE", "1")
+
+ # Ask NSS to use lower-security password encryption. See Bug 1594559
+ env.setdefault("NSS_MAX_MP_PBE_ITERATION_COUNT", "10")
+
+ # ASan specific environment stuff
+ if "ASAN_SYMBOLIZER_PATH" in env and os.path.isfile(env["ASAN_SYMBOLIZER_PATH"]):
+ llvmsym = env["ASAN_SYMBOLIZER_PATH"]
+ else:
+ if mozinfo.isMac:
+ llvmSymbolizerDir = ldLibraryPath
+ else:
+ llvmSymbolizerDir = xrePath
+ llvmsym = os.path.join(
+ llvmSymbolizerDir, "llvm-symbolizer" + mozinfo.info["bin_suffix"]
+ )
+ asan = bool(mozinfo.info.get("asan"))
+ if asan:
+ try:
+ # Symbolizer support
+ if os.path.isfile(llvmsym):
+ env["ASAN_SYMBOLIZER_PATH"] = llvmsym
+ log.info("INFO | runtests.py | ASan using symbolizer at %s" % llvmsym)
+ else:
+ log.error(
+ "TEST-UNEXPECTED-FAIL | runtests.py | Failed to find"
+ " ASan symbolizer at %s" % llvmsym
+ )
+
+ # Returns total system memory in kilobytes.
+ if mozinfo.isWin:
+ # pylint --py3k W1619
+ totalMemory = (
+ int(
+ os.popen(
+ "wmic computersystem get TotalPhysicalMemory"
+ ).readlines()[1]
+ )
+ / 1024
+ )
+ elif mozinfo.isMac:
+ # pylint --py3k W1619
+ totalMemory = (
+ int(os.popen("sysctl hw.memsize").readlines()[0].split()[1]) / 1024
+ )
+ else:
+ 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 machines.
+ 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 useLSan:
+ 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")
+ 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 Exception:
+ 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.
+ 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.error(
+ "TEST-UNEXPECTED-FAIL | runtests.py | Failed to find TSan"
+ " symbolizer at %s" % llvmsym
+ )
+
+ ubsan = bool(mozinfo.info.get("ubsan"))
+ if ubsan and (mozinfo.isLinux or mozinfo.isMac):
+ log.info("UBSan enabled.")
+
+ return env
+
+
+def get_stack_fixer_function(utilityPath, symbolsPath, hideErrors=False):
+ """
+ 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
+
+ if os.getenv("MOZ_DISABLE_STACK_FIX", 0):
+ print(
+ "WARNING: No stack-fixing will occur because MOZ_DISABLE_STACK_FIX is set"
+ )
+ 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 fix_stacks.py, using breakpad symbol files.
+ # This method is preferred for automation, since native symbols may
+ # have been stripped.
+ stack_fixer_module = import_stack_fixer_module("fix_stacks")
+
+ def stack_fixer_function(line):
+ return stack_fixer_module.fixSymbols(
+ line,
+ slowWarning=True,
+ breakpadSymsDir=symbolsPath,
+ hide_errors=hideErrors,
+ )
+
+ elif mozinfo.isLinux or mozinfo.isMac or mozinfo.isWin:
+ # Run each line through fix_stacks.py. This method is preferred for
+ # developer machines, so we don't have to run "mach buildsymbols".
+ stack_fixer_module = import_stack_fixer_module("fix_stacks")
+
+ def stack_fixer_function(line):
+ return stack_fixer_module.fixSymbols(
+ line, slowWarning=True, hide_errors=hideErrors
+ )
+
+ else:
+ return None
+
+ return stack_fixer_function