Bug 901990 - Part 1: Integrate test manifests with build config; r=ted

This commit is contained in:
Gregory Szorc 2013-09-24 11:50:04 -07:00
parent 21eee8712a
commit 5bb409fdd5
39 changed files with 593 additions and 387 deletions

View File

@ -18,6 +18,7 @@ import mdn_theme
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.graphviz',
'sphinx.ext.todo',
]
templates_path = ['_templates']

View File

@ -20,6 +20,7 @@ Important Concepts
Profile Guided Optimization <pgo>
slow
environment-variables
test_manifests
mozbuild
========

View File

@ -0,0 +1,153 @@
.. _test_manifests:
==============
Test Manifests
==============
Many test suites have their test metadata defined in files called
**test manifests**.
Test manifests are divided into two flavors: :ref:`manifest_destiny_manifests`
and :ref:`reftest_manifests`.
.. _manifest_destiny_manifests:
Manifest Destiny Manifests
==========================
Manifest destiny manifests are essentially ini files that conform to a basic
set of assumptions.
The `reference documentation <http://mozbase.readthedocs.org/en/latest/manifestdestiny.html>`_
for manifest destiny manifests describes the basic format of test manifests.
In summary, manifests are ini files with section names describing test files::
[test_foo.js]
[test_bar.js]
Keys under sections can hold metadata about each test::
[test_foo.js]
skip-if = os == win
There is a special **DEFAULT** section whose keys/metadata apply to all
sections/tests::
[DEFAULT]
property = value
[test_foo.js]
In the above example, **test_foo.js** inherits the metadata **property = value**
from the **DEFAULT** section.
Recognized Metadata
-------------------
Test manifests can define some common keys/metadata to influence behavior.
Those keys are as follows:
head
List of files that will be executed before the test file. (Used in
xpcshell tests.)
tail
List of files that will be executed after the test file. (Used in
xpcshell tests.)
support-files
List of additional files required to run tests. This is typically
defined in the **DEFAULT** section.
Unlike other file lists, *support-files* supports a globbing mechanism
to facilitate pulling in many files with minimal typing. This globbing
mechanism is activated if an entry in this value contains a ``*``
character. A single ``*`` will wildcard match all files in a directory.
A double ``**`` will descend into child directories. For example,
``data/*`` will match ``data/foo`` but not ``data/subdir/bar`` where
``data/**`` will match ``data/foo`` and ``data/subdir/bar``.
generated-files
List of files that are generated as part of the build and don't exist in
the source tree.
The build system assumes that each manifest file, test file, and file
listed in **head**, **tail**, and **support-files** is static and
provided by the source tree (and not automatically generated as part
of the build). This variable tells the build system not to make this
assumption.
This variable will likely go away sometime once all generated files are
accounted for in the build config.
If a generated file is not listed in this key, a clobber build will
likely fail.
dupe-manifest
Record that this manifest duplicates another manifest.
The common scenario is two manifest files will include a shared
manifest file via the ``[include:file]`` special section. The build
system enforces that each test file is only provided by a single
manifest. Having this key present bypasses that check.
The value of this key is ignored.
run-if
Run this test only if the specified condition is true.
See :ref:`manifest_filter_language`.
skip-if
Skip this test if the specified condition is true.
See :ref:`manifest_filter_language`.
fail-if
Expect test failure if the specified condition is true.
See :ref:`manifest_filter_language`.
run-sequentially
If present, the test should not be run in parallel with other tests.
Some test harnesses support parallel test execution on separate processes
and/or threads (behavior varies by test harness). If this key is present,
the test harness should not attempt to run this test in parallel with any
other test.
By convention, the value of this key is a string describing why the test
can't be run in parallel.
.. _manifest_filter_language:
Manifest Filter Language
------------------------
Some manifest keys accept a special filter syntax as their values. These
values are essentially boolean expressions that are evaluated at test
execution time.
See
`the source <https://hg.mozilla.org/mozilla-central/file/default/testing/mozbase/manifestdestiny/manifestparser/manifestparser.py>`_.
.. todo::
Document manifest filter language.
.. _manifest_file_installation:
File Installation
-----------------
Files referenced by manifests are automatically installed into the object
directory into paths defined in
:py:func:`mozbuild.frontend.emitter.TreeMetadataEmitter._process_test_manifest`.
Referenced files in the manifest not in the same directory tree as the manifest
file are **not** installed.
.. _reftest_manifests:
Reftest Manifests
=================
See `MDN <https://developer.mozilla.org/en-US/docs/Creating_reftest-based_unit_tests>`_.

View File

@ -1,137 +0,0 @@
# -*- makefile -*-
# vim:set ts=8 sw=8 sts=8 noet:
#
# 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/.
#
ifndef INCLUDED_TESTS_XPCSHELL_MK #{
ifdef XPCSHELL_TESTS #{
ifndef relativesrcdir
$(error Must define relativesrcdir when defining XPCSHELL_TESTS.)
endif
define _INSTALL_TESTS
$(call install_cmd, $(filter-out %~,$(wildcard $(srcdir)/$(dir)/*)) $(testxpcobjdir)/$(relativesrcdir)/$(dir))
endef # do not remove the blank line!
SOLO_FILE ?= $(error Specify a test filename in SOLO_FILE when using check-interactive or check-one)
testxpcsrcdir = $(topsrcdir)/testing/xpcshell
libs:: libs-xpcshell-tests
###########################################################################
libs-xpcshell-tests:
$(foreach dir,$(XPCSHELL_TESTS),$(_INSTALL_TESTS))
ifndef NO_XPCSHELL_MANIFEST_CHECK #{
$(call py_action,xpccheck,$(topsrcdir) $(addprefix $(MOZILLA_DIR)/$(relativesrcdir)/,$(XPCSHELL_TESTS)))
endif #} NO_XPCSHELL_MANIFEST_CHECK
###########################################################################
# Execute all tests in the $(XPCSHELL_TESTS) directories.
# See also testsuite-targets.mk 'xpcshell-tests' target for global execution.
xpcshell-tests:
$(info Please consider running xpcshell tests via |mach xpcshell-test|. mach is more powerful, easier to use, and will be the only supported way to run tests in the future. Consider switching to mach today!)
$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
-I$(DEPTH)/build \
-I$(topsrcdir)/build \
-I$(DEPTH)/_tests/mozbase/mozinfo \
$(testxpcsrcdir)/runxpcshelltests.py \
--symbols-path=$(DIST)/crashreporter-symbols \
--build-info-json=$(DEPTH)/mozinfo.json \
--tests-root-dir=$(testxpcobjdir) \
--testing-modules-dir=$(DEPTH)/_tests/modules \
--xunit-file=$(testxpcobjdir)/$(relativesrcdir)/results.xml \
--xunit-suite-name=xpcshell \
--test-plugin-path=$(DIST)/plugins \
$(EXTRA_TEST_ARGS) \
$(LIBXUL_DIST)/bin/xpcshell \
$(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
xpcshell-tests-remote: DM_TRANS?=adb
xpcshell-tests-remote:
$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
-I$(DEPTH)/build \
$(topsrcdir)/testing/xpcshell/remotexpcshelltests.py \
--symbols-path=$(DIST)/crashreporter-symbols \
--build-info-json=$(DEPTH)/mozinfo.json \
--testing-modules-dir=$(DEPTH)/_tests/modules \
$(EXTRA_TEST_ARGS) \
--dm_trans=$(DM_TRANS) \
--deviceIP=${TEST_DEVICE} \
--objdir=$(DEPTH) \
$(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
###########################################################################
# Execute a single test, specified in $(SOLO_FILE), but don't automatically
# start the test. Instead, present the xpcshell prompt so the user can
# attach a debugger and then start the test.
check-interactive:
$(info Please consider running xpcshell tests via mach: |mach xpcshell-test --interactive path/to/test|.)
$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
-I$(DEPTH)/build \
-I$(topsrcdir)/build \
-I$(DEPTH)/_tests/mozbase/mozinfo \
$(testxpcsrcdir)/runxpcshelltests.py \
--symbols-path=$(DIST)/crashreporter-symbols \
--build-info-json=$(DEPTH)/mozinfo.json \
--test-path=$(SOLO_FILE) \
--testing-modules-dir=$(DEPTH)/_tests/modules \
--profile-name=$(MOZ_APP_NAME) \
--test-plugin-path=$(DIST)/plugins \
--interactive \
$(LIBXUL_DIST)/bin/xpcshell \
$(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
# Execute a single test, specified in $(SOLO_FILE)
check-one:
$(info Please consider running xpcshell tests via mach: |mach xpcshell-test path/to/test|.)
$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
-I$(DEPTH)/build \
-I$(topsrcdir)/build \
-I$(DEPTH)/_tests/mozbase/mozinfo \
$(testxpcsrcdir)/runxpcshelltests.py \
--symbols-path=$(DIST)/crashreporter-symbols \
--build-info-json=$(DEPTH)/mozinfo.json \
--test-path=$(SOLO_FILE) \
--testing-modules-dir=$(DEPTH)/_tests/modules \
--profile-name=$(MOZ_APP_NAME) \
--test-plugin-path=$(DIST)/plugins \
--verbose \
$(EXTRA_TEST_ARGS) \
$(LIBXUL_DIST)/bin/xpcshell \
$(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
check-one-remote: DM_TRANS?=adb
check-one-remote:
$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
-I$(DEPTH)/build \
-I$(topsrcdir)/build \
-I$(topsrcdir)/build/mobile \
-I$(topsrcdir)/testing/mozbase/mozdevice/mozdevice \
$(testxpcsrcdir)/remotexpcshelltests.py \
--symbols-path=$(DIST)/crashreporter-symbols \
--build-info-json=$(DEPTH)/mozinfo.json \
--test-path=$(SOLO_FILE) \
--testing-modules-dir=$(DEPTH)/_tests/modules \
--profile-name=$(MOZ_APP_NAME) \
--verbose \
$(EXTRA_TEST_ARGS) \
--dm_trans=$(DM_TRANS) \
--deviceIP=${TEST_DEVICE} \
--objdir=$(DEPTH) \
--noSetup \
$(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
.PHONY: xpcshell-tests check-interactive check-one libs-xpcshell-tests
endif #} XPCSHELL_TESTS
INCLUDED_TESTS_XPCSHELL_MK = 1
endif #} INCLUDED_TESTS_XPCSHELL_MK

View File

@ -163,10 +163,6 @@ ifdef ENABLE_TESTS
# locally against non-current test code.
DIRS += $(TEST_DIRS)
ifndef INCLUDED_TESTS_XPCSHELL_MK #{
include $(topsrcdir)/config/makefiles/xpcshell.mk
endif #}
ifndef INCLUDED_TESTS_MOCHITEST_MK #{
include $(topsrcdir)/config/makefiles/mochitest.mk
endif #}

View File

@ -1,137 +0,0 @@
# -*- makefile -*-
# vim:set ts=8 sw=8 sts=8 noet:
#
# 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/.
#
ifndef INCLUDED_TESTS_XPCSHELL_MK #{
ifdef XPCSHELL_TESTS #{
ifndef relativesrcdir
$(error Must define relativesrcdir when defining XPCSHELL_TESTS.)
endif
define _INSTALL_TESTS
$(call install_cmd, $(filter-out %~,$(wildcard $(srcdir)/$(dir)/*)) $(testxpcobjdir)/$(relativesrcdir)/$(dir))
endef # do not remove the blank line!
SOLO_FILE ?= $(error Specify a test filename in SOLO_FILE when using check-interactive or check-one)
testxpcsrcdir = $(topsrcdir)/testing/xpcshell
libs:: libs-xpcshell-tests
###########################################################################
libs-xpcshell-tests:
$(foreach dir,$(XPCSHELL_TESTS),$(_INSTALL_TESTS))
ifndef NO_XPCSHELL_MANIFEST_CHECK #{
$(call py_action,xpccheck,$(topsrcdir) $(addprefix $(MOZILLA_DIR)/$(relativesrcdir)/,$(XPCSHELL_TESTS)))
endif #} NO_XPCSHELL_MANIFEST_CHECK
###########################################################################
# Execute all tests in the $(XPCSHELL_TESTS) directories.
# See also testsuite-targets.mk 'xpcshell-tests' target for global execution.
xpcshell-tests:
$(info Please consider running xpcshell tests via |mach xpcshell-test|. mach is more powerful, easier to use, and will be the only supported way to run tests in the future. Consider switching to mach today!)
$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
-I$(DEPTH)/build \
-I$(topsrcdir)/build \
-I$(DEPTH)/_tests/mozbase/mozinfo \
$(testxpcsrcdir)/runxpcshelltests.py \
--symbols-path=$(DIST)/crashreporter-symbols \
--build-info-json=$(DEPTH)/mozinfo.json \
--tests-root-dir=$(testxpcobjdir) \
--testing-modules-dir=$(DEPTH)/_tests/modules \
--xunit-file=$(testxpcobjdir)/$(relativesrcdir)/results.xml \
--xunit-suite-name=xpcshell \
--test-plugin-path=$(DIST)/plugins \
$(EXTRA_TEST_ARGS) \
$(LIBXUL_DIST)/bin/xpcshell \
$(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
xpcshell-tests-remote: DM_TRANS?=adb
xpcshell-tests-remote:
$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
-I$(DEPTH)/build \
$(topsrcdir)/testing/xpcshell/remotexpcshelltests.py \
--symbols-path=$(DIST)/crashreporter-symbols \
--build-info-json=$(DEPTH)/mozinfo.json \
--testing-modules-dir=$(DEPTH)/_tests/modules \
$(EXTRA_TEST_ARGS) \
--dm_trans=$(DM_TRANS) \
--deviceIP=${TEST_DEVICE} \
--objdir=$(DEPTH) \
$(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
###########################################################################
# Execute a single test, specified in $(SOLO_FILE), but don't automatically
# start the test. Instead, present the xpcshell prompt so the user can
# attach a debugger and then start the test.
check-interactive:
$(info Please consider running xpcshell tests via mach: |mach xpcshell-test --interactive path/to/test|.)
$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
-I$(DEPTH)/build \
-I$(topsrcdir)/build \
-I$(DEPTH)/_tests/mozbase/mozinfo \
$(testxpcsrcdir)/runxpcshelltests.py \
--symbols-path=$(DIST)/crashreporter-symbols \
--build-info-json=$(DEPTH)/mozinfo.json \
--test-path=$(SOLO_FILE) \
--testing-modules-dir=$(DEPTH)/_tests/modules \
--profile-name=$(MOZ_APP_NAME) \
--test-plugin-path=$(DIST)/plugins \
--interactive \
$(LIBXUL_DIST)/bin/xpcshell \
$(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
# Execute a single test, specified in $(SOLO_FILE)
check-one:
$(info Please consider running xpcshell tests via mach: |mach xpcshell-test path/to/test|.)
$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
-I$(DEPTH)/build \
-I$(topsrcdir)/build \
-I$(DEPTH)/_tests/mozbase/mozinfo \
$(testxpcsrcdir)/runxpcshelltests.py \
--symbols-path=$(DIST)/crashreporter-symbols \
--build-info-json=$(DEPTH)/mozinfo.json \
--test-path=$(SOLO_FILE) \
--testing-modules-dir=$(DEPTH)/_tests/modules \
--profile-name=$(MOZ_APP_NAME) \
--test-plugin-path=$(DIST)/plugins \
--verbose \
$(EXTRA_TEST_ARGS) \
$(LIBXUL_DIST)/bin/xpcshell \
$(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
check-one-remote: DM_TRANS?=adb
check-one-remote:
$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
-I$(DEPTH)/build \
-I$(topsrcdir)/build \
-I$(topsrcdir)/build/mobile \
-I$(topsrcdir)/testing/mozbase/mozdevice/mozdevice \
$(testxpcsrcdir)/remotexpcshelltests.py \
--symbols-path=$(DIST)/crashreporter-symbols \
--build-info-json=$(DEPTH)/mozinfo.json \
--test-path=$(SOLO_FILE) \
--testing-modules-dir=$(DEPTH)/_tests/modules \
--profile-name=$(MOZ_APP_NAME) \
--verbose \
$(EXTRA_TEST_ARGS) \
--dm_trans=$(DM_TRANS) \
--deviceIP=${TEST_DEVICE} \
--objdir=$(DEPTH) \
--noSetup \
$(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
.PHONY: xpcshell-tests check-interactive check-one libs-xpcshell-tests
endif #} XPCSHELL_TESTS
INCLUDED_TESTS_XPCSHELL_MK = 1
endif #} INCLUDED_TESTS_XPCSHELL_MK

View File

@ -163,10 +163,6 @@ ifdef ENABLE_TESTS
# locally against non-current test code.
DIRS += $(TEST_DIRS)
ifndef INCLUDED_TESTS_XPCSHELL_MK #{
include $(topsrcdir)/config/makefiles/xpcshell.mk
endif #}
ifndef INCLUDED_TESTS_MOCHITEST_MK #{
include $(topsrcdir)/config/makefiles/mochitest.mk
endif #}

View File

@ -33,7 +33,7 @@ from ..frontend.data import (
TestWebIDLFile,
VariablePassthru,
XPIDLFile,
XpcshellManifests,
TestManifest,
WebIDLFile,
)
from ..util import (
@ -278,7 +278,7 @@ class RecursiveMakeBackend(CommonBackend):
self.summary.backend_detailed_summary = types.MethodType(detailed,
self.summary)
self.xpcshell_manifests = []
self._test_manifests = {}
self.backend_input_files.add(os.path.join(self.environment.topobjdir,
'config', 'autoconf.mk'))
@ -384,8 +384,8 @@ class RecursiveMakeBackend(CommonBackend):
elif isinstance(obj, Program):
self._process_program(obj.program, backend_file)
elif isinstance(obj, XpcshellManifests):
self._process_xpcshell_manifests(obj, backend_file)
elif isinstance(obj, TestManifest):
self._process_test_manifest(obj, backend_file)
elif isinstance(obj, LocalInclude):
self._process_local_include(obj.path, backend_file)
@ -501,7 +501,6 @@ class RecursiveMakeBackend(CommonBackend):
self._update_from_avoid_write(root.close())
self._update_from_avoid_write(root_deps.close())
def consume_finished(self):
CommonBackend.consume_finished(self)
@ -602,17 +601,19 @@ class RecursiveMakeBackend(CommonBackend):
self._update_from_avoid_write(backend_deps.close())
self.summary.managed_count += 1
# Make the master xpcshell.ini file
self.xpcshell_manifests.sort()
if len(self.xpcshell_manifests) > 0:
mastermanifest = FileAvoidWrite(os.path.join(
self.environment.topobjdir, 'testing', 'xpcshell', 'xpcshell.ini'))
mastermanifest.write(
'; THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT MODIFY BY HAND.\n\n')
for manifest in self.xpcshell_manifests:
mastermanifest.write("[include:%s]\n" % manifest)
self._update_from_avoid_write(mastermanifest.close())
self.summary.managed_count += 1
# Make the master test manifest files.
for flavor, t in self._test_manifests.items():
install_prefix, manifests = t
manifest_stem = os.path.join(install_prefix, '%s.ini' % flavor)
self._write_master_test_manifest(os.path.join(
self.environment.topobjdir, '_tests', manifest_stem),
manifests)
# Catch duplicate inserts.
try:
self._install_manifests['tests'].add_optional_exists(manifest_stem)
except ValueError:
pass
self._write_manifests('install', self._install_manifests)
@ -795,12 +796,28 @@ class RecursiveMakeBackend(CommonBackend):
header = 'mozilla/dom/%sBinding.h' % os.path.splitext(basename)[0]
self._install_manifests['dist_include'].add_optional_exists(header)
def _process_xpcshell_manifests(self, obj, backend_file, namespace=""):
manifest = obj.xpcshell_manifests
backend_file.write('XPCSHELL_TESTS += %s\n' % os.path.dirname(manifest))
if obj.relativedir != '':
manifest = '%s/%s' % (obj.relativedir, manifest)
self.xpcshell_manifests.append(manifest)
def _process_test_manifest(self, obj, backend_file):
self.backend_input_files.add(os.path.join(obj.topsrcdir,
obj.manifest_relpath))
# Duplicate manifests may define the same file. That's OK.
for source, dest in obj.installs.items():
try:
self._install_manifests['tests'].add_symlink(source, dest)
except ValueError:
if not obj.dupe_manifest:
raise
for dest in obj.external_installs:
try:
self._install_manifests['tests'].add_optional_exists(dest)
except ValueError:
if not obj.dupe_manifest:
raise
m = self._test_manifests.setdefault(obj.flavor,
(obj.install_prefix, set()))
m[1].add(obj.manifest_relpath)
def _process_local_include(self, local_include, backend_file):
if local_include.startswith('/'):
@ -825,3 +842,14 @@ class RecursiveMakeBackend(CommonBackend):
self._update_from_avoid_write(fh.close())
purger.purge(man_dir)
def _write_master_test_manifest(self, path, manifests):
master = FileAvoidWrite(path)
master.write(
'; THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT MODIFY BY HAND.\n\n')
for manifest in sorted(manifests):
master.write('[include:%s]\n' % manifest)
self._update_from_avoid_write(master.close())
self.summary.managed_count += 1

View File

@ -278,16 +278,56 @@ class Program(SandboxDerived):
program += bin_suffix
self.program = program
class XpcshellManifests(SandboxDerived):
"""Build object container for XPCSHELL_TESTS_MANIFESTS (was: XPCSHELL_TESTS).
This object contains a list of xpcshell.ini manifest files.
"""
__slots__ = ('xpcshell_manifests')
class TestManifest(SandboxDerived):
"""Represents a manifest file containing information about tests."""
def __init__(self, sandbox, manifests):
__slots__ = (
# The type of test manifest this is.
'flavor',
# Maps source filename to destination filename. The destination
# path is relative from the tests root directory.
'installs',
# Where all files for this manifest flavor are installed in the unified
# test package directory.
'install_prefix',
# Set of files provided by an external mechanism.
'external_installs',
# The full path of this manifest file.
'path',
# The directory where this manifest is defined.
'directory',
# The parsed manifestparser.TestManifest instance.
'manifest',
# The relative path of the parsed manifest within the srcdir.
'manifest_relpath',
# If this manifest is a duplicate of another one, this is the
# manifestparser.TestManifest of the other one.
'dupe_manifest',
)
def __init__(self, sandbox, path, manifest, flavor=None,
install_prefix=None, relpath=None, dupe_manifest=False):
SandboxDerived.__init__(self, sandbox)
self.xpcshell_manifests = manifests
self.path = path
self.directory = os.path.dirname(path)
self.manifest = manifest
self.flavor = flavor
self.install_prefix = install_prefix
self.manifest_relpath = relpath
self.dupe_manifest = dupe_manifest
self.installs = {}
self.external_installs = set()
class LocalInclude(SandboxDerived):
"""Describes an individual local include path."""

View File

@ -4,12 +4,18 @@
from __future__ import unicode_literals
import json
import logging
import os
import traceback
import sys
from mach.mixin.logging import LoggingMixin
import mozpack.path as mozpath
import manifestparser
from mozpack.files import FileFinder
from .data import (
ConfigFileSubstitution,
@ -24,10 +30,10 @@ from .data import (
Program,
ReaderSummary,
TestWebIDLFile,
TestManifest,
VariablePassthru,
XPIDLFile,
XpcshellManifests,
WebIDLFile,
XPIDLFile,
)
from .reader import (
@ -49,6 +55,13 @@ class TreeMetadataEmitter(LoggingMixin):
self.config = config
# TODO add mozinfo into config or somewhere else.
mozinfo_path = os.path.join(config.topobjdir, 'mozinfo.json')
if os.path.exists(mozinfo_path):
self.mozinfo = json.load(open(mozinfo_path, 'rt'))
else:
self.mozinfo = {}
def emit(self, output):
"""Convert the BuildReader output into data structures.
@ -179,12 +192,136 @@ class TreeMetadataEmitter(LoggingMixin):
('PREPROCESSED_WEBIDL_FILES', PreprocessedWebIDLFile),
('TEST_WEBIDL_FILES', TestWebIDLFile),
('WEBIDL_FILES', WebIDLFile),
('XPCSHELL_TESTS_MANIFESTS', XpcshellManifests),
]
for sandbox_var, klass in simple_lists:
for name in sandbox.get(sandbox_var, []):
yield klass(sandbox, name)
# While there are multiple test manifests, the behavior is very similar
# across them. We enforce this by having common handling of all
# manifests and outputting a single class type with the differences
# described inside the instance.
#
# Keys are variable prefixes and values are tuples describing how these
# manifests should be handled:
#
# (flavor, install_prefix, active)
#
# flavor identifies the flavor of this test.
# install_prefix is the path prefix of where to install the files in
# the tests directory.
# active indicates whether to filter out inactive tests from the
# manifest.
#
# We ideally don't filter out inactive tests. However, not every test
# harness can yet deal with test filtering. Once all harnesses can do
# this, this feature can be dropped.
test_manifests = dict(
A11Y=('a11y', 'testing/mochitest/a11y', True),
BROWSER_CHROME=('browser-chrome', 'testing/mochitest/browser', True),
MOCHITEST=('mochitest', 'testing/mochitest/tests', True),
MOCHITEST_CHROME=('chrome', 'testing/mochitest/chrome', True),
XPCSHELL_TESTS=('xpcshell', 'xpcshell', False),
)
for prefix, info in test_manifests.items():
for path in sandbox.get('%s_MANIFESTS' % prefix, []):
for obj in self._process_test_manifest(sandbox, info, path):
yield obj
def _process_test_manifest(self, sandbox, info, manifest_path):
flavor, install_prefix, filter_inactive = info
manifest_path = os.path.normpath(manifest_path)
path = mozpath.normpath(mozpath.join(sandbox['SRCDIR'], manifest_path))
manifest_dir = mozpath.dirname(path)
manifest_reldir = mozpath.dirname(mozpath.relpath(path,
self.config.topsrcdir))
try:
m = manifestparser.TestManifest(manifests=[path], strict=True)
if not m.tests:
raise SandboxValidationError('Empty test manifest: %s'
% path)
obj = TestManifest(sandbox, path, m, flavor=flavor,
install_prefix=install_prefix,
relpath=mozpath.join(manifest_reldir, mozpath.basename(path)),
dupe_manifest='dupe-manifest' in m.tests[0])
filtered = m.tests
if filter_inactive:
filtered = m.active_tests(**self.mozinfo)
out_dir = mozpath.join(install_prefix, manifest_reldir)
finder = FileFinder(base=manifest_dir, find_executables=False)
for test in filtered:
obj.installs[mozpath.normpath(test['path'])] = \
mozpath.join(out_dir, test['relpath'])
# xpcshell defines extra files to install in the
# "head" and "tail" lists.
# All manifests support support-files.
for thing in ('head', 'tail', 'support-files'):
for pattern in test.get(thing, '').split():
# We only support globbing on support-files because
# the harness doesn't support * for head and tail.
#
# While we could feed everything through the finder, we
# don't because we want explicitly listed files that
# no longer exist to raise an error. The finder is also
# slower than simple lookup.
if '*' in pattern and thing == 'support-files':
paths = [f[0] for f in finder.find(pattern)]
if not paths:
raise SandboxValidationError('%s support-files '
'wildcard in %s returns no results.' % (
pattern, path))
for f in paths:
full = mozpath.normpath(mozpath.join(manifest_dir, f))
obj.installs[full] = mozpath.join(out_dir, f)
else:
full = mozpath.normpath(mozpath.join(manifest_dir,
pattern))
# Only install paths in our directory. This
# rule is somewhat arbitrary and could be lifted.
if not full.startswith(manifest_dir):
continue
obj.installs[full] = mozpath.join(out_dir, pattern)
# We also copy the manifest into the output directory.
out_path = mozpath.join(out_dir, os.path.basename(manifest_path))
obj.installs[path] = out_path
# Some manifests reference files that are auto generated as
# part of the build or shouldn't be installed for some
# reason. Here, we prune those files from the install set.
# FUTURE we should be able to detect autogenerated files from
# other build metadata. Once we do that, we can get rid of this.
for f in m.tests[0].get('generated-files', '').split():
# We re-raise otherwise the stack trace isn't informative.
try:
del obj.installs[mozpath.join(manifest_dir, f)]
except KeyError:
raise SandboxValidationError('Error processing test '
'manifest %s: entry in generated-files not present '
'elsewhere in manifest: %s' % (path, f))
obj.external_installs.add(mozpath.join(out_dir, f))
yield obj
except (AssertionError, Exception):
raise SandboxValidationError('Error processing test '
'manifest file %s: %s' % (path,
'\n'.join(traceback.format_exception(*sys.exc_info()))))
def _emit_directory_traversal_from_sandbox(self, sandbox):
o = DirectoryTraversal(sandbox)
o.dirs = sandbox.get('DIRS', [])

View File

@ -450,11 +450,25 @@ VARIABLES = {
These will be preprocessed before being parsed and converted.
"""),
'XPCSHELL_TESTS_MANIFESTS': (StrictOrderingOnAppendList, list, [],
"""XPCSHELL Test Manifest list
# Test declaration.
'A11Y_MANIFESTS': (StrictOrderingOnAppendList, list, [],
"""List of manifest files defining a11y tests.
"""),
This is a list of xpcshell.ini manifest files.
Formerly XPCSHELL_TESTS=
'BROWSER_CHROME_MANIFESTS': (StrictOrderingOnAppendList, list, [],
"""List of manifest files defining browser chrome tests.
"""),
'MOCHITEST_MANIFESTS': (StrictOrderingOnAppendList, list, [],
"""List of manifest files defining mochitest tests.
"""),
'MOCHITEST_CHROME_MANIFESTS': (StrictOrderingOnAppendList, list, [],
"""List of manifest files defining mochitest chrome tests.
"""),
'XPCSHELL_TESTS_MANIFESTS': (StrictOrderingOnAppendList, list, [],
"""List of manifest files defining xpcshell tests.
"""),
}

View File

@ -68,12 +68,10 @@ CONFIGS = {
'non_global_defines': [],
'substs': [],
},
'xpcshell_manifests': {
'test-manifests-written': {
'defines': [],
'non_global_defines': [],
'substs': [
('XPCSHELL_TESTS_MANIFESTS', 'XPCSHELL_TESTS'),
],
'substs': [],
},
'ipdl_sources': {
'defines': [],

View File

@ -0,0 +1,3 @@
[DEFAULT]
[test_bar.js]

View File

@ -0,0 +1,3 @@
[DEFAULT]
[mochitest.js]

View File

@ -0,0 +1,9 @@
# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/
XPCSHELL_TESTS_MANIFESTS += [
'dir1/xpcshell.ini',
'xpcshell.ini',
]
MOCHITEST_MANIFESTS += ['mochitest.ini']

View File

@ -0,0 +1,3 @@
[DEFAULT]
[xpcshell.js]

View File

@ -1,11 +0,0 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/
XPCSHELL_TESTS_MANIFESTS = ['aa/xpcshell.ini', 'bb/xpcshell.ini']
XPCSHELL_TESTS_MANIFESTS += ['cc/xpcshell.ini', 'dd/xpcshell.ini']
if CONFIG['_INVALID_CONFIG_VALUE']:
XPCSHELL_TESTS_MANIFESTS += ['invalid_val/xpcshell.ini']
else:
XPCSHELL_TESTS_MANIFESTS += ['valid_val/xpcshell.ini']

View File

@ -376,20 +376,23 @@ class TestRecursiveMakeBackend(BackendTester):
self.assertIn('mozilla/mozilla1.h', m)
self.assertIn('mozilla/dom/dom2.h', m)
def test_xpcshell_manifests(self):
"""Ensure XPCSHELL_TESTS_MANIFESTS is written out correctly."""
env = self._consume('xpcshell_manifests', RecursiveMakeBackend)
def test_test_manifests_files_written(self):
"""Ensure test manifests get turned into files."""
env = self._consume('test-manifests-written', RecursiveMakeBackend)
backend_path = os.path.join(env.topobjdir, 'backend.mk')
lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]]
tests_dir = os.path.join(env.topobjdir, '_tests')
m_master = os.path.join(tests_dir, 'testing', 'mochitest', 'tests', 'mochitest.ini')
x_master = os.path.join(tests_dir, 'xpcshell', 'xpcshell.ini')
self.assertTrue(os.path.exists(m_master))
self.assertTrue(os.path.exists(x_master))
# Avoid positional parameter and async related breakage
var = 'XPCSHELL_TESTS'
xpclines = sorted([val for val in lines if val.startswith(var)])
# Assignment[aa], append[cc], conditional[valid]
expected = ('aa', 'bb', 'cc', 'dd', 'valid_val')
self.assertEqual(xpclines, ["XPCSHELL_TESTS += %s" % val for val in expected])
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]',
])
def test_xpidl_generation(self):
"""Ensure xpidl files and directories are written out."""
@ -418,18 +421,6 @@ class TestRecursiveMakeBackend(BackendTester):
self.assertTrue(os.path.isfile(os.path.join(p, 'Makefile')))
def test_xpcshell_master_manifest(self):
"""Ensure that the master xpcshell manifest is written out correctly."""
env = self._consume('xpcshell_manifests', RecursiveMakeBackend)
manifest_path = os.path.join(env.topobjdir,
'testing', 'xpcshell', 'xpcshell.ini')
lines = [l.strip() for l in open(manifest_path, 'rt').readlines()]
expected = ('aa', 'bb', 'cc', 'dd', 'valid_val')
self.assertEqual(lines, [
'; THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT MODIFY BY HAND.',
''] + ['[include:%s/xpcshell.ini]' % x for x in expected])
def test_old_install_manifest_deleted(self):
# Simulate an install manifest from a previous backend version. Ensure
# it is deleted.

View File

@ -0,0 +1,2 @@
[DEFAULT]
foo = bar

View File

@ -0,0 +1,4 @@
# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/
MOCHITEST_MANIFESTS += ['empty.ini']

View File

@ -0,0 +1,4 @@
[DEFAULT]
support-files = a11y-support/**
[test_a11y.js]

View File

@ -0,0 +1,4 @@
[DEFAULT]
support-files = support1 support2
[test_browser.js]

View File

@ -0,0 +1,3 @@
[DEFAULT]
[test_chrome.js]

View File

@ -0,0 +1,5 @@
[DEFAULT]
support-files = external1 external2
generated-files = external1 external2
[test_mochitest.js]

View File

@ -0,0 +1,8 @@
# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/
A11Y_MANIFESTS += ['a11y.ini']
BROWSER_CHROME_MANIFESTS += ['browser.ini']
MOCHITEST_MANIFESTS += ['mochitest.ini']
MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
XPCSHELL_TESTS_MANIFESTS += ['xpcshell.ini']

View File

@ -0,0 +1,6 @@
[DEFAULT]
head = head1 head2
tail = tail1 tail2
dupe-manifest =
[test_xpcshell.js]

View File

@ -0,0 +1,4 @@
# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/
XPCSHELL_TESTS_MANIFESTS += ['does_not_exist.ini']

View File

@ -0,0 +1,4 @@
# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/
MOCHITEST_MANIFESTS += ['test.ini']

View File

@ -0,0 +1,4 @@
[DEFAULT]
generated-files = does_not_exist
[test_foo]

View File

@ -1,14 +0,0 @@
# -*- Mode: python; c-basic-offset: 4; 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/.
XPCSHELL_TESTS_MANIFESTS += ['bar/xpcshell.ini', 'foo/xpcshell.ini']
XPCSHELL_TESTS_MANIFESTS += ['tans/xpcshell.ini']
if CONFIG['DISABLED_VAR']:
XPCSHELL_TESTS_MANIFESTS += ['disabled_var/xpcshell.ini']
if not CONFIG['DISABLED_VAR']:
XPCSHELL_TESTS_MANIFESTS += ['enabled_var/xpcshell.ini']

View File

@ -17,12 +17,15 @@ from mozbuild.frontend.data import (
Defines,
Exports,
Program,
XpcshellManifests,
IPDLFile,
LocalInclude,
TestManifest,
)
from mozbuild.frontend.emitter import TreeMetadataEmitter
from mozbuild.frontend.reader import BuildReader
from mozbuild.frontend.reader import (
BuildReader,
SandboxValidationError,
)
from mozbuild.test.common import MockConfig
@ -217,23 +220,105 @@ class TestEmitterBasic(unittest.TestCase):
program = objs[1].program
self.assertEqual(program, 'test_program.prog')
def test_xpcshell_manifests(self):
reader = self.reader('xpcshell_manifests')
objs = self.read_topsrcdir(reader)
def test_test_manifest_missing_manifest(self):
"""A missing manifest file should result in an error."""
reader = self.reader('test-manifest-missing-manifest')
with self.assertRaisesRegexp(SandboxValidationError, 'IOError: Missing files'):
self.read_topsrcdir(reader)
def test_empty_test_manifest_rejected(self):
"""A test manifest without any entries is rejected."""
reader = self.reader('test-manifest-empty')
with self.assertRaisesRegexp(SandboxValidationError, 'Empty test manifest'):
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), 5)
metadata = {
'a11y.ini': {
'flavor': 'a11y',
'installs': {
'a11y.ini',
'test_a11y.js',
# From ** wildcard.
'a11y-support/foo',
'a11y-support/dir1/bar',
},
},
'browser.ini': {
'flavor': 'browser-chrome',
'installs': {
'browser.ini',
'test_browser.js',
'support1',
'support2',
},
},
'mochitest.ini': {
'flavor': 'mochitest',
'installs': {
'mochitest.ini',
'test_mochitest.js',
},
'external': {
'external1',
'external2',
},
},
'chrome.ini': {
'flavor': 'chrome',
'installs': {
'chrome.ini',
'test_chrome.js',
},
},
'xpcshell.ini': {
'flavor': 'xpcshell',
'dupe': True,
'installs': {
'xpcshell.ini',
'test_xpcshell.js',
'head1',
'head2',
'tail1',
'tail2',
},
},
}
inis = []
for o in objs:
if isinstance(o, XpcshellManifests):
inis.append(o.xpcshell_manifests)
m = metadata[os.path.basename(o.manifest_relpath)]
iniByDir = [
'bar/xpcshell.ini',
'enabled_var/xpcshell.ini',
'foo/xpcshell.ini',
'tans/xpcshell.ini',
]
self.assertTrue(o.path.startswith(o.directory))
self.assertEqual(o.flavor, m['flavor'])
self.assertEqual(o.dupe_manifest, m.get('dupe', False))
self.assertEqual(sorted(inis), iniByDir)
external_normalized = set(os.path.basename(p) for p in
o.external_installs)
self.assertEqual(external_normalized, m.get('external', set()))
self.assertEqual(len(o.installs), len(m['installs']))
for path in o.installs.keys():
self.assertTrue(path.startswith(o.directory))
path = path[len(o.directory)+1:]
self.assertIn(path, m['installs'])
def test_test_manifest_unmatched_generated(self):
reader = self.reader('test-manifest-unmatched-generated')
with self.assertRaisesRegexp(SandboxValidationError,
'entry in generated-files not present elsewhere'):
self.read_topsrcdir(reader),
def test_ipdl_sources(self):
reader = self.reader('ipdl_sources')

View File

@ -47,7 +47,6 @@ MOZINFO_FILES := \
PKG_STAGE = $(DIST)/test-package-stage
libs::
$(INSTALL) xpcshell.ini $(DEPTH)/_tests/xpcshell
$(INSTALL) $(srcdir)/xpcshell_b2g.ini $(DEPTH)/_tests/xpcshell
$(INSTALL) $(srcdir)/xpcshell_android.ini $(DEPTH)/_tests/xpcshell
cp $(DEPTH)/_tests/xpcshell/xpcshell.ini $(DEPTH)/_tests/xpcshell/all-test-dirs.list