Bug 860839 - Initial implementation of |mach test|; r=jhammel
authorGregory Szorc <gps@mozilla.com>
Mon, 09 Sep 2013 12:37:38 -0700
changeset 159176 8f055452c4d9eea4897dd2a9ea0ea9be58721592
parent 159175 84c2750c87366009a51a073306b2be442c0dbbb3
child 159177 bbb2f32dd205766763c9d38e8668062f7285027f
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjhammel
bugs860839
milestone26.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 860839 - Initial implementation of |mach test|; r=jhammel The future of running tests is this command. It is a unified command for running tests. Currently, it only supports running test suites from their full test suite name or TBPL abbreviation. Support will be added in the future for running individual tests or mixing and matching tests of different flavors.
build/mach_bootstrap.py
python/mozbuild/mozbuild/base.py
testing/mach_commands.py
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -61,16 +61,17 @@ MACH_MODULES = [
     'addon-sdk/mach_commands.py',
     'layout/tools/reftest/mach_commands.py',
     'python/mach_commands.py',
     'python/mach/mach/commands/commandinfo.py',
     'python/mozboot/mozboot/mach_commands.py',
     'python/mozbuild/mozbuild/config.py',
     'python/mozbuild/mozbuild/mach_commands.py',
     'python/mozbuild/mozbuild/frontend/mach_commands.py',
+    'testing/mach_commands.py',
     'testing/marionette/mach_commands.py',
     'testing/mochitest/mach_commands.py',
     'testing/xpcshell/mach_commands.py',
     'testing/talos/mach_commands.py',
     'testing/xpcshell/mach_commands.py',
     'tools/mercurial/mach_commands.py',
     'tools/mach_commands.py',
 ]
--- a/python/mozbuild/mozbuild/base.py
+++ b/python/mozbuild/mozbuild/base.py
@@ -484,16 +484,18 @@ class MachCommandBase(MozbuildObject):
     This provides a level of indirection so MozbuildObject can be refactored
     without having to change everything that inherits from it.
     """
 
     def __init__(self, context):
         MozbuildObject.__init__(self, context.topdir, context.settings,
             context.log_manager)
 
+        self._mach_context = context
+
         # Incur mozconfig processing so we have unified error handling for
         # errors. Otherwise, the exceptions could bubble back to mach's error
         # handler.
         try:
             self.mozconfig
 
         except MozconfigFindException as e:
             print(e.message)
new file mode 100644
--- /dev/null
+++ b/testing/mach_commands.py
@@ -0,0 +1,161 @@
+# 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 os
+
+from mach.decorators import (
+    CommandArgument,
+    CommandProvider,
+    Command,
+)
+
+from mozbuild.base import MachCommandBase
+
+
+HANDLE_FILE_ERROR = '''
+%s is a file.
+However, I do not yet know how to run tests from files. You'll need to run
+the mach command for the test type you are trying to run. e.g.
+$ mach xpcshell-test path/to/file
+'''.strip()
+
+HANDLE_DIR_ERROR = '''
+%s is a directory.
+However, I do not yet know how to run tests from directories. You can try
+running the mach command for the tests in that directory. e.g.
+$ mach xpcshell-test path/to/directory
+'''.strip()
+
+UNKNOWN_TEST = '''
+I don't know how to run the test: %s
+
+You need to specify a test suite name or abbreviation. It's possible I just
+haven't been told about this test suite yet. If you suspect that's the issue,
+please file a bug at https://bugzilla.mozilla.org/enter_bug.cgi?product=Testing&component=General
+and request support be added.
+'''.strip()
+
+MOCHITEST_CHUNK_BY_DIR = 4
+MOCHITEST_TOTAL_CHUNKS = 5
+
+TEST_SUITES = {
+    'crashtest': {
+        'aliases': ('C', 'Rc', 'RC', 'rc'),
+        'mach_command': 'crashtest',
+        'kwargs': {'test_file': None},
+    },
+    'crashtest-ipc': {
+        'aliases': ('Cipc', 'cipc'),
+        'mach_command': 'crashtest-ipc',
+        'kwargs': {'test_file': None},
+    },
+    'jetpack': {
+        'aliases': ('J',),
+        'mach_command': 'jetpack-test',
+        'kwargs': {},
+    },
+    'mochitest-a11y': {
+        'mach_command': 'mochitest-a11y',
+        'kwargs': {'test_file': None},
+    },
+    'mochitest-browser': {
+        'aliases': ('bc', 'BC', 'Bc'),
+        'mach_command': 'mochitest-browser',
+        'kwargs': {'test_file': None},
+    },
+    'mochitest-chrome': {
+        'mach_command': 'mochitest-chrome',
+        'kwargs': {'test_file': None},
+    },
+    'mochitest-ipcplugins': {
+        'make_target': 'mochitest-ipcplugins',
+    },
+    'mochitest-plain': {
+        'mach_command': 'mochitest-plain',
+        'kwargs': {'test_file': None},
+    },
+    'reftest': {
+        'aliases': ('RR', 'rr', 'Rr'),
+        'mach_command': 'reftest',
+        'kwargs': {'test_file': None},
+    },
+    'reftest-ipc': {
+        'aliases': ('Ripc',),
+        'mach_command': 'reftest-ipc',
+        'kwargs': {'test_file': None},
+    },
+    'xpcshell': {
+        'aliases': ('X', 'x'),
+        'mach_command': 'xpcshell-test',
+        'kwargs': {'test_file': 'all'},
+    },
+}
+
+for i in range(1, MOCHITEST_TOTAL_CHUNKS + 1):
+    TEST_SUITES['mochitest-%d' %i] = {
+        'aliases': ('M%d' % i, 'm%d' % i),
+        'mach_command': 'mochitest-plain',
+        'kwargs': {
+            'chunk_by_dir': MOCHITEST_CHUNK_BY_DIR,
+            'total_chunks': MOCHITEST_TOTAL_CHUNKS,
+            'this_chunk': i,
+            'test_file': None,
+        },
+    }
+
+TEST_HELP = '''
+Test or tests to run. Tests can be specified by test suite name or alias.
+The following test suites and aliases are supported: %s
+''' % ', '.join(sorted(TEST_SUITES))
+TEST_HELP = TEST_HELP.strip()
+
+
+@CommandProvider
+class Test(MachCommandBase):
+    @Command('test', category='testing', description='Run tests.')
+    @CommandArgument('what', default=None, nargs='*', help=TEST_HELP)
+    def test(self, what):
+        status = None
+        for entry in what:
+            status = self._run_test(entry)
+
+            if status:
+                break
+
+        return status
+
+    def _run_test(self, what):
+        suite = None
+        if what in TEST_SUITES:
+            suite = TEST_SUITES[what]
+        else:
+            for v in TEST_SUITES.values():
+                if what in v.get('aliases', []):
+                    suite = v
+                    break
+
+        if suite:
+            if 'mach_command' in suite:
+                return self._mach_context.commands.dispatch(
+                    suite['mach_command'], self._mach_context, **suite['kwargs'])
+
+            if 'make_target' in suite:
+                return self._run_make(target=suite['make_target'],
+                    pass_thru=True)
+
+            raise Exception('Do not know how to run suite. This is a logic '
+                'error in this mach command.')
+
+        if os.path.isfile(what):
+            print(HANDLE_FILE_ERROR % what)
+            return 1
+
+        if os.path.isdir(what):
+            print(HANDLE_DIR_ERROR % what)
+            return 1
+
+        print(UNKNOWN_TEST % what)
+        return 1