Bug 1317970 - Use manifestparser manifests for python unit tests, r=chmanchester
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Wed, 16 Nov 2016 09:59:22 -0500
changeset 370912 068f2a43fe68d786814e3789b4966344449daea1
parent 370911 55405892e588fa851979d8ea31a656853666b40f
child 370913 9100b758e2580c05ddf8c8ac0f17ab4d8fc26809
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerschmanchester
bugs1317970
milestone53.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 1317970 - Use manifestparser manifests for python unit tests, r=chmanchester This deprecates PYTHON_UNIT_TESTS and replaces it with PYTHON_UNITTEST_MANIFESTS. In the build system, this means python unittests will be treated the same as all other test suites that use manifestparser. New manifests called 'python.ini' have been created for all test directories containing python unittests. MozReview-Commit-ID: IBHG7Thif2D
build/compare-mozconfig/python.ini
build/moz.build
config/moz.build
config/tests/python.ini
dom/bindings/moz.build
dom/bindings/mozwebidlcodegen/test/python.ini
mozglue/linker/tests/moz.build
mozglue/linker/tests/python.ini
python/mach/mach/test/python.ini
python/mach_commands.py
python/moz.build
python/mozbuild/dumbmake/test/python.ini
python/mozbuild/mozbuild/backend/recursivemake.py
python/mozbuild/mozbuild/frontend/context.py
python/mozbuild/mozbuild/frontend/emitter.py
python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/moz.build
python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/python.ini
python/mozbuild/mozbuild/test/frontend/data/test-python-unit-test-missing/moz.build
python/mozbuild/mozbuild/test/frontend/test_emitter.py
python/mozbuild/mozbuild/test/python.ini
python/mozbuild/mozbuild/testing.py
python/mozbuild/mozpack/test/python.ini
python/mozlint/test/python.ini
testing/mozbase/docs/manifestparser.rst
testing/mozbase/moz.build
testing/mozbase/test-manifest.ini
testing/mozbase/test.py
testing/xpcshell/moz.build
testing/xpcshell/python.ini
toolkit/crashreporter/moz.build
toolkit/crashreporter/tools/python.ini
xpcom/idl-parser/xpidl/moz.build
xpcom/idl-parser/xpidl/python.ini
xpcom/typelib/xpt/tools/moz.build
xpcom/typelib/xpt/tools/python.ini
new file mode 100644
--- /dev/null
+++ b/build/compare-mozconfig/python.ini
@@ -0,0 +1,1 @@
+[compare-mozconfigs-wrapper.py]
--- a/build/moz.build
+++ b/build/moz.build
@@ -33,18 +33,18 @@ if CONFIG['MOZ_APP_PROFILE']:
     DEFINES['MOZ_APP_PROFILE'] = CONFIG['MOZ_APP_PROFILE']
 
 for var in ('MOZ_CRASHREPORTER', 'MOZ_PROFILE_MIGRATOR',
             'MOZ_APP_STATIC_INI'):
     if CONFIG[var]:
         DEFINES[var] = True
 
 if CONFIG['MOZ_BUILD_APP'] == 'browser':
