bug 1255479 - make `mach python-tests` use TestResolver for discovery, make `mach test` work for python tests. r=nalexander
authorTed Mielczarek <ted@mielczarek.org>
Thu, 10 Mar 2016 12:30:10 -0500
changeset 288764 a03f77804981532999e4d3d41795d8bc65797059
parent 288763 879ea25d7775a6b99154d9fe2babbe50f1ae5758
child 288765 444b87fe454b724c35b04a101ea952d9910f53c1
push id30089
push userkwierso@gmail.com
push dateWed, 16 Mar 2016 00:26:08 +0000
treeherdermozilla-central@7773387a9a2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnalexander
bugs1255479
milestone48.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 1255479 - make `mach python-tests` use TestResolver for discovery, make `mach test` work for python tests. r=nalexander MozReview-Commit-ID: CK2Vh6gdnb0
build/compare-mozconfig/compare-mozconfigs-wrapper.py
python/mach_commands.py
python/moz.build
testing/mach_commands.py
testing/xpcshell/selftest.py
toolkit/crashreporter/tools/unit-symbolstore.py
--- a/build/compare-mozconfig/compare-mozconfigs-wrapper.py
+++ b/build/compare-mozconfig/compare-mozconfigs-wrapper.py
@@ -1,63 +1,68 @@
 #!/usr/bin/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/.
 
 from __future__ import unicode_literals
 
+import logging
+import mozunit
 import subprocess
 import sys
+import unittest
+
 from os import path
 from buildconfig import substs
 
-import logging
 log = logging.getLogger(__name__)
 
 def determine_platform():
     platform_mapping = {'WINNT': {'x86_64': 'win64',
                                   'i686': 'win32'},
                         'Darwin': {'x86_64': 'macosx-universal',
                                    'i386':'macosx-universal'},
                         'Linux': {'x86_64': 'linux64',
                                   'i686': 'linux32'}}
 
     os_type = substs['OS_TARGET']
     cpu_type = substs['TARGET_CPU']
     return platform_mapping.get(os_type, {}).get(cpu_type, None)
 
-def main():
-    """ A wrapper script that calls compare-mozconfig.py
-    based on the platform that the machine is building for"""
-    platform = determine_platform()
 
-    if platform is not None:
-        python_exe = substs['PYTHON']
-        topsrcdir = substs['top_srcdir']
+class TestCompareMozconfigs(unittest.TestCase):
+    def test_compare_mozconfigs(self):
+        """ A wrapper script that calls compare-mozconfig.py
+        based on the platform that the machine is building for"""
+        platform = determine_platform()
 
-        # construct paths and args for compare-mozconfig
-        browser_dir = path.join(topsrcdir, 'browser')
-        script_path = path.join(topsrcdir, 'build/compare-mozconfig/compare-mozconfigs.py')
-        whitelist_path = path.join(browser_dir, 'config/mozconfigs/whitelist')
-        beta_mozconfig_path = path.join(browser_dir, 'config/mozconfigs', platform, 'beta')
-        release_mozconfig_path = path.join(browser_dir, 'config/mozconfigs', platform, 'release')
-        nightly_mozconfig_path = path.join(browser_dir, 'config/mozconfigs', platform, 'nightly')
+        if platform is not None:
+            python_exe = substs['PYTHON']
+            topsrcdir = substs['top_srcdir']
 
-        log.info("Comparing beta against nightly mozconfigs")
-        ret_code = subprocess.call([python_exe, script_path, '--whitelist',
-                                    whitelist_path, '--no-download',
-                                    platform + ',' + beta_mozconfig_path +
-                                    ',' + nightly_mozconfig_path])
+            # construct paths and args for compare-mozconfig
+            browser_dir = path.join(topsrcdir, 'browser')
+            script_path = path.join(topsrcdir, 'build/compare-mozconfig/compare-mozconfigs.py')
+            whitelist_path = path.join(browser_dir, 'config/mozconfigs/whitelist')
+            beta_mozconfig_path = path.join(browser_dir, 'config/mozconfigs', platform, 'beta')
+            release_mozconfig_path = path.join(browser_dir, 'config/mozconfigs', platform, 'release')
+            nightly_mozconfig_path = path.join(browser_dir, 'config/mozconfigs', platform, 'nightly')
 
-        if ret_code > 0:
-            return ret_code
+            log.info("Comparing beta against nightly mozconfigs")
+            ret_code = subprocess.call([python_exe, script_path, '--whitelist',
+                                        whitelist_path, '--no-download',
+                                        platform + ',' + beta_mozconfig_path +
+                                        ',' + nightly_mozconfig_path])
 
-        log.info("Comparing release against nightly mozconfigs")
-        ret_code = subprocess.call([python_exe, script_path, '--whitelist',
-                                    whitelist_path, '--no-download',
-                                    platform + ',' + release_mozconfig_path +
-                                    ',' + nightly_mozconfig_path])
+            if ret_code > 0:
+                return ret_code
 
-        return ret_code
+            log.info("Comparing release against nightly mozconfigs")
+            ret_code = subprocess.call([python_exe, script_path, '--whitelist',
+                                        whitelist_path, '--no-download',
+                                        platform + ',' + release_mozconfig_path +
+                                        ',' + nightly_mozconfig_path])
+
+            self.assertEqual(0, ret_code)
 
 if __name__ == '__main__':
-    sys.exit(main())
+    mozunit.main()
--- a/python/mach_commands.py
+++ b/python/mach_commands.py
@@ -72,58 +72,48 @@ class MachCommands(MachCommandBase):
     @CommandArgument('--verbose',
         default=False,
         action='store_true',
         help='Verbose output.')
     @CommandArgument('--stop',
         default=False,
         action='store_true',
         help='Stop running tests after the first error or failure.')
-    @CommandArgument('tests', nargs='+',
+    @CommandArgument('tests', nargs='*',
         metavar='TEST',
         help='Tests to run. Each test can be a single file or a directory.')
-    def python_test(self, tests, verbose=False, stop=False):
+    def python_test(self,
+                    tests=[],
+                    test_objects=None,
+                    subsuite=None,
+                    verbose=False,
+                    stop=False):
         self._activate_virtualenv()
-        import glob
 
         # Python's unittest, and in particular discover, has problems with
         # clashing namespaces when importing multiple test modules. What follows
         # is a simple way to keep environments separate, at the price of
         # launching Python multiple times. This also runs tests via mozunit,
         # which produces output in the format Mozilla infrastructure expects.
         return_code = 0
-        files = []
-        # We search for files in both the current directory (for people running
-        # from topsrcdir or cd'd into their test directory) and topsrcdir (to
-        # support people running mach from the objdir).  The |break|s in the
-        # loop below ensure that we don't run tests twice if we're running mach
-        # from topsrcdir
-        search_dirs = ['.', self.topsrcdir]
-        last_search_dir = search_dirs[-1]
-        for t in tests:
-            for d in search_dirs:
-                test = mozpath.join(d, t)
-                if test.endswith('.py') and os.path.isfile(test):
-                    files.append(test)
-                    break
-                elif os.path.isfile(test + '.py'):
-                    files.append(test + '.py')
-                    break
-                elif os.path.isdir(test):
-                    files += glob.glob(mozpath.join(test, 'test*.py'))
-                    files += glob.glob(mozpath.join(test, 'unit*.py'))
-                    break
-                elif d == last_search_dir:
-                    self.log(logging.WARN, 'python-test',
-                             {'test': t},
-                             'TEST-UNEXPECTED-FAIL | Invalid test: {test}')
-                    if stop:
-                        return 1
+        if test_objects is None:
+            # If we're not being called from `mach test`, do our own
+            # test resolution.
+            from mozbuild.testing import TestResolver
+            resolver = self._spawn(TestResolver)
+            if tests:
+                # If we were given test paths, try to find tests matching them.
+                test_objects = resolver.resolve_tests(paths=tests,
+                                                      flavor='python')
+            else:
+                # Otherwise just run all Python tests.
+                test_objects = resolver.resolve_tests(flavor='python')
 
-        for f in files:
+        for test in test_objects:
+            f = test['path']
             file_displayed_test = []  # Used as a boolean.
 
             def _line_handler(line):
                 if not file_displayed_test and line.startswith('TEST-'):
                     file_displayed_test.append(True)
 
             inner_return_code = self.run_process(
                 [self.virtualenv_manager.python_path, f],
--- a/python/moz.build
+++ b/python/moz.build
@@ -15,44 +15,34 @@ SPHINX_PYTHON_PACKAGE_DIRS += [
     'mozbuild/mozbuild',
     'mozbuild/mozpack',
     'mozversioncontrol/mozversioncontrol',
 ]
 
 SPHINX_TREES['mach'] = 'mach/docs'
 
 PYTHON_UNIT_TESTS += [
-    'mach/mach/test/__init__.py',
-    'mach/mach/test/common.py',
     'mach/mach/test/test_conditions.py',
     'mach/mach/test/test_config.py',
     'mach/mach/test/test_entry_point.py',
     'mach/mach/test/test_error_output.py',
     'mach/mach/test/test_logger.py',
-    'mozbuild/dumbmake/test/__init__.py',
     'mozbuild/dumbmake/test/test_dumbmake.py',
-    'mozbuild/mozbuild/test/__init__.py',
     'mozbuild/mozbuild/test/action/test_buildlist.py',
     'mozbuild/mozbuild/test/action/test_generate_browsersearch.py',
-    'mozbuild/mozbuild/test/backend/__init__.py',
-    'mozbuild/mozbuild/test/backend/common.py',
     'mozbuild/mozbuild/test/backend/test_android_eclipse.py',
     'mozbuild/mozbuild/test/backend/test_build.py',
     'mozbuild/mozbuild/test/backend/test_configenvironment.py',
     'mozbuild/mozbuild/test/backend/test_recursivemake.py',
     'mozbuild/mozbuild/test/backend/test_visualstudio.py',
-    'mozbuild/mozbuild/test/common.py',
-    'mozbuild/mozbuild/test/compilation/__init__.py',
     'mozbuild/mozbuild/test/compilation/test_warnings.py',
     'mozbuild/mozbuild/test/configure/test_configure.py',
     'mozbuild/mozbuild/test/configure/test_options.py',
-    'mozbuild/mozbuild/test/controller/__init__.py',
     'mozbuild/mozbuild/test/controller/test_ccachestats.py',
     'mozbuild/mozbuild/test/controller/test_clobber.py',
-    'mozbuild/mozbuild/test/frontend/__init__.py',
     'mozbuild/mozbuild/test/frontend/test_context.py',
     'mozbuild/mozbuild/test/frontend/test_emitter.py',
     'mozbuild/mozbuild/test/frontend/test_namespaces.py',
     'mozbuild/mozbuild/test/frontend/test_reader.py',
     'mozbuild/mozbuild/test/frontend/test_sandbox.py',
     'mozbuild/mozbuild/test/test_base.py',
     'mozbuild/mozbuild/test/test_containers.py',
     'mozbuild/mozbuild/test/test_dotproperties.py',
@@ -61,17 +51,16 @@ PYTHON_UNIT_TESTS += [
     'mozbuild/mozbuild/test/test_line_endings.py',
     'mozbuild/mozbuild/test/test_makeutil.py',
     'mozbuild/mozbuild/test/test_mozconfig.py',
     'mozbuild/mozbuild/test/test_mozinfo.py',
     'mozbuild/mozbuild/test/test_preprocessor.py',
     'mozbuild/mozbuild/test/test_pythonutil.py',
     'mozbuild/mozbuild/test/test_testing.py',
     'mozbuild/mozbuild/test/test_util.py',
-    'mozbuild/mozpack/test/__init__.py',
     'mozbuild/mozpack/test/test_chrome_flags.py',
     'mozbuild/mozpack/test/test_chrome_manifest.py',
     'mozbuild/mozpack/test/test_copier.py',
     'mozbuild/mozpack/test/test_errors.py',
     'mozbuild/mozpack/test/test_files.py',
     'mozbuild/mozpack/test/test_manifests.py',
     'mozbuild/mozpack/test/test_mozjar.py',
     'mozbuild/mozpack/test/test_packager.py',
--- a/testing/mach_commands.py
+++ b/testing/mach_commands.py
@@ -91,16 +91,20 @@ TEST_SUITES = {
     'mochitest-plain': {
         'mach_command': 'mochitest',
         'kwargs': {'flavor': 'plain', 'test_paths': None},
     },
     'luciddream': {
         'mach_command': 'luciddream',
         'kwargs': {'test_paths': None},
     },
+    'python': {
+        'mach_command': 'python-test',
+        'kwargs': {'tests': None},
+    },
     'reftest': {
         'aliases': ('RR', 'rr', 'Rr'),
         'mach_command': 'reftest',
         'kwargs': {'tests': None},
     },
     'reftest-ipc': {
         'aliases': ('Ripc',),
         'mach_command': 'reftest-ipc',
@@ -141,16 +145,20 @@ TEST_FLAVORS = {
     'marionette': {
         'mach_command': 'marionette-test',
         'kwargs': {'tests': []},
     },
     'mochitest': {
         'mach_command': 'mochitest',
         'kwargs': {'flavor': 'mochitest', 'test_paths': []},
     },
+    'python': {
+        'mach_command': 'python-test',
+        'kwargs': {},
+    },
     'reftest': {
         'mach_command': 'reftest',
         'kwargs': {'tests': []}
     },
     'steeplechase': {},
     'web-platform-tests': {
         'mach_command': 'web-platform-tests',
         'kwargs': {'include': []}
--- a/testing/xpcshell/selftest.py
+++ b/testing/xpcshell/selftest.py
@@ -1,20 +1,25 @@
 #!/usr/bin/env python
 #
 # Any copyright is dedicated to the Public Domain.
 # http://creativecommons.org/publicdomain/zero/1.0/
 #
 
-from __future__ import with_statement
-import sys, os, unittest, tempfile, shutil, re, pprint
 import mozinfo
+import mozunit
+import os
+import pprint
+import re
+import shutil
+import sys
+import tempfile
+import unittest
 
 from StringIO import StringIO
-
 from mozlog import structured
 from mozbuild.base import MozbuildObject
 os.environ.pop('MOZ_OBJDIR', None)
 build_obj = MozbuildObject.from_environment()
 
 from runxpcshelltests import XPCShellTests
 
 mozinfo.find_and_update_from_json()
@@ -1336,9 +1341,9 @@ add_test({
         self.assertEquals(1, self.x.testCount)
         self.assertEquals(1, self.x.passCount)
         self.assertEquals(0, self.x.failCount)
         self.assertEquals(0, self.x.todoCount)
         self.assertInLog(TEST_PASS_STRING)
         self.assertNotInLog(TEST_FAIL_STRING)
 
 if __name__ == "__main__":
-    unittest.main(verbosity=3)
+    mozunit.main()
--- a/toolkit/crashreporter/tools/unit-symbolstore.py
+++ b/toolkit/crashreporter/tools/unit-symbolstore.py
@@ -1,15 +1,16 @@
 #!/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/.
 
 import concurrent.futures
 import mock
+import mozunit
 import os
 import platform
 import shutil
 import struct
 import subprocess
 import sys
 import tempfile
 import unittest
@@ -528,10 +529,10 @@ class TestFunctional(HelperMixin, unitte
         self.assertEqual('hg:', filename[:3])
 
 
 if __name__ == '__main__':
     # use ThreadPoolExecutor to use threading instead of processes so
     # that our mocking/module-patching works.
     symbolstore.Dumper.GlobalInit(concurrent.futures.ThreadPoolExecutor)
 
-    unittest.main()
+    mozunit.main()