bug 1255479 - make `mach python-tests` use TestResolver for discovery, make `mach test` work for python tests. r?nalexander draft
authorTed Mielczarek <ted@mielczarek.org>
Thu, 10 Mar 2016 12:30:10 -0500
changeset 339186 6d1d44188bb3a4fc69da2900e8118ce2a88a5fd7
parent 339185 ba8933994145311fafcd4270fd33a0c656bdeb0b
child 515924 a526125ccb582c16c6620d05e8a1cb822afd3cd2
push id12654
push usertmielczarek@mozilla.com
push dateThu, 10 Mar 2016 17:32:40 +0000
reviewersnalexander
bugs1255479
milestone47.0a1
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,49 @@ 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
@@ -523,10 +524,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()