-    PYTHON_UNIT_TESTS += [
-        'compare-mozconfig/compare-mozconfigs-wrapper.py',
+    PYTHON_UNITTEST_MANIFESTS += [
+        'compare-mozconfig/python.ini',
     ]
 
 if CONFIG['ENABLE_TESTS'] or CONFIG['MOZ_DMD']:
     FINAL_TARGET_FILES += ['/tools/rb/fix_stack_using_bpsyms.py']
     if CONFIG['OS_ARCH'] == 'Darwin':
         FINAL_TARGET_FILES += ['/tools/rb/fix_macosx_stack.py']
     if CONFIG['OS_ARCH'] == 'Linux':
         FINAL_TARGET_FILES += ['/tools/rb/fix_linux_stack.py']
--- a/config/moz.build
+++ b/config/moz.build
@@ -27,22 +27,18 @@ if CONFIG['HOST_OS_ARCH'] != 'WINNT':
     # stdc++compat depends on config/export, so avoid a circular
     # dependency added by HostProgram depending on stdc++compat,
     # while the program here is in C.
     HostProgram('nsinstall_real', c_only=True)
 
 if CONFIG['MOZ_SYSTEM_ICU']:
     DEFINES['MOZ_SYSTEM_ICU'] = True
 
-PYTHON_UNIT_TESTS += [
-    'tests/test_mozbuild_reading.py',
-    'tests/unit-expandlibs.py',
-    'tests/unit-mozunit.py',
-    'tests/unit-nsinstall.py',
-    'tests/unit-printprereleasesuffix.py',
+PYTHON_UNITTEST_MANIFESTS += [
+    'tests/python.ini',
 ]
 
 if CONFIG['GNU_CC'] and CONFIG['MOZ_OPTIMIZE']:
     CFLAGS += ['-O3']
 
 HOST_DEFINES = {
     'UNICODE': True,
     '_UNICODE': True,
new file mode 100644
--- /dev/null
+++ b/config/tests/python.ini
@@ -0,0 +1,5 @@
+[test_mozbuild_reading.py]
+[unit-expandlibs.py]
+[unit-mozunit.py]
+[unit-nsinstall.py]
+[unit-printprereleasesuffix.py]
--- a/dom/bindings/moz.build
+++ b/dom/bindings/moz.build
@@ -139,18 +139,18 @@ FINAL_LIBRARY = 'xul'
 SPHINX_TREES['webidl'] = 'docs'
 SPHINX_PYTHON_PACKAGE_DIRS += ['mozwebidlcodegen']
 
 if CONFIG['MOZ_BUILD_APP'] in ['browser', 'mobile/android', 'xulrunner']:
     # This is needed for Window.webidl
     DEFINES['HAVE_SIDEBAR'] = True
 
 
-PYTHON_UNIT_TESTS += [
-    'mozwebidlcodegen/test/test_mozwebidlcodegen.py',
+PYTHON_UNITTEST_MANIFESTS += [
+    'mozwebidlcodegen/test/python.ini',
 ]
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wno-error=shadow']
 
 if CONFIG['COMPILE_ENVIRONMENT']:
     GENERATED_FILES += ['CSS2Properties.webidl']
     css_props = GENERATED_FILES['CSS2Properties.webidl']
new file mode 100644
--- /dev/null
+++ b/dom/bindings/mozwebidlcodegen/test/python.ini
@@ -0,0 +1,1 @@
+[test_mozwebidlcodegen.py]
--- a/mozglue/linker/tests/moz.build
+++ b/mozglue/linker/tests/moz.build
@@ -12,12 +12,12 @@ SimplePrograms([
 LOCAL_INCLUDES += ['..']
 USE_LIBS += [
     'linker',
     'mfbt',
 ]
 OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']
 DISABLE_STL_WRAPPING = True
 
-PYTHON_UNIT_TESTS += ['run_test_zip.py']
+PYTHON_UNITTEST_MANIFESTS += ['python.ini']
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wno-error=shadow']
new file mode 100644
--- /dev/null
+++ b/mozglue/linker/tests/python.ini
@@ -0,0 +1,1 @@
+[run_test_zip.py]
new file mode 100644
--- /dev/null
+++ b/python/mach/mach/test/python.ini
@@ -0,0 +1,6 @@
+[test_conditions.py]
+[test_config.py]
+[test_dispatcher.py]
+[test_entry_point.py]
+[test_error_output.py]
+[test_logger.py]
--- a/python/mach_commands.py
+++ b/python/mach_commands.py
@@ -60,17 +60,17 @@ class MachCommands(MachCommandBase):
               'test resolution. Supports pytest-style tests.'))
     @CommandArgument('-j', '--jobs',
         default=1,
         type=int,
         help='Number of concurrent jobs to run. Default is 1.')
     @CommandArgument('tests', nargs='*',
         metavar='TEST',
         help=('Tests to run. Each test can be a single file or a directory. '
-              'Default test resolution relies on PYTHON_UNIT_TESTS.'))
+              'Default test resolution relies on PYTHON_UNITTEST_MANIFESTS.'))
     def python_test(self,
                     tests=[],
                     test_objects=None,
                     subsuite=None,
                     verbose=False,
                     path_only=False,
                     stop=False,
                     jobs=1):
@@ -113,23 +113,23 @@ class MachCommands(MachCommandBase):
             else:
                 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 everything in PYTHON_UNIT_TESTS
+                    # Otherwise just run everything in PYTHON_UNITTEST_MANIFESTS
                     test_objects = resolver.resolve_tests(flavor='python')
 
         if not test_objects:
             message = 'TEST-UNEXPECTED-FAIL | No tests collected'
             if not path_only:
-                message += ' (Not in PYTHON_UNIT_TESTS? Try --path-only?)'
+                message += ' (Not in PYTHON_UNITTEST_MANIFESTS? Try --path-only?)'
             self.log(logging.WARN, 'python-test', {}, message)
             return 1
 
         self.jobs = jobs
         self.terminate = False
         self.verbose = verbose
 
         return_code = 0
--- a/python/moz.build
+++ b/python/moz.build
@@ -15,74 +15,15 @@ SPHINX_PYTHON_PACKAGE_DIRS += [
     'mozbuild/mozbuild',
     'mozbuild/mozpack',
     'mozlint/mozlint',
     'mozversioncontrol/mozversioncontrol',
 ]
 
 SPHINX_TREES['mach'] = 'mach/docs'
 
-PYTHON_UNIT_TESTS += [
-    'mach/mach/test/test_conditions.py',
-    'mach/mach/test/test_config.py',
-    'mach/mach/test/test_dispatcher.py',
-    'mach/mach/test/test_entry_point.py',
-    'mach/mach/test/test_error_output.py',
-    'mach/mach/test/test_logger.py',
-    'mozbuild/dumbmake/test/test_dumbmake.py',
-    'mozbuild/mozbuild/test/action/test_buildlist.py',
-    'mozbuild/mozbuild/test/action/test_generate_browsersearch.py',
-    'mozbuild/mozbuild/test/action/test_package_fennec_apk.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/compilation/test_warnings.py',
-    'mozbuild/mozbuild/test/configure/lint.py',
-    'mozbuild/mozbuild/test/configure/test_checks_configure.py',
-    'mozbuild/mozbuild/test/configure/test_compile_checks.py',
-    'mozbuild/mozbuild/test/configure/test_configure.py',
-    'mozbuild/mozbuild/test/configure/test_lint.py',
-    'mozbuild/mozbuild/test/configure/test_moz_configure.py',
-    'mozbuild/mozbuild/test/configure/test_options.py',
-    'mozbuild/mozbuild/test/configure/test_toolchain_configure.py',
-    'mozbuild/mozbuild/test/configure/test_toolchain_helpers.py',
-    'mozbuild/mozbuild/test/configure/test_toolkit_moz_configure.py',
-    'mozbuild/mozbuild/test/configure/test_util.py',
-    'mozbuild/mozbuild/test/controller/test_ccachestats.py',
-    'mozbuild/mozbuild/test/controller/test_clobber.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',
-    'mozbuild/mozbuild/test/test_expression.py',
-    'mozbuild/mozbuild/test/test_jarmaker.py',
-    '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/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',
-    'mozbuild/mozpack/test/test_packager_formats.py',
-    'mozbuild/mozpack/test/test_packager_l10n.py',
-    'mozbuild/mozpack/test/test_packager_unpack.py',
-    'mozbuild/mozpack/test/test_path.py',
-    'mozbuild/mozpack/test/test_unify.py',
-    'mozlint/test/test_formatters.py',
-    'mozlint/test/test_parser.py',
-    'mozlint/test/test_roller.py',
-    'mozlint/test/test_types.py',
+PYTHON_UNITTEST_MANIFESTS += [
+    'mach/mach/test/python.ini',
+    'mozbuild/dumbmake/test/python.ini',
+    'mozbuild/mozbuild/test/python.ini',
+    'mozbuild/mozpack/test/python.ini',
+    'mozlint/test/python.ini',
 ]
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/dumbmake/test/python.ini
@@ -0,0 +1,1 @@
+[test_dumbmake.py]
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -115,17 +115,16 @@ MOZBUILD_VARIABLES = [
     b'NO_DIST_INSTALL',
     b'NO_EXPAND_LIBS',
     b'NO_INTERFACES_MANIFEST',
     b'NO_JS_MANIFEST',
     b'OS_LIBS',
     b'PARALLEL_DIRS',
     b'PREF_JS_EXPORTS',
     b'PROGRAM',
-    b'PYTHON_UNIT_TESTS',
     b'RESOURCE_FILES',
     b'SDK_HEADERS',
     b'SDK_LIBRARY',
     b'SHARED_LIBRARY_LIBS',
     b'SHARED_LIBRARY_NAME',
     b'SIMPLE_PROGRAMS',
     b'SONAME',
     b'STATIC_LIBRARY_NAME',
--- a/python/mozbuild/mozbuild/frontend/context.py
+++ b/python/mozbuild/mozbuild/frontend/context.py
@@ -1149,20 +1149,16 @@ VARIABLES = {
         """),
 
     'IS_COMPONENT': (bool, bool,
         """Whether the library contains a binary XPCOM component manifest.
 
         Implies FORCE_SHARED_LIB.
         """),
 
-    'PYTHON_UNIT_TESTS': (StrictOrderingOnAppendList, list,
-        """A list of python unit tests.
-        """),
-
     'HOST_LIBRARY_NAME': (unicode, unicode,
         """Name of target library generated when cross compiling.
         """),
 
     'JAVA_JAR_TARGETS': (dict, dict,
         """Defines Java JAR targets to be built.
 
         This variable should not be populated directly. Instead, it should
@@ -1570,16 +1566,21 @@ VARIABLES = {
     'WEBRTC_SIGNALLING_TEST_MANIFESTS': (ManifestparserManifestList, list,
         """List of manifest files defining WebRTC signalling tests.
         """),
 
     'XPCSHELL_TESTS_MANIFESTS': (ManifestparserManifestList, list,
         """List of manifest files defining xpcshell tests.
         """),
 
+    'PYTHON_UNITTEST_MANIFESTS': (ManifestparserManifestList, list,
+        """List of manifest files defining python unit tests.
+        """),
+
+
     # The following variables are used to control the target of installed files.
     'XPI_NAME': (unicode, unicode,
         """The name of an extension XPI to generate.
 
         When this variable is present, the results of this directory will end up
         being packaged into an extension instead of the main dist/bin results.
         """),
 
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -929,17 +929,16 @@ class TreeMetadataEmitter(LoggingMixin):
         passthru = VariablePassthru(context)
         varlist = [
             'ALLOW_COMPILER_WARNINGS',
             'ANDROID_APK_NAME',
             'ANDROID_APK_PACKAGE',
             'ANDROID_GENERATED_RESFILES',
             'DISABLE_STL_WRAPPING',
             'EXTRA_DSO_LDOPTS',
-            'PYTHON_UNIT_TESTS',
             'RCFILE',
             'RESFILE',
             'RCINCLUDE',
             'DEFFILE',
             'WIN32_EXE_LDFLAGS',
             'LD_VERSION_SCRIPT',
             'USE_EXTENSION_MANIFEST',
             'NO_JS_MANIFEST',
@@ -1255,21 +1254,16 @@ class TreeMetadataEmitter(LoggingMixin):
                 for obj in self._process_reftest_manifest(context, flavor, path, manifest):
                     yield obj
 
         for flavor in WEB_PLATFORM_TESTS_FLAVORS:
             for path, manifest in context.get("%s_MANIFESTS" % flavor.upper().replace('-', '_'), []):
                 for obj in self._process_web_platform_tests_manifest(context, path, manifest):
                     yield obj
 
-        python_tests = context.get('PYTHON_UNIT_TESTS')
-        if python_tests:
-            for obj in self._process_python_tests(context, python_tests):
-                yield obj
-
     def _process_test_manifest(self, context, info, manifest_path, mpmanifest):
         flavor, install_root, install_subdir, package_tests = info
 
         path = mozpath.normpath(mozpath.join(context.srcdir, manifest_path))
         manifest_dir = mozpath.dirname(path)
         manifest_reldir = mozpath.dirname(mozpath.relpath(path,
             context.config.topsrcdir))
         install_prefix = mozpath.join(install_root, install_subdir)
@@ -1425,46 +1419,16 @@ class TreeMetadataEmitter(LoggingMixin):
                     'head': '',
                     'tail': '',
                     'support-files': '',
                     'subsuite': '',
                 })
 
         yield obj
 
-    def _process_python_tests(self, context, python_tests):
-        manifest_full_path = context.main_path
-        manifest_reldir = mozpath.dirname(mozpath.relpath(manifest_full_path,
-            context.config.topsrcdir))
-
-        obj = TestManifest(context, manifest_full_path,
-                mozpath.basename(manifest_full_path),
-                flavor='python', install_prefix='python/',
-                relpath=mozpath.join(manifest_reldir,
-                    mozpath.basename(manifest_full_path)))
-
-        for test in python_tests:
-            test = mozpath.normpath(mozpath.join(context.srcdir, test))
-            if not os.path.isfile(test):
-                raise SandboxValidationError('Path specified in '
-                   'PYTHON_UNIT_TESTS does not exist: %s' % test,
-                   context)
-            obj.tests.append({
-                'path': test,
-                'here': mozpath.dirname(test),
-                'manifest': manifest_full_path,
-                'name': mozpath.basename(test),
-                'head': '',
-                'tail': '',
-                'support-files': '',
-                'subsuite': '',
-            })
-
-        yield obj
-
     def _process_jar_manifests(self, context):
         jar_manifests = context.get('JAR_MANIFESTS', [])
         if len(jar_manifests) > 1:
             raise SandboxValidationError('While JAR_MANIFESTS is a list, '
                 'it is currently limited to one value.', context)
 
         for path in jar_manifests:
             yield JARManifest(context, path)
--- a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/moz.build
+++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/moz.build
@@ -4,9 +4,9 @@
 A11Y_MANIFESTS += ['a11y.ini']
 BROWSER_CHROME_MANIFESTS += ['browser.ini']
 METRO_CHROME_MANIFESTS += ['metro.ini']
 MOCHITEST_MANIFESTS += ['mochitest.ini']
 MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
 XPCSHELL_TESTS_MANIFESTS += ['xpcshell.ini']
 REFTEST_MANIFESTS += ['reftest.list']
 CRASHTEST_MANIFESTS += ['crashtest.list']
-PYTHON_UNIT_TESTS += ['test_foo.py']
+PYTHON_UNITTEST_MANIFESTS += ['python.ini']
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-keys-extracted/python.ini
@@ -0,0 +1,1 @@
+[test_foo.py]
deleted file mode 100644
--- a/python/mozbuild/mozbuild/test/frontend/data/test-python-unit-test-missing/moz.build
+++ /dev/null
@@ -1,4 +0,0 @@
-# Any copyright is dedicated to the Public Domain.
-# http://creativecommons.org/publicdomain/zero/1.0/
-
-PYTHON_UNIT_TESTS += ['test_foo.py']
--- a/python/mozbuild/mozbuild/test/frontend/test_emitter.py
+++ b/python/mozbuild/mozbuild/test/frontend/test_emitter.py
@@ -535,23 +535,16 @@ class TestEmitterBasic(unittest.TestCase
             'reftest1-ref.html': 'reftest.list',
             'reftest2.html': 'included-reftest.list',
             'reftest2-ref.html': 'included-reftest.list',
         }
 
         for t in obj.tests:
             self.assertTrue(t['manifest'].endswith(expected_manifests[t['name']]))
 
-    def test_python_unit_test_missing(self):
-        """Missing files in PYTHON_UNIT_TESTS should raise."""
-        reader = self.reader('test-python-unit-test-missing')
-        with self.assertRaisesRegexp(SandboxValidationError,
-            'Path specified in PYTHON_UNIT_TESTS does not exist:'):
-            self.read_topsrcdir(reader)
-
     def test_test_manifest_keys_extracted(self):
         """Ensure all metadata from test manifests is extracted."""
         reader = self.reader('test-manifest-keys-extracted')
 
         objs = [o for o in self.read_topsrcdir(reader)
                 if isinstance(o, TestManifest)]
 
         self.assertEqual(len(objs), 9)
@@ -614,19 +607,21 @@ class TestEmitterBasic(unittest.TestCase
             'reftest.list': {
                 'flavor': 'reftest',
                 'installs': {},
             },
             'crashtest.list': {
                 'flavor': 'crashtest',
                 'installs': {},
             },
-            'moz.build': {
+            'python.ini': {
                 'flavor': 'python',
-                'installs': {},
+                'installs': {
+                    'python.ini': False,
+                },
             }
         }
 
         for o in objs:
             m = metadata[mozpath.basename(o.manifest_relpath)]
 
             self.assertTrue(o.path.startswith(o.directory))
             self.assertEqual(o.flavor, m['flavor'])
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/python.ini
@@ -0,0 +1,40 @@
+[action/test_buildlist.py]
+[action/test_generate_browsersearch.py]
+[action/test_package_fennec_apk.py]
+[backend/test_android_eclipse.py]
+[backend/test_build.py]
+[backend/test_configenvironment.py]
+[backend/test_recursivemake.py]
+[backend/test_visualstudio.py]
+[compilation/test_warnings.py]
+[configure/lint.py]
+[configure/test_checks_configure.py]
+[configure/test_compile_checks.py]
+[configure/test_configure.py]
+[configure/test_lint.py]
+[configure/test_moz_configure.py]
+[configure/test_options.py]
+[configure/test_toolchain_configure.py]
+[configure/test_toolchain_helpers.py]
+[configure/test_toolkit_moz_configure.py]
+[configure/test_util.py]
+[controller/test_ccachestats.py]
+[controller/test_clobber.py]
+[frontend/test_context.py]
+[frontend/test_emitter.py]
+[frontend/test_namespaces.py]
+[frontend/test_reader.py]
+[frontend/test_sandbox.py]
+[test_base.py]
+[test_containers.py]
+[test_dotproperties.py]
+[test_expression.py]
+[test_jarmaker.py]
+[test_line_endings.py]
+[test_makeutil.py]
+[test_mozconfig.py]
+[test_mozinfo.py]
+[test_preprocessor.py]
+[test_pythonutil.py]
+[test_testing.py]
+[test_util.py]
--- a/python/mozbuild/mozbuild/testing.py
+++ b/python/mozbuild/mozbuild/testing.py
@@ -283,16 +283,17 @@ TEST_MANIFESTS = dict(
     A11Y=('a11y', 'testing/mochitest', 'a11y', True),
     BROWSER_CHROME=('browser-chrome', 'testing/mochitest', 'browser', True),
     ANDROID_INSTRUMENTATION=('instrumentation', 'instrumentation', '.', False),
     JETPACK_PACKAGE=('jetpack-package', 'testing/mochitest', 'jetpack-package', True),
     JETPACK_ADDON=('jetpack-addon', 'testing/mochitest', 'jetpack-addon', False),
     FIREFOX_UI_FUNCTIONAL=('firefox-ui-functional', 'firefox-ui', '.', False),
     FIREFOX_UI_UPDATE=('firefox-ui-update', 'firefox-ui', '.', False),
     PUPPETEER_FIREFOX=('firefox-ui-functional', 'firefox-ui', '.', False),
+    PYTHON_UNITTEST=('python', 'python', '.', False),
 
     # marionette tests are run from the srcdir
     # TODO(ato): make packaging work as for other test suites
     MARIONETTE=('marionette', 'marionette', '.', False),
     MARIONETTE_UNIT=('marionette', 'marionette', '.', False),
     MARIONETTE_WEBAPI=('marionette', 'marionette', '.', False),
 
     METRO_CHROME=('metro-chrome', 'testing/mochitest', 'metro', True),
@@ -306,18 +307,17 @@ TEST_MANIFESTS = dict(
 REFTEST_FLAVORS = ('crashtest', 'reftest')
 
 # Web platform tests have their own manifest format and are processed separately.
 WEB_PLATFORM_TESTS_FLAVORS = ('web-platform-tests',)
 
 def all_test_flavors():
     return ([v[0] for v in TEST_MANIFESTS.values()] +
             list(REFTEST_FLAVORS) +
-            list(WEB_PLATFORM_TESTS_FLAVORS) +
-            ['python'])
+            list(WEB_PLATFORM_TESTS_FLAVORS))
 
 class TestInstallInfo(object):
     def __init__(self):
         self.seen = set()
         self.pattern_installs = []
         self.installs = []
         self.external_installs = set()
         self.deferred_installs = set()
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozpack/test/python.ini
@@ -0,0 +1,13 @@
+[test_chrome_flags.py]
+[test_chrome_manifest.py]
+[test_copier.py]
+[test_errors.py]
+[test_files.py]
+[test_manifests.py]
+[test_mozjar.py]
+[test_packager.py]
+[test_packager_formats.py]
+[test_packager_l10n.py]
+[test_packager_unpack.py]
+[test_path.py]
+[test_unify.py]
new file mode 100644
--- /dev/null
+++ b/python/mozlint/test/python.ini
@@ -0,0 +1,4 @@
+[test_formatters.py]
+[test_parser.py]
+[test_roller.py]
+[test_types.py]
--- a/testing/mozbase/docs/manifestparser.rst
+++ b/testing/mozbase/docs/manifestparser.rst
@@ -450,22 +450,20 @@ To update from a directory of tests in `
 
 .. code-block:: text
 
     manifestparser update manifest.ini ~/mozmill/src/mozmill-tests/firefox/
 
 Tests
 `````
 
-manifestparser includes a suite of tests:
-
-https://github.com/mozilla/mozbase/tree/master/manifestparsery/tests
+manifestparser includes a suite of tests.
 
 `test_manifest.txt` is a doctest that may be helpful in figuring out
-how to use the API.  Tests are run via `python test.py`.
+how to use the API.  Tests are run via `mach python-test testing/mozbase/manifestparser`.
 
 Bugs
 ````
 
 Please file any bugs or feature requests at
 
 https://bugzilla.mozilla.org/enter_bug.cgi?product=Testing&component=ManifestParser
 
--- a/testing/mozbase/moz.build
+++ b/testing/mozbase/moz.build
@@ -1,16 +1,29 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=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/.
 
-PYTHON_UNIT_TESTS += [
-    'test.py',
+PYTHON_UNITTEST_MANIFESTS += [
+    'manifestparser/tests/manifest.ini',
+    'mozcrash/tests/manifest.ini',
+    'mozdevice/tests/manifest.ini',
+    'mozfile/tests/manifest.ini',
+    'mozhttpd/tests/manifest.ini',
+    'mozinfo/tests/manifest.ini',
+    'mozinstall/tests/manifest.ini',
+    'mozlog/tests/manifest.ini',
+    'moznetwork/tests/manifest.ini',
+    'mozprocess/tests/manifest.ini',
+    'mozprofile/tests/manifest.ini',
+    'mozrunner/tests/manifest.ini',
+    'moztest/tests/manifest.ini',
+    'mozversion/tests/manifest.ini',
 ]
 
 python_modules = [
     'manifestparser',
     'mozcrash',
     'mozdebug',
     'mozdevice',
     'mozfile',
@@ -28,11 +41,9 @@ python_modules = [
     'moztest',
     'mozversion',
 ]
 
 TEST_HARNESS_FILES.mozbase += [m + '/**' for m in python_modules]
 
 TEST_HARNESS_FILES.mozbase += [
     'setup_development.py',
-    'test-manifest.ini',
-    'test.py',
 ]
deleted file mode 100644
--- a/testing/mozbase/test-manifest.ini
+++ /dev/null
@@ -1,24 +0,0 @@
-# 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/.
-
-# mozbase test manifest, in the format of
-# http://mozbase.readthedocs.org/en/latest/manifestparser.html
-
-# run with
-# https://github.com/mozilla/mozbase/blob/master/test.py
-
-[include:manifestparser/tests/manifest.ini]
-[include:mozcrash/tests/manifest.ini]
-[include:mozdevice/tests/manifest.ini]
-[include:mozfile/tests/manifest.ini]
-[include:mozhttpd/tests/manifest.ini]
-[include:mozinfo/tests/manifest.ini]
-[include:mozinstall/tests/manifest.ini]
-[include:mozlog/tests/manifest.ini]
-[include:moznetwork/tests/manifest.ini]
-[include:mozprocess/tests/manifest.ini]
-[include:mozprofile/tests/manifest.ini]
-[include:mozrunner/tests/manifest.ini]
-[include:moztest/tests/manifest.ini]
-[include:mozversion/tests/manifest.ini]
deleted file mode 100755
--- a/testing/mozbase/test.py
+++ /dev/null
@@ -1,104 +0,0 @@
-#!/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/.
-
-"""
-run mozbase tests from a manifest,
-by default https://github.com/mozilla/mozbase/blob/master/test-manifest.ini
-"""
-
-import imp
-import manifestparser
-import mozinfo
-import optparse
-import os
-import sys
-import unittest
-
-import mozlog
-from moztest.results import TestResultCollection
-from moztest.adapters.unit import StructuredTestRunner
-
-here = os.path.dirname(os.path.abspath(__file__))
-
-
-def unittests(path):
-    """return the unittests in a .py file"""
-
-    path = os.path.abspath(path)
-    unittests = []
-    assert os.path.exists(path)
-    directory = os.path.dirname(path)
-    sys.path.insert(0, directory)  # insert directory into path for top-level imports
-    modname = os.path.splitext(os.path.basename(path))[0]
-    module = imp.load_source(modname, path)
-    sys.path.pop(0)  # remove directory from global path
-    loader = unittest.TestLoader()
-    suite = loader.loadTestsFromModule(module)
-    for test in suite:
-        unittests.append(test)
-    return unittests
-
-
-def main(args=sys.argv[1:]):
-
-    # parse command line options
-    usage = '%prog [options] manifest.ini <manifest.ini> <...>'
-    parser = optparse.OptionParser(usage=usage, description=__doc__)
-    parser.add_option('-b', "--binary",
-                      dest="binary", help="Binary path",
-                      metavar=None, default=None)
-    parser.add_option('--list', dest='list_tests',
-                      action='store_true', default=False,
-                      help="list paths of tests to be run")
-    mozlog.commandline.add_logging_group(parser)
-    options, args = parser.parse_args(args)
-    logger = mozlog.commandline.setup_logging("mozbase", options,
-                                              {"tbpl": sys.stdout})
-
-    # read the manifest
-    if args:
-        manifests = args
-    else:
-        manifests = [os.path.join(here, 'test-manifest.ini')]
-    missing = []
-    for manifest in manifests:
-        # ensure manifests exist
-        if not os.path.exists(manifest):
-            missing.append(manifest)
-    assert not missing, 'manifest(s) not found: %s' % ', '.join(missing)
-    manifest = manifestparser.TestManifest(manifests=manifests)
-
-    if options.binary:
-        # A specified binary should override the environment variable
-        os.environ['BROWSER_PATH'] = options.binary
-
-    # gather the tests
-    tests = manifest.active_tests(disabled=False, **mozinfo.info)
-    tests = [test['path'] for test in tests]
-    logger.suite_start(tests)
-
-    if options.list_tests:
-        # print test paths
-        print '\n'.join(tests)
-        sys.exit(0)
-
-    # create unittests
-    unittestlist = []
-    for test in tests:
-        unittestlist.extend(unittests(test))
-
-    # run the tests
-    suite = unittest.TestSuite(unittestlist)
-    runner = StructuredTestRunner(logger=logger)
-    unittest_results = runner.run(suite)
-    results = TestResultCollection.from_unittest_results(None, unittest_results)
-    logger.suite_end()
-
-    # exit according to results
-    sys.exit(1 if results.num_failures else 0)
-
-if __name__ == '__main__':
-    main()
--- a/testing/xpcshell/moz.build
+++ b/testing/xpcshell/moz.build
@@ -1,18 +1,15 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=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/.
 
 TEST_DIRS += ['example']
 
-if CONFIG['HOST_OS_ARCH'] != 'Darwin':
-    # Disabled on Mac due to our builders still being on MacOS 10.7,
-    # see bug 1255588
-    PYTHON_UNIT_TESTS += [
-        'selftest.py',
-    ]
+PYTHON_UNITTEST_MANIFESTS += [
+    'python.ini',
+]
 
 TESTING_JS_MODULES += [
     'dbg-actors.js',
 ]
new file mode 100644
--- /dev/null
+++ b/testing/xpcshell/python.ini
@@ -0,0 +1,3 @@
+[selftest.py]
+# Disabled on Mac due to our builders still being on MacOS 10.7, see bug 1255588
+skip-if = os == "mac"
--- a/toolkit/crashreporter/moz.build
+++ b/toolkit/crashreporter/moz.build
@@ -108,18 +108,18 @@ DEFINES['UNICODE'] = True
 DEFINES['_UNICODE'] = True
 
 JAR_MANIFESTS += ['jar.mn']
 
 LOCAL_INCLUDES += [
     'google-breakpad/src',
 ]
 
-PYTHON_UNIT_TESTS += [
-    'tools/unit-symbolstore.py',
+PYTHON_UNITTEST_MANIFESTS += [
+    'tools/python.ini',
 ]
 
 include('/toolkit/crashreporter/crashreporter.mozbuild')
 
 with Files('**'):
     BUG_COMPONENT = ('Toolkit', 'Breakpad Integration')
 
 if CONFIG['GNU_CXX']:
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/tools/python.ini
@@ -0,0 +1,1 @@
+[unit-symbolstore.py]
--- a/xpcom/idl-parser/xpidl/moz.build
+++ b/xpcom/idl-parser/xpidl/moz.build
@@ -1,16 +1,16 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=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/.
 
-PYTHON_UNIT_TESTS += [
-    'runtests.py',
+PYTHON_UNITTEST_MANIFESTS += [
+    'python.ini',
 ]
 
 GENERATED_FILES += [
     ('xpidl.stub', 'xpidllex.py', 'xpidlyacc.py'),
 ]
 
 GENERATED_FILES[('xpidl.stub', 'xpidllex.py', 'xpidlyacc.py')].script = 'header.py:main'
 
new file mode 100644
--- /dev/null
+++ b/xpcom/idl-parser/xpidl/python.ini
@@ -0,0 +1,1 @@
+[runtests.py]
--- a/xpcom/typelib/xpt/tools/moz.build
+++ b/xpcom/typelib/xpt/tools/moz.build
@@ -1,13 +1,13 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=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/.
 
-PYTHON_UNIT_TESTS += [
-    'runtests.py',
+PYTHON_UNITTEST_MANIFESTS += [
+    'python.ini',
 ]
 
 SDK_FILES.bin += [
     'xpt.py',
 ]
new file mode 100644
--- /dev/null
+++ b/xpcom/typelib/xpt/tools/python.ini
@@ -0,0 +1,1 @@
+[runtests.py]