Bug 1529000 - add a way to provide environment variables to use at startup in mochitest manifests, r=ahal.
authorFlorian Quèze <florian@queze.net>
Wed, 06 Mar 2019 19:03:51 +0000
changeset 520552 60c03f3c8c5b67e337887eaf22191c71127687dc
parent 520551 32412e6eb5511f3f551aad5be3e38ec1acbb2f3f
child 520553 62aba7d6d779e6a5e3a088b94ea0df7eef2d92bd
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersahal
bugs1529000
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1529000 - add a way to provide environment variables to use at startup in mochitest manifests, r=ahal. Differential Revision: https://phabricator.services.mozilla.com/D21726
testing/mochitest/runtests.py
testing/mozbase/mozprofile/mozprofile/cli.py
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -852,25 +852,27 @@ class MochitestDesktop(object):
         self.staged_addons = staged_addons
         self.server = None
         self.wsserver = None
         self.websocketProcessBridge = None
         self.sslTunnel = None
         self.manifest = None
         self.tests_by_manifest = defaultdict(list)
         self.prefs_by_manifest = defaultdict(set)
+        self.env_vars_by_manifest = defaultdict(set)
         self._active_tests = None
         self.currentTests = None
         self._locations = None
 
         self.marionette = None
         self.start_script = None
         self.mozLogs = None
         self.start_script_kwargs = {}
         self.extraPrefs = {}
+        self.extraEnv = {}
 
         if logger_options.get('log'):
             self.log = logger_options['log']
         else:
             commandline.log_formatters["tbpl"] = (
                 MochitestFormatter,
                 "Mochitest specific tbpl formatter")
             self.log = commandline.setup_logging("mochitest", logger_options, {"tbpl": sys.stdout})
@@ -1480,21 +1482,23 @@ toolbar#nav-bar {
                 self.log.warning(
                     'Warning: %s from manifest %s is not a valid test' %
                     (test['name'], test['manifest']))
                 continue
 
             manifest_relpath = os.path.relpath(test['manifest'], manifest_root)
             self.tests_by_manifest[manifest_relpath].append(tp)
             self.prefs_by_manifest[manifest_relpath].add(test.get('prefs'))
-
-            if 'prefs' in test and not options.runByManifest and 'disabled' not in test:
-                self.log.error("parsing {}: runByManifest mode must be enabled to "
-                               "set the `prefs` key".format(manifest_relpath))
-                sys.exit(1)
+            self.env_vars_by_manifest[manifest_relpath].add(test.get('environment'))
+
+            for key in ['prefs', 'environment']:
+                if key in test and not options.runByManifest and 'disabled' not in test:
+                    self.log.error("parsing {}: runByManifest mode must be enabled to "
+                                   "set the `{}` key".format(manifest_relpath, key))
+                    sys.exit(1)
 
             testob = {'path': tp, 'manifest': manifest_relpath}
             if 'disabled' in test:
                 testob['disabled'] = test['disabled']
             if 'expected' in test:
                 testob['expected'] = test['expected']
             if 'uses-unsafe-cpows' in test:
                 testob['uses-unsafe-cpows'] = test['uses-unsafe-cpows'] == 'true'
@@ -1513,16 +1517,23 @@ toolbar#nav-bar {
         # stash all prefs from tests in the same manifest into a set. If the
         # length of the set > 1, then we know 'prefs' didn't come from DEFAULT.
         pref_not_default = [m for m, p in self.prefs_by_manifest.iteritems() if len(p) > 1]
         if pref_not_default:
             self.log.error("The 'prefs' key must be set in the DEFAULT section of a "
                            "manifest. Fix the following manifests: {}".format(
                             '\n'.join(pref_not_default)))
             sys.exit(1)
+        # The 'environment' key needs to be set in the DEFAULT section too.
+        env_not_default = [m for m, p in self.env_vars_by_manifest.iteritems() if len(p) > 1]
+        if env_not_default:
+            self.log.error("The 'environment' key must be set in the DEFAULT section of a "
+                           "manifest. Fix the following manifests: {}".format(
+                            '\n'.join(env_not_default)))
+            sys.exit(1)
 
         def path_sort(ob1, ob2):
             path1 = ob1['path'].split('/')
             path2 = ob2['path'].split('/')
             return cmp(path1, path2)
 
         paths.sort(path_sort)
         if options.dump_tests:
@@ -1625,16 +1636,26 @@ toolbar#nav-bar {
 
         # bug 1443327: do not set MOZ_CRASHREPORTER_SHUTDOWN during browser-chrome
         # tests, since some browser-chrome tests test content process crashes;
         # also exclude non-e10s since at least one non-e10s mochitest is problematic
         if (options.flavor == 'browser' or not options.e10s) and \
            'MOZ_CRASHREPORTER_SHUTDOWN' in browserEnv:
             del browserEnv["MOZ_CRASHREPORTER_SHUTDOWN"]
 
+        try:
+            browserEnv.update(
+                dict(
+                    parse_key_value(
+                        self.extraEnv,
+                        context='environment variable in manifest')))
+        except KeyValueParseError as e:
+            self.log.error(str(e))
+            return None
+
         # These variables are necessary for correct application startup; change
         # via the commandline at your own risk.
         browserEnv["XPCOM_DEBUG_BREAK"] = "stack"
 
         # interpolate environment passed with options
         try:
             browserEnv.update(
                 dict(
@@ -2587,16 +2608,24 @@ toolbar#nav-bar {
             prefs = list(self.prefs_by_manifest[m])[0]
             self.extraPrefs = origPrefs.copy()
             if prefs:
                 prefs = prefs.strip().split()
                 self.log.info("The following extra prefs will be set:\n  {}".format(
                     '\n  '.join(prefs)))
                 self.extraPrefs.update(parse_preferences(prefs))
 
+            envVars = list(self.env_vars_by_manifest[m])[0]
+            self.extraEnv = {}
+            if envVars:
+                self.extraEnv = envVars.strip().split()
+                self.log.info(
+                    "The following extra environment variables will be set:\n  {}".format(
+                        '\n  '.join(self.extraEnv)))
+
             # If we are using --run-by-manifest, we should not use the profile path (if) provided
             # by the user, since we need to create a new directory for each run. We would face
             # problems if we use the directory provided by the user.
             tests_in_manifest = [t['path'] for t in tests if t['manifest'] == m]
             res = self.runMochitests(options, tests_in_manifest)
             result = result or res
 
             # Dump the logging buffer
--- a/testing/mozbase/mozprofile/mozprofile/cli.py
+++ b/testing/mozbase/mozprofile/mozprofile/cli.py
@@ -24,17 +24,17 @@ from .profile import Profile
 class KeyValueParseError(Exception):
     """Error when parsing strings of serialized key-values."""
 
     def __init__(self, msg, errors=()):
         self.errors = errors
         Exception.__init__(self, msg)
 
 
-def parse_key_value(strings, separator='=', context='key, value: '):
+def parse_key_value(strings, separator='=', context='key, value'):
     """Parse string-serialized key-value pairs in the form of `key = value`.
 
     Args:
         strings (list): List of strings to parse.
         separator (str): Identifier used to split the strings.
 
     Returns:
         list: A list of (<key>, <value>) tuples. Whitespace is not stripped.
@@ -42,17 +42,17 @@ def parse_key_value(strings, separator='
     Raises:
         KeyValueParseError
     """
 
     # syntax check
     missing = [string for string in strings if separator not in string]
     if missing:
         raise KeyValueParseError(
-            "Error: syntax error in %s" %
+            "Error: syntax error in %s: %s" %
             (context, ','.join(missing)), errors=missing)
     return [string.split(separator, 1) for string in strings]
 
 
 def parse_preferences(prefs, context='--setpref='):
     """Parse preferences specified on the command line.
 
     Args: