Bug 1278890 - Search for a firefox binary in the test package mach bootstrap, r?armenzg draft
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Wed, 13 Jul 2016 10:50:13 -0400
changeset 387190 e5c7ced3edb959cae4a3413ace1ed2ab8eda9578
parent 386893 04821a70c739a00d12e12df651c0989441e22728
child 387191 a85edf771af15a4429e4faf36be36ca6bd03d9f6
push id22912
push userahalberstadt@mozilla.com
push dateWed, 13 Jul 2016 15:23:47 +0000
reviewersarmenzg
bugs1278890
milestone50.0a1
Bug 1278890 - Search for a firefox binary in the test package mach bootstrap, r?armenzg When running test harnesses from the test package, the firefox binary will live somewhere outside of the mach context. There are two common situations where a developer might be running from a test package: 1) From a mozharness context 2) From an objdir context This patch will attempt to detect either of those situations and automagically find the firefox binary. MozReview-Commit-ID: fBAbfuG5XQ
testing/tools/mach_test_package_bootstrap.py
--- a/testing/tools/mach_test_package_bootstrap.py
+++ b/testing/tools/mach_test_package_bootstrap.py
@@ -1,41 +1,44 @@
 # 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/.
 
 from __future__ import print_function, unicode_literals
 
+import json
 import os
 import platform
 import sys
+import types
 
 
 SEARCH_PATHS = [
     'marionette',
     'marionette/marionette/runner/mixins/browsermob-proxy-py',
     'marionette/client',
     'mochitest',
+    'mozbase/manifestparser',
     'mozbase/mozcrash',
     'mozbase/mozdebug',
     'mozbase/mozdevice',
     'mozbase/mozfile',
     'mozbase/mozhttpd',
+    'mozbase/mozinfo',
+    'mozbase/mozinstall',
     'mozbase/mozleak',
     'mozbase/mozlog',
     'mozbase/moznetwork',
     'mozbase/mozprocess',
     'mozbase/mozprofile',
     'mozbase/mozrunner',
+    'mozbase/mozscreenshot',
     'mozbase/mozsystemmonitor',
-    'mozbase/mozinfo',
-    'mozbase/mozscreenshot',
     'mozbase/moztest',
     'mozbase/mozversion',
-    'mozbase/manifestparser',
     'tools/mach',
     'tools/wptserve',
 ]
 
 # Individual files providing mach commands.
 MACH_MODULES = [
     'mochitest/mach_test_package_commands.py',
     'tools/mach/mach/commands/commandinfo.py',
@@ -55,22 +58,60 @@ CATEGORIES = {
     },
     'misc': {
         'short': 'Potpourri',
         'long': 'Potent potables and assorted snacks.',
         'priority': 10,
     },
     'disabled': {
         'short': 'Disabled',
-        'long': 'The disabled commands are hidden by default. Use -v to display them. These commands are unavailable for your current context, run "mach <command>" to see why.',
+        'long': 'The disabled commands are hidden by default. Use -v to display them. '
+                'These commands are unavailable for your current context, '
+                'run "mach <command>" to see why.',
         'priority': 0,
     }
 }
 
 
+def ancestors(path, depth=0):
+    """Emit the parent directories of a path."""
+    count = 1
+    while path and count != depth:
+        yield path
+        newpath = os.path.dirname(path)
+        if newpath == path:
+            break
+        path = newpath
+        count += 1
+
+
+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'))
+
+    # 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:
+            return mozinstall.get_binary(path, 'firefox')
+        except mozinstall.InvalidBinary:
+            continue
+
+
 def bootstrap(test_package_root):
     test_package_root = os.path.abspath(test_package_root)
 
     # Ensure we are running Python 2.7+. We put this check here so we generate a
     # user-friendly error message rather than a cryptic stack trace on module
     # import.
     if sys.version_info[0] != 2 or sys.version_info[1] < 7:
         print('Python 2.7 or above (but not Python 3) is required to run mach.')
@@ -80,20 +121,31 @@ def bootstrap(test_package_root):
     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:
             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.bin_dir = os.path.join(test_package_root, 'bin')
         context.modules_dir = os.path.join(test_package_root, 'modules')
 
+        context.ancestors = ancestors
+        context.find_firefox = types.MethodType(find_firefox, context)
+
+        # 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
+
     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'])
 
     for path in MACH_MODULES: