1
Universal manifests for Mozilla test harnesses
3
# What is ManifestDestiny?
5
What ManifestDestiny gives you:
7
* manifests are ordered lists of tests
8
* tests may have an arbitrary number of key, value pairs
9
* the parser returns an ordered list of test data structures, which
10
are just dicts with some keys. For example, a test with no
11
user-specified metadata looks like this:
14
'/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests/testToolbar/testBackForwardButtons.js',
15
'name': 'testToolbar/testBackForwardButtons.js', 'here':
16
'/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests',
17
'manifest': '/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests',}]
19
The keys displayed here (path, name, here, and manifest) are reserved
20
keys for ManifestDestiny and any consuming APIs. You can add
21
additional key, value metadata to each test.
24
# Why have test manifests?
26
It is desirable to have a unified format for test manifests for testing
27
[mozilla-central](http://hg.mozilla.org/mozilla-central), etc.
29
* It is desirable to be able to selectively enable or disable tests based on platform or other conditions. This should be easy to do. Currently, since many of the harnesses just crawl directories, there is no effective way of disabling a test except for removal from mozilla-central
30
* It is desriable to do this in a universal way so that enabling and disabling tests as well as other tasks are easily accessible to a wider audience than just those intimately familiar with the specific test framework.
31
* It is desirable to have other metadata on top of the test. For instance, let's say a test is marked as skipped. It would be nice to give the reason why.
34
Most Mozilla test harnesses work by crawling a directory structure.
35
While this is straight-forward, manifests offer several practical
38
* ability to turn a test off easily: if a test is broken on m-c
39
currently, the only way to turn it off, generally speaking, is just
40
removing the test. Often this is undesirable, as if the test should
41
be dismissed because other people want to land and it can't be
42
investigated in real time (is it a failure? is the test bad? is no
43
one around that knows the test?), then backing out a test is at best
44
problematic. With a manifest, a test may be disabled without
45
removing it from the tree and a bug filed with the appropriate
49
disabled = https://bugzilla.mozilla.org/show_bug.cgi?id=123456
51
* ability to run different (subsets of) tests on different
52
platforms. Traditionally, we've done a bit of magic or had the test
53
know what platform it would or would not run on. With manifests, you
54
can mark what platforms a test will or will not run on and change
55
these without changing the test.
57
[test_works_on_windows_only.js]
60
* ability to markup tests with metadata. We have a large, complicated,
61
and always changing infrastructure. key, value metadata may be used
62
as an annotation to a test and appropriately curated and mined. For
63
instance, we could mark certain tests as randomorange with a bug
64
number, if it were desirable.
66
* ability to have sane and well-defined test-runs. You can keep
67
different manifests for different test runs and ``[include:]``
68
(sub)manifests as appropriate to your needs.
73
Manifests are .ini file with the section names denoting the path
74
relative to the manifest:
80
The sections are read in order. In addition, tests may include
81
arbitrary key, value metadata to be used by the harness. You may also
82
have a `[DEFAULT]` section that will give key, value pairs that will
83
be inherited by each test unless overridden:
94
# override type from DEFAULT
99
You can also include other manifests:
101
[include:subdir/anothermanifest.ini]
103
Manifests are included relative to the directory of the manifest with
104
the `[include:]` directive unless they are absolute paths.
109
Manifest Destiny gives tests as a list of dictionaries (in python
112
* path: full path to the test
113
* name: short name of the test; this is the (usually) relative path
114
specified in the section name
115
* here: the parent directory of the manifest
116
* manifest: the path to the manifest containing the test
118
This data corresponds to a one-line manifest:
120
[testToolbar/testBackForwardButtons.js]
122
If additional key, values were specified, they would be in this dict
125
Outside of the reserved keys, the remaining key, values
126
are up to convention to use. There is a (currently very minimal)
127
generic integration layer in ManifestDestiny for use of all harnesses,
128
`manifestparser.TestManifest`.
129
For instance, if the 'disabled' key is present, you can get the set of
130
tests without disabled (various other queries are doable as well).
132
Since the system is convention-based, the harnesses may do whatever
133
they want with the data. They may ignore it completely, they may use
134
the provided integration layer, or they may provide their own
135
integration layer. This should allow whatever sort of logic is
136
desired. For instance, if in yourtestharness you wanted to run only on
137
mondays for a certain class of tests:
140
for test in manifests.tests:
141
if 'runOnDay' in test:
142
if calendar.day_name[calendar.weekday(*datetime.datetime.now().timetuple()[:3])].lower() == test['runOnDay'].lower():
148
* the manifests allow you to specify test data
149
* the parser gives you this data
150
* you can use it however you want or process it further as you need
152
Tests are denoted by sections in an .ini file (see
153
http://hg.mozilla.org/automation/ManifestDestiny/file/tip/manifestdestiny/tests/mozmill-example.ini).
155
Additional manifest files may be included with an `[include:]` directive:
157
[include:path-to-additional-file.manifest]
159
The path to included files is relative to the current manifest.
161
The `[DEFAULT]` section contains variables that all tests inherit from.
163
Included files will inherit the top-level variables but may override
164
in their own `[DEFAULT]` section.
167
# ManifestDestiny Architecture
169
There is a two- or three-layered approach to the ManifestDestiny
170
architecture, depending on your needs:
172
1. ManifestParser: this is a generic parser for .ini manifests that
173
facilitates the `[include:]` logic and the inheritence of
174
metadata. Despite the internal variable being called `self.tests`
175
(an oversight), this layer has nothing in particular to do with tests.
177
2. TestManifest: this is a harness-agnostic integration layer that is
178
test-specific. TestManifest faciliates `skip-if` and `run-if` logic.
180
3. Optionally, a harness will have an integration layer than inherits
181
from TestManifest if more harness-specific customization is desired at
184
See the source code at https://github.com/mozilla/mozbase/tree/master/manifestdestiny
186
https://github.com/mozilla/mozbase/blob/master/manifestdestiny/manifestparser.py
192
A test harness will normally call `TestManifest.active_tests`:
194
def active_tests(self, exists=True, disabled=True, **tags):
196
The manifests are passed to the `__init__` or `read` methods with
197
appropriate arguments. `active_tests` then allows you to select the
200
- exists : return only existing tests
201
- disabled : whether to return disabled tests; if not these will be
202
filtered out; if True (the default), the `disabled` key of a
203
test's metadata will be present and will be set to the reason that a
205
- tags : keys and values to filter on (e.g. `os='linux'`)
207
`active_tests` looks for tests with `skip-if`
208
`run-if`. If the condition is or is not fulfilled,
209
respectively, the test is marked as disabled. For instance, if you
210
pass `**dict(os='linux')` as `**tags`, if a test contains a line
211
`skip-if = os == 'linux'` this test will be disabled, or
212
`run-if = os = 'win'` in which case the test will also be disabled. It
213
is up to the harness to pass in tags appropriate to its usage.
218
ManifestDestiny comes with a console script, `manifestparser create`, that
219
may be used to create a seed manifest structure from a directory of
220
files. Run `manifestparser help create` for usage information.
225
To copy tests and manifests from a source:
227
manifestparser [options] copy from_manifest to_directory -tag1 -tag2 --key1=value1 key2=value2 ...
232
To update the tests associated with with a manifest from a source
235
manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ...
240
Here is an example of how to create manifests for a directory tree and
241
update the tests listed in the manifests from an external source.
243
## Creating Manifests
245
Let's say you want to make a series of manifests for a given directory structure containing `.js` test files:
247
testing/mozmill/tests/firefox/
248
testing/mozmill/tests/firefox/testAwesomeBar/
249
testing/mozmill/tests/firefox/testPreferences/
250
testing/mozmill/tests/firefox/testPrivateBrowsing/
251
testing/mozmill/tests/firefox/testSessionStore/
252
testing/mozmill/tests/firefox/testTechnicalTools/
253
testing/mozmill/tests/firefox/testToolbar/
254
testing/mozmill/tests/firefox/restartTests
256
You can use `manifestparser create` to do this:
258
$ manifestparser help create
259
Usage: manifestparser.py [options] create directory <directory> <...>
261
create a manifest from a list of directories
264
-p PATTERN, --pattern=PATTERN
265
glob pattern for files
266
-i IGNORE, --ignore=IGNORE
267
directories to ignore
268
-w IN_PLACE, --in-place=IN_PLACE
269
Write .ini files in place; filename to write to
271
We only want `.js` files and we want to skip the `restartTests` directory.
272
We also want to write a manifest per directory, so I use the `--in-place`
273
option to write the manifests:
275
manifestparser create . -i restartTests -p '*.js' -w manifest.ini
277
This creates a manifest.ini per directory that we care about with the JS test files:
279
testing/mozmill/tests/firefox/manifest.ini
280
testing/mozmill/tests/firefox/testAwesomeBar/manifest.ini
281
testing/mozmill/tests/firefox/testPreferences/manifest.ini
282
testing/mozmill/tests/firefox/testPrivateBrowsing/manifest.ini
283
testing/mozmill/tests/firefox/testSessionStore/manifest.ini
284
testing/mozmill/tests/firefox/testTechnicalTools/manifest.ini
285
testing/mozmill/tests/firefox/testToolbar/manifest.ini
287
The top-level `manifest.ini` merely has `[include:]` references to the sub manifests:
289
[include:testAwesomeBar/manifest.ini]
290
[include:testPreferences/manifest.ini]
291
[include:testPrivateBrowsing/manifest.ini]
292
[include:testSessionStore/manifest.ini]
293
[include:testTechnicalTools/manifest.ini]
294
[include:testToolbar/manifest.ini]
296
Each sub-level manifest contains the (`.js`) test files relative to it.
298
## Updating the tests from manifests
300
You may need to update tests as given in manifests from a different source directory.
301
`manifestparser update` was made for just this purpose:
303
Usage: manifestparser [options] update manifest directory -tag1 -tag2 --key1=value1 --key2=value2 ...
305
update the tests as listed in a manifest from a directory
307
To update from a directory of tests in `~/mozmill/src/mozmill-tests/firefox/` run:
309
manifestparser update manifest.ini ~/mozmill/src/mozmill-tests/firefox/
314
ManifestDestiny includes a suite of tests:
316
https://github.com/mozilla/mozbase/tree/master/manifestdestiny/tests
318
`test_manifest.txt` is a doctest that may be helpful in figuring out
319
how to use the API. Tests are run via `python test.py`.
324
Please file any bugs or feature requests at
326
https://bugzilla.mozilla.org/enter_bug.cgi?product=Testing&component=ManifestParser
328
Or contact jhammel @mozilla.org or in #ateam on irc.mozilla.org
333
Run `manifestparser help` for usage information.
335
To create a manifest from a set of directories:
337
manifestparser [options] create directory <directory> <...> [create-options]
339
To output a manifest of tests:
341
manifestparser [options] write manifest <manifest> <...> -tag1 -tag2 --key1=value1 --key2=value2 ...
343
To copy tests and manifests from a source:
345
manifestparser [options] copy from_manifest to_manifest -tag1 -tag2 --key1=value1 key2=value2 ...
347
To update the tests associated with with a manifest from a source
350
manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ...
353
# Design Considerations
355
Contrary to some opinion, manifestparser.py and the associated .ini
356
format were not magically plucked from the sky but were descended upon
357
through several design considerations.
359
* test manifests should be ordered. While python 2.6 and greater has
360
a ConfigParser that can use an ordered dictionary, it is a
361
requirement that we support python 2.4 for the build + testing
362
environment. To that end, a `read_ini` function was implemented
363
in manifestparser.py that should be the equivalent of the .ini
364
dialect used by ConfigParser.
366
* the manifest format should be easily human readable/writable. While
367
there was initially some thought of using JSON, there was pushback
368
that JSON was not easily editable. An ideal manifest format would
369
degenerate to a line-separated list of files. While .ini format
370
requires an additional `[]` per line, and while there have been
371
complaints about this, hopefully this is good enough.
373
* python does not have an in-built YAML parser. Since it was
374
undesirable for manifestparser.py to have any dependencies, YAML was
375
dismissed as a format.
377
* we could have used a proprietary format but decided against it.
378
Everyone knows .ini and there are good tools to deal with it.
379
However, since read_ini is the only function that transforms a
380
manifest to a list of key, value pairs, while the implications for
381
changing the format impacts downstream code, doing so should be
382
programmatically simple.
384
* there should be a single file that may easily be
385
transported. Traditionally, test harnesses have lived in
386
mozilla-central. This is less true these days and it is increasingly
387
likely that more tests will not live in mozilla-central going
388
forward. So `manifestparser.py` should be highly consumable. To
389
this end, it is a single file, as appropriate to mozilla-central,
390
which is also a working python package deployed to PyPI for easy
394
# Developing ManifestDestiny
396
ManifestDestiny is developed and maintained by Mozilla's
397
[Automation and Testing Team](https://wiki.mozilla.org/Auto-tools).
398
The project page is located at
399
https://wiki.mozilla.org/Auto-tools/Projects/ManifestDestiny .
402
# Historical Reference
404
Date-ordered list of links about how manifests came to be where they are today::
406
* https://wiki.mozilla.org/Auto-tools/Projects/UniversalManifest
407
* http://alice.nodelman.net/blog/post/2010/05/
408
* http://alice.nodelman.net/blog/post/universal-manifest-for-unit-tests-a-proposal/
409
* https://elvis314.wordpress.com/2010/07/05/improving-personal-hygiene-by-adjusting-mochitests/
410
* https://elvis314.wordpress.com/2010/07/27/types-of-data-we-care-about-in-a-manifest/
411
* https://bugzilla.mozilla.org/show_bug.cgi?id=585106
412
* http://elvis314.wordpress.com/2011/05/20/converting-xpcshell-from-listing-directories-to-a-manifest/
413
* https://bugzilla.mozilla.org/show_bug.cgi?id=616999
414
* https://wiki.mozilla.org/Auto-tools/Projects/ManifestDestiny
415
* https://developer.mozilla.org/en/Writing_xpcshell-based_unit_tests#Adding_your_tests_to_the_xpcshell_manifest