Bug 1320194 - Refactor test metadata related backend code into a partial TestManifestBackend
Currently the CommonBackend is responsible for processing TestManifest objects and using them to generate
the test metadata files (e.g all-tests.pkl et al). This patch pulls that logic out into a partial backend
specifically for test manifests.
This patch is solely a refactoring and shouldn't change any build behaviour. CommonBackend has a
TestManifestBackend instance and calls consume_object directly on it. However, this is just a temporary
measure to avoid checking in a broken commit.
This commit also adds a test for the 'test-defaults.pkl' file which was previously missing.
MozReview-Commit-ID: HOr2QVT8CJ1
--- a/python/mozbuild/mozbuild/backend/__init__.py
+++ b/python/mozbuild/mozbuild/backend/__init__.py
@@ -5,16 +5,17 @@
backends = {
'AndroidEclipse': 'mozbuild.backend.android_eclipse',
'ChromeMap': 'mozbuild.codecoverage.chrome_map',
'CompileDB': 'mozbuild.compilation.database',
'CppEclipse': 'mozbuild.backend.cpp_eclipse',
'FasterMake': 'mozbuild.backend.fastermake',
'FasterMake+RecursiveMake': None,
'RecursiveMake': 'mozbuild.backend.recursivemake',
+ 'TestManifest': 'mozbuild.backend.test_manifest',
'Tup': 'mozbuild.backend.tup',
'VisualStudio': 'mozbuild.backend.visualstudio',
}
def get_backend_class(name):
if '+' in name:
from mozbuild.backend.base import HybridBackend
--- a/python/mozbuild/mozbuild/backend/common.py
+++ b/python/mozbuild/mozbuild/backend/common.py
@@ -1,22 +1,22 @@
# 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 absolute_import, unicode_literals
-import cPickle as pickle
import itertools
import json
import os
import mozpack.path as mozpath
from mozbuild.backend.base import BuildBackend
+from mozbuild.backend.test_manifest import TestManifestBackend
from mozbuild.frontend.context import (
Context,
Path,
RenamedSourcePath,
VARIABLES,
)
from mozbuild.frontend.data import (
@@ -161,85 +161,43 @@ class WebIDLCollection(object):
def generated_events_basenames(self):
return [os.path.basename(s) for s in self.generated_events_sources]
def generated_events_stems(self):
return [os.path.splitext(b)[0] for b in self.generated_events_basenames()]
-class TestManager(object):
- """Helps hold state related to tests."""
-
- def __init__(self, config):
- self.config = config
- self.topsrcdir = mozpath.normpath(config.topsrcdir)
-
- self.tests_by_path = defaultdict(list)
- self.installs_by_path = defaultdict(list)
- self.deferred_installs = set()
- self.manifest_defaults = {}
-
- def add(self, t, flavor, topsrcdir):
- t = dict(t)
- t['flavor'] = flavor
-
- path = mozpath.normpath(t['path'])
- assert mozpath.basedir(path, [topsrcdir])
-
- key = path[len(topsrcdir)+1:]
- t['file_relpath'] = key
- t['dir_relpath'] = mozpath.dirname(key)
-
- self.tests_by_path[key].append(t)
-
- def add_defaults(self, manifest):
- if not hasattr(manifest, 'manifest_defaults'):
- return
- for sub_manifest, defaults in manifest.manifest_defaults.items():
- self.manifest_defaults[sub_manifest] = defaults
-
- def add_installs(self, obj, topsrcdir):
- for src, (dest, _) in obj.installs.iteritems():
- key = src[len(topsrcdir)+1:]
- self.installs_by_path[key].append((src, dest))
- for src, pat, dest in obj.pattern_installs:
- key = mozpath.join(src[len(topsrcdir)+1:], pat)
- self.installs_by_path[key].append((src, pat, dest))
- for path in obj.deferred_installs:
- self.deferred_installs.add(path[2:])
-
-
class BinariesCollection(object):
"""Tracks state of binaries produced by the build."""
def __init__(self):
self.shared_libraries = []
self.programs = []
class CommonBackend(BuildBackend):
"""Holds logic common to all build backends."""
def _init(self):
self._idl_manager = XPIDLManager(self.environment)
- self._test_manager = TestManager(self.environment)
self._webidls = WebIDLCollection()
self._binaries = BinariesCollection()
self._configs = set()
self._ipdl_sources = set()
+ # Temporarily compose a partial TestManifestBackend, soon test manifest
+ # processing will no longer be part of the build and this will be removed.
+ self._test_backend = TestManifestBackend(self.environment)
+
def consume_object(self, obj):
self._configs.add(obj.config)
if isinstance(obj, TestManifest):
- for test in obj.tests:
- self._test_manager.add(test, obj.flavor, obj.topsrcdir)
- self._test_manager.add_defaults(obj.manifest)
- self._test_manager.add_installs(obj, obj.topsrcdir)
+ self._test_backend.consume_object(obj)
elif isinstance(obj, XPIDLFile):
# TODO bug 1240134 tracks not processing XPIDL files during
# artifact builds.
self._idl_manager.register_idl(obj)
elif isinstance(obj, ConfigFileSubstitution):
# Do not handle ConfigFileSubstitution for Makefiles. Leave that
@@ -365,31 +323,20 @@ class CommonBackend(BuildBackend):
self._write_unified_files(unified_source_mapping, ipdl_dir, poison_windows_h=False)
self._handle_ipdl_sources(ipdl_dir, sorted_ipdl_sources, unified_source_mapping)
for config in self._configs:
self.backend_input_files.add(config.source)
# Write out a machine-readable file describing every test.
- topobjdir = self.environment.topobjdir
- with self._write_file(mozpath.join(topobjdir, 'all-tests.pkl'), mode='rb') as fh:
- pickle.dump(dict(self._test_manager.tests_by_path), fh, protocol=2)
-
- with self._write_file(mozpath.join(topobjdir, 'test-defaults.pkl'), mode='rb') as fh:
- pickle.dump(self._test_manager.manifest_defaults, fh, protocol=2)
-
- path = mozpath.join(self.environment.topobjdir, 'test-installs.pkl')
- with self._write_file(path, mode='rb') as fh:
- pickle.dump({k: v for k, v in self._test_manager.installs_by_path.items()
- if k in self._test_manager.deferred_installs},
- fh,
- protocol=2)
+ self._test_backend.consume_finished()
# Write out a machine-readable file describing binaries.
+ topobjdir = self.environment.topobjdir
with self._write_file(mozpath.join(topobjdir, 'binaries.json')) as fh:
d = {
'shared_libraries': [s.to_dict() for s in self._binaries.shared_libraries],
'programs': [p.to_dict() for p in self._binaries.programs],
}
json.dump(d, fh, sort_keys=True, indent=4)
def _handle_webidl_collection(self, webidls):
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/backend/test_manifest.py
@@ -0,0 +1,95 @@
+# 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 absolute_import, unicode_literals
+
+import cPickle as pickle
+from collections import defaultdict
+
+import mozpack.path as mozpath
+
+from mozbuild.backend.base import PartialBackend
+from mozbuild.frontend.data import TestManifest
+
+
+class TestManifestBackend(PartialBackend):
+ """Partial backend that generates test metadata files."""
+
+ def _init(self):
+ self.tests_by_path = defaultdict(list)
+ self.installs_by_path = defaultdict(list)
+ self.deferred_installs = set()
+ self.manifest_defaults = {}
+
+ # Add config.status so performing a build will invalidate this backend.
+ self.backend_input_files.add(mozpath.join(
+ self.environment.topobjdir, 'config.status'))
+
+ def consume_object(self, obj):
+ if not isinstance(obj, TestManifest):
+ return
+
+ self.backend_input_files.add(obj.path)
+ self.backend_input_files |= obj.context_all_paths
+ try:
+ from reftest import ReftestManifest
+
+ if isinstance(obj.manifest, ReftestManifest):
+ # Mark included files as part of the build backend so changes
+ # result in re-config.
+ self.backend_input_files |= obj.manifest.manifests
+ except ImportError:
+ # Ignore errors caused by the reftest module not being present.
+ # This can happen when building SpiderMonkey standalone, for example.
+ pass
+
+ for test in obj.tests:
+ self.add(test, obj.flavor, obj.topsrcdir)
+ self.add_defaults(obj.manifest)
+ self.add_installs(obj, obj.topsrcdir)
+
+ def consume_finished(self):
+ topobjdir = self.environment.topobjdir
+
+ with self._write_file(mozpath.join(topobjdir, 'all-tests.pkl'), mode='rb') as fh:
+ pickle.dump(dict(self.tests_by_path), fh, protocol=2)
+
+ with self._write_file(mozpath.join(topobjdir, 'test-defaults.pkl'), mode='rb') as fh:
+ pickle.dump(self.manifest_defaults, fh, protocol=2)
+
+ path = mozpath.join(topobjdir, 'test-installs.pkl')
+ with self._write_file(path, mode='rb') as fh:
+ pickle.dump({k: v for k, v in self.installs_by_path.items()
+ if k in self.deferred_installs},
+ fh,
+ protocol=2)
+
+ def add(self, t, flavor, topsrcdir):
+ t = dict(t)
+ t['flavor'] = flavor
+
+ path = mozpath.normpath(t['path'])
+ assert mozpath.basedir(path, [topsrcdir])
+
+ key = path[len(topsrcdir)+1:]
+ t['file_relpath'] = key
+ t['dir_relpath'] = mozpath.dirname(key)
+
+ self.tests_by_path[key].append(t)
+
+ def add_defaults(self, manifest):
+ if not hasattr(manifest, 'manifest_defaults'):
+ return
+ for sub_manifest, defaults in manifest.manifest_defaults.items():
+ self.manifest_defaults[sub_manifest] = defaults
+
+ def add_installs(self, obj, topsrcdir):
+ for src, (dest, _) in obj.installs.iteritems():
+ key = src[len(topsrcdir)+1:]
+ self.installs_by_path[key].append((src, dest))
+ for src, pat, dest in obj.pattern_installs:
+ key = mozpath.join(src[len(topsrcdir)+1:], pat)
+ self.installs_by_path[key].append((src, pat, dest))
+ for path in obj.deferred_installs:
+ self.deferred_installs.add(path[2:])
--- a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
+++ b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
@@ -540,56 +540,36 @@ class TestRecursiveMakeBackend(BackendTe
lines = [l.strip() for l in open(x_master, 'rt').readlines()]
self.assertEqual(lines, [
'# THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT MODIFY BY HAND.',
'',
'[include:dir1/xpcshell.ini]',
'[include:xpcshell.ini]',
])
- all_tests_path = mozpath.join(env.topobjdir, 'all-tests.pkl')
- self.assertTrue(os.path.exists(all_tests_path))
-
- with open(all_tests_path, 'rb') as fh:
- o = pickle.load(fh)
-
- self.assertIn('xpcshell.js', o)
- self.assertIn('dir1/test_bar.js', o)
-
- self.assertEqual(len(o['xpcshell.js']), 1)
-
def test_test_manifest_pattern_matches_recorded(self):
"""Pattern matches in test manifests' support-files should be recorded."""
env = self._consume('test-manifests-written', RecursiveMakeBackend)
m = InstallManifest(path=mozpath.join(env.topobjdir,
'_build_manifests', 'install', '_test_files'))
# This is not the most robust test in the world, but it gets the job
# done.
entries = [e for e in m._dests.keys() if '**' in e]
self.assertEqual(len(entries), 1)
self.assertIn('support/**', entries[0])
def test_test_manifest_deffered_installs_written(self):
"""Shared support files are written to their own data file by the backend."""
env = self._consume('test-manifest-shared-support', RecursiveMakeBackend)
- all_tests_path = mozpath.join(env.topobjdir, 'all-tests.pkl')
- self.assertTrue(os.path.exists(all_tests_path))
test_installs_path = mozpath.join(env.topobjdir, 'test-installs.pkl')
with open(test_installs_path, 'r') as fh:
test_installs = pickle.load(fh)
- self.assertEqual(set(test_installs.keys()),
- set(['child/test_sub.js',
- 'child/data/**',
- 'child/another-file.sjs']))
- for key in test_installs.keys():
- self.assertIn(key, test_installs)
-
test_files_manifest = mozpath.join(env.topobjdir,
'_build_manifests',
'install',
'_test_files')
# First, read the generated for ini manifest contents.
m = InstallManifest(path=test_files_manifest)
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/backend/test_test_manifest.py
@@ -0,0 +1,73 @@
+# 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 cPickle as pickle
+import os
+
+import mozpack.path as mozpath
+from mozunit import main
+
+from mozbuild.backend.test_manifest import TestManifestBackend
+from mozbuild.test.backend.common import BackendTester
+
+
+class TestTestManifestBackend(BackendTester):
+
+ def test_all_tests_metadata_file_written(self):
+ """Ensure all-tests.pkl is generated."""
+ env = self._consume('test-manifests-written', TestManifestBackend)
+
+ all_tests_path = mozpath.join(env.topobjdir, 'all-tests.pkl')
+ self.assertTrue(os.path.exists(all_tests_path))
+
+ with open(all_tests_path, 'rb') as fh:
+ o = pickle.load(fh)
+
+ self.assertIn('xpcshell.js', o)
+ self.assertIn('dir1/test_bar.js', o)
+
+ self.assertEqual(len(o['xpcshell.js']), 1)
+
+ def test_test_installs_metadata_file_written(self):
+ """Ensure test-installs.pkl is generated."""
+ env = self._consume('test-manifest-shared-support', TestManifestBackend)
+ all_tests_path = mozpath.join(env.topobjdir, 'all-tests.pkl')
+ self.assertTrue(os.path.exists(all_tests_path))
+ test_installs_path = mozpath.join(env.topobjdir, 'test-installs.pkl')
+
+ with open(test_installs_path, 'r') as fh:
+ test_installs = pickle.load(fh)
+
+ self.assertEqual(set(test_installs.keys()),
+ set(['child/test_sub.js',
+ 'child/data/**',
+ 'child/another-file.sjs']))
+
+ for key in test_installs.keys():
+ self.assertIn(key, test_installs)
+
+ def test_test_defaults_metadata_file_written(self):
+ """Ensure test-defaults.pkl is generated."""
+ env = self._consume('test-manifests-written', TestManifestBackend)
+
+ test_defaults_path = mozpath.join(env.topobjdir, 'test-defaults.pkl')
+ self.assertTrue(os.path.exists(test_defaults_path))
+
+ with open(test_defaults_path, 'rb') as fh:
+ o = {mozpath.normpath(k): v for k, v in pickle.load(fh).items()}
+
+ self.assertEquals(set(mozpath.relpath(k, env.topsrcdir) for k in o.keys()),
+ set(['dir1/xpcshell.ini',
+ 'xpcshell.ini',
+ 'mochitest.ini']))
+
+ manifest_path = mozpath.join(env.topsrcdir, 'xpcshell.ini')
+ self.assertIn('here', o[manifest_path])
+ self.assertIn('support-files', o[manifest_path])
+
+
+if __name__ == '__main__':
+ main()
--- a/python/mozbuild/mozbuild/test/python.ini
+++ b/python/mozbuild/mozbuild/test/python.ini
@@ -1,15 +1,16 @@
[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_test_manifest.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]