Bug 1300163 - Lazy load certain mach context attributes using the 'key' mechanism, r?armenzg draft
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Fri, 02 Sep 2016 14:35:14 -0400
changeset 410292 8645df300bd3877658d97d01e0c870a3793a1295
parent 410291 9d4cdd8aa6c9bdc1ffe4f03a2df46a186229e77b
child 410293 5b894f120d5b0eb4277780fae4b163d550364b96
push id28715
push userahalberstadt@mozilla.com
push dateTue, 06 Sep 2016 16:35:28 +0000
reviewersarmenzg
bugs1300163
milestone51.0a1
Bug 1300163 - Lazy load certain mach context attributes using the 'key' mechanism, r?armenzg You can set attributes on a mach context by using the 'key' argument to the context_handler. Basically, whatever gets returned by the handler when <key> is passed in, will get set (i.e cached) on the context object for fast retrieval next time. This is a way to lazy load these attributes. Previously I was setting functions like 'find_firefox()' on the context object, and then having the mach_commands call that directly. But this way is much cleaner. Now, the loaded 'mozharness_config' can be stored as an attribute on the context. Also 'find_firefox()' is now an attribute called 'firefox_bin'. MozReview-Commit-ID: 4lsKGpizfH7
layout/tools/reftest/mach_test_package_commands.py
testing/marionette/mach_test_package_commands.py
testing/mochitest/mach_test_package_commands.py
testing/tools/mach_test_package_bootstrap.py
testing/xpcshell/mach_test_package_commands.py
--- a/layout/tools/reftest/mach_test_package_commands.py
+++ b/layout/tools/reftest/mach_test_package_commands.py
@@ -9,25 +9,21 @@ from functools import partial
 
 from mach.decorators import (
     CommandProvider,
     Command,
 )
 
 
 def run_reftest(context, **kwargs):
+    kwargs['app'] = kwargs['app'] or context.firefox_bin
     kwargs['certPath'] = context.certs_dir
     kwargs['utilityPath'] = context.bin_dir
     kwargs['extraProfileFiles'].append(os.path.join(context.bin_dir, 'plugins'))
 
-    if not kwargs['app']:
-        # This could still return None in which case --appname must be used
-        # to specify the firefox binary.
-        kwargs['app'] = context.find_firefox()
-
     if not kwargs['tests']:
         kwargs['tests'] = [os.path.join('layout', 'reftests', 'reftest.list')]
 
     test_root = os.path.join(context.package_root, 'reftest', 'tests')
     normalize = partial(context.normalize_test_path, test_root)
     kwargs['tests'] = map(normalize, kwargs['tests'])
 
     from runreftest import run as run_test_harness
--- a/testing/marionette/mach_test_package_commands.py
+++ b/testing/marionette/mach_test_package_commands.py
@@ -19,19 +19,17 @@ def run_marionette(context, **kwargs):
     from marionette.runtests import (
         MarionetteTestRunner,
         MarionetteHarness
     )
     from mozlog.structured import commandline
 
 
     args = argparse.Namespace(**kwargs)
-
-    if not args.binary:
-        args.binary = context.find_firefox()
+    args.binary = args.binary or context.firefox_bin
 
     test_root = os.path.join(context.package_root, 'marionette', 'tests')
     if not args.tests:
         args.tests = [os.path.join(test_root, 'testing', 'marionette', 'harness',
                                    'marionette', 'tests', 'unit-tests.ini')]
 
     normalize = partial(context.normalize_test_path, test_root)
     args.tests = map(normalize, args.tests)
--- a/testing/mochitest/mach_test_package_commands.py
+++ b/testing/mochitest/mach_test_package_commands.py
@@ -14,27 +14,30 @@ from mach.decorators import (
 )
 
 parser = None
 
 
 def run_mochitest(context, **kwargs):
     args = Namespace(**kwargs)
     args.certPath = context.certs_dir
-    args.utilityPath = context.bin_dir
-    args.extraProfileFiles.append(os.path.join(context.bin_dir, 'plugins'))
-
-    if not args.app:
-        args.app = context.find_firefox()
 
     if args.test_paths:
         test_root = os.path.join(context.package_root, 'mochitest', 'tests')
         normalize = partial(context.normalize_test_path, test_root)
         args.test_paths = map(normalize, args.test_paths)
 
+    return run_mochitest_desktop(context, args)
+
+
+def run_mochitest_desktop(context, args):
+    args.app = args.app or context.firefox_bin
+    args.utilityPath = context.bin_dir
+    args.extraProfileFiles.append(os.path.join(context.bin_dir, 'plugins'))
+
     from runtests import run_test_harness
     return run_test_harness(parser, args)
 
 
 def setup_argument_parser():
     from mochitest_options import MochitestArgumentParser
     global parser
     parser = MochitestArgumentParser(app='generic')
--- a/testing/tools/mach_test_package_bootstrap.py
+++ b/testing/tools/mach_test_package_bootstrap.py
@@ -89,21 +89,21 @@ def ancestors(path, depth=0):
 
 
 def find_firefox(context):
     """Try to automagically find the firefox binary."""
     import mozinstall
     search_paths = []
 
     # Check for a mozharness setup
-    if context.mozharness_config:
-        with open(context.mozharness_config, 'r') as f:
-            config = json.load(f)
-        workdir = os.path.join(config['base_work_dir'], config['work_dir'])
-        search_paths.append(os.path.join(workdir, 'application'))
+    config = context.mozharness_config
+    if config and 'binary_path' in config:
+        return config['binary_path']
+    elif config:
+        search_paths.append(os.path.join(context.mozharness_workdir, 'application'))
 
     # Check for test-stage setup
     dist_bin = os.path.join(os.path.dirname(context.package_root), 'bin')
     if os.path.isdir(dist_bin):
         search_paths.append(dist_bin)
 
     for path in search_paths:
         try:
@@ -132,35 +132,43 @@ def bootstrap(test_package_root):
         print('Python 2.7 or above (but not Python 3) is required to run mach.')
         print('You are running Python', platform.python_version())
         sys.exit(1)
 
     sys.path[0:0] = [os.path.join(test_package_root, path) for path in SEARCH_PATHS]
     import mach.main
 
     def populate_context(context, key=None):
-        if key is not None:
+        if key is None:
+            context.package_root = test_package_root
+            context.bin_dir = os.path.join(test_package_root, 'bin')
+            context.certs_dir = os.path.join(test_package_root, 'certs')
+            context.module_dir = os.path.join(test_package_root, 'modules')
+            context.ancestors = ancestors
+            context.normalize_test_path = normalize_test_path
             return
 
-        context.package_root = test_package_root
-        context.bin_dir = os.path.join(test_package_root, 'bin')
-        context.certs_dir = os.path.join(test_package_root, 'certs')
-        context.modules_dir = os.path.join(test_package_root, 'modules')
+        # The values for the following 'key's will be set lazily, and cached
+        # after first being invoked.
+        if key == 'firefox_bin':
+            return find_firefox(context)
+
 
-        context.ancestors = ancestors
-        context.find_firefox = types.MethodType(find_firefox, context)
-        context.normalize_test_path = normalize_test_path
+        if key == 'mozharness_config':
+            for dir_path in ancestors(context.package_root):
+                mozharness_config = os.path.join(dir_path, 'logs', 'localconfig.json')
+                if os.path.isfile(mozharness_config):
+                    with open(mozharness_config, 'rb') as f:
+                        return json.load(f)
+            return {}
 
-        # Search for a mozharness localconfig.json
-        context.mozharness_config = None
-        for dir_path in ancestors(test_package_root):
-            mozharness_config = os.path.join(dir_path, 'logs', 'localconfig.json')
-            if os.path.isfile(mozharness_config):
-                context.mozharness_config = mozharness_config
-                break
+        if key == 'mozharness_workdir':
+            config = context.mozharness_config
+            if config:
+                return os.path.join(config['base_work_dir'], config['work_dir'])
 
     mach = mach.main.Mach(os.getcwd())
     mach.populate_context_handler = populate_context
 
     for category, meta in CATEGORIES.items():
         mach.define_category(category, meta['short'], meta['long'],
                              meta['priority'])
 
--- a/testing/xpcshell/mach_test_package_commands.py
+++ b/testing/xpcshell/mach_test_package_commands.py
@@ -16,22 +16,20 @@ from xpcshellcommandline import parser_d
 from mach.decorators import (
     CommandProvider,
     Command,
 )
 
 
 def run_xpcshell(context, **kwargs):
     args = Namespace(**kwargs)
+    args.appPath = args.appPath or os.path.dirname(context.firefox_bin)
     args.utility_path = context.bin_dir
     args.testingModulesDir = context.modules_dir
 
-    if not args.appPath:
-        args.appPath = os.path.dirname(context.find_firefox())
-
     if not args.xpcshell:
         args.xpcshell = os.path.join(args.appPath, 'xpcshell')
 
     if not args.pluginsPath:
         for path in context.ancestors(args.appPath, depth=2):
             test = os.path.join(path, 'plugins')
             if os.path.isdir(test):
                 args.pluginsPath = test