testing/mozbase/docs/manifestdestiny.rst

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:7a30975133b4
1 Managing lists of tests
2 =======================
3
4 We don't always want to run all tests, all the time. Sometimes a test
5 may be broken, in other cases we only want to run a test on a specific
6 platform or build of Mozilla. To handle these cases (and more), we
7 created a python library to create and use test "manifests", which
8 codify this information.
9
10 :mod:`manifestdestiny` --- Create and manage test manifests
11 -----------------------------------------------------------
12
13 manifestdestiny lets you easily create and use test manifests, to
14 control which tests are run under what circumstances.
15
16 What ManifestDestiny gives you:
17
18 * manifests are ordered lists of tests
19 * tests may have an arbitrary number of key, value pairs
20 * the parser returns an ordered list of test data structures, which
21 are just dicts with some keys. For example, a test with no
22 user-specified metadata looks like this:
23
24 .. code-block:: text
25
26 [{'expected': 'pass',
27 'path': '/home/mozilla/mozmill/src/ManifestDestiny/manifestdestiny/tests/testToolbar/testBackForwardButtons.js',
28 'relpath': 'testToolbar/testBackForwardButtons.js',
29 'name': 'testBackForwardButtons.js',
30 'here': '/home/mozilla/mozmill/src/ManifestDestiny/manifestdestiny/tests',
31 'manifest': '/home/mozilla/mozmill/src/ManifestDestiny/manifestdestiny/tests/manifest.ini',}]
32
33 The keys displayed here (path, relpath, name, here, and manifest) are
34 reserved keys for ManifestDestiny and any consuming APIs. You can add
35 additional key, value metadata to each test.
36
37 Why have test manifests?
38 ````````````````````````
39
40 It is desirable to have a unified format for test manifests for testing
41 [mozilla-central](http://hg.mozilla.org/mozilla-central), etc.
42
43 * 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
44 * 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.
45 * 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.
46
47
48 Most Mozilla test harnesses work by crawling a directory structure.
49 While this is straight-forward, manifests offer several practical
50 advantages:
51
52 * ability to turn a test off easily: if a test is broken on m-c
53 currently, the only way to turn it off, generally speaking, is just
54 removing the test. Often this is undesirable, as if the test should
55 be dismissed because other people want to land and it can't be
56 investigated in real time (is it a failure? is the test bad? is no
57 one around that knows the test?), then backing out a test is at best
58 problematic. With a manifest, a test may be disabled without
59 removing it from the tree and a bug filed with the appropriate
60 reason:
61
62 .. code-block:: text
63
64 [test_broken.js]
65 disabled = https://bugzilla.mozilla.org/show_bug.cgi?id=123456
66
67 * ability to run different (subsets of) tests on different
68 platforms. Traditionally, we've done a bit of magic or had the test
69 know what platform it would or would not run on. With manifests, you
70 can mark what platforms a test will or will not run on and change
71 these without changing the test.
72
73 .. code-block:: text
74
75 [test_works_on_windows_only.js]
76 run-if = os == 'win'
77
78 * ability to markup tests with metadata. We have a large, complicated,
79 and always changing infrastructure. key, value metadata may be used
80 as an annotation to a test and appropriately curated and mined. For
81 instance, we could mark certain tests as randomorange with a bug
82 number, if it were desirable.
83
84 * ability to have sane and well-defined test-runs. You can keep
85 different manifests for different test runs and ``[include:]``
86 (sub)manifests as appropriate to your needs.
87
88 Manifest Format
89 ````````
90
91 Manifests are .ini file with the section names denoting the path
92 relative to the manifest:
93
94 .. code-block:: text
95
96 [foo.js]
97 [bar.js]
98 [fleem.js]
99
100 The sections are read in order. In addition, tests may include
101 arbitrary key, value metadata to be used by the harness. You may also
102 have a `[DEFAULT]` section that will give key, value pairs that will
103 be inherited by each test unless overridden:
104
105 .. code-block:: text
106
107 [DEFAULT]
108 type = restart
109
110 [lilies.js]
111 color = white
112
113 [daffodils.js]
114 color = yellow
115 type = other
116 # override type from DEFAULT
117
118 [roses.js]
119 color = red
120
121 You can also include other manifests:
122
123 .. code-block:: text
124
125 [include:subdir/anothermanifest.ini]
126
127 Manifests are included relative to the directory of the manifest with
128 the `[include:]` directive unless they are absolute paths.
129
130 By default you can use both '#' and ';' as comment characters. Comments
131 must start on a new line, inline comments are not supported.
132
133 .. code-block:: text
134
135 [roses.js]
136 # a valid comment
137 ; another valid comment
138 color = red # not a valid comment
139
140 In the example above, the 'color' property will have the value 'red #
141 not a valid comment'.
142
143 Manifest Conditional Expressions
144 ````````````````````````````````
145 The conditional expressions used in manifests are parsed using the *ExpressionParser* class.
146
147 .. autoclass:: manifestparser.ExpressionParser
148
149 Consumers of this module are expected to pass in a value dictionary
150 for evaluating conditional expressions. A common pattern is to pass
151 the dictionary from the :mod:`mozinfo` module.
152
153 Data
154 ````
155
156 Manifest Destiny gives tests as a list of dictionaries (in python
157 terms).
158
159 * path: full path to the test
160 * relpath: relative path starting from the root manifest location
161 * name: file name of the test
162 * here: the parent directory of the manifest
163 * manifest: the path to the manifest containing the test
164
165 This data corresponds to a one-line manifest:
166
167 .. code-block:: text
168
169 [testToolbar/testBackForwardButtons.js]
170
171 If additional key, values were specified, they would be in this dict
172 as well.
173
174 Outside of the reserved keys, the remaining key, values
175 are up to convention to use. There is a (currently very minimal)
176 generic integration layer in ManifestDestiny for use of all harnesses,
177 `manifestparser.TestManifest`.
178 For instance, if the 'disabled' key is present, you can get the set of
179 tests without disabled (various other queries are doable as well).
180
181 Since the system is convention-based, the harnesses may do whatever
182 they want with the data. They may ignore it completely, they may use
183 the provided integration layer, or they may provide their own
184 integration layer. This should allow whatever sort of logic is
185 desired. For instance, if in yourtestharness you wanted to run only on
186 mondays for a certain class of tests:
187
188 .. code-block:: text
189
190 tests = []
191 for test in manifests.tests:
192 if 'runOnDay' in test:
193 if calendar.day_name[calendar.weekday(*datetime.datetime.now().timetuple()[:3])].lower() == test['runOnDay'].lower():
194 tests.append(test)
195 else:
196 tests.append(test)
197
198 To recap:
199 * the manifests allow you to specify test data
200 * the parser gives you this data
201 * you can use it however you want or process it further as you need
202
203 Tests are denoted by sections in an .ini file (see
204 http://hg.mozilla.org/automation/ManifestDestiny/file/tip/manifestdestiny/tests/mozmill-example.ini).
205
206 Additional manifest files may be included with an `[include:]` directive:
207
208 .. code-block:: text
209
210 [include:path-to-additional-file.manifest]
211
212 The path to included files is relative to the current manifest.
213
214 The `[DEFAULT]` section contains variables that all tests inherit from.
215
216 Included files will inherit the top-level variables but may override
217 in their own `[DEFAULT]` section.
218
219 ManifestDestiny Architecture
220 ````````````````````````````
221
222 There is a two- or three-layered approach to the ManifestDestiny
223 architecture, depending on your needs:
224
225 1. ManifestParser: this is a generic parser for .ini manifests that
226 facilitates the `[include:]` logic and the inheritence of
227 metadata. Despite the internal variable being called `self.tests`
228 (an oversight), this layer has nothing in particular to do with tests.
229
230 2. TestManifest: this is a harness-agnostic integration layer that is
231 test-specific. TestManifest faciliates `skip-if` and `run-if` logic.
232
233 3. Optionally, a harness will have an integration layer than inherits
234 from TestManifest if more harness-specific customization is desired at
235 the manifest level.
236
237 See the source code at https://github.com/mozilla/mozbase/tree/master/manifestdestiny
238 and
239 https://github.com/mozilla/mozbase/blob/master/manifestdestiny/manifestparser.py
240 in particular.
241
242 Using Manifests
243 ```````````````
244
245 A test harness will normally call `TestManifest.active_tests`:
246
247 .. code-block:: text
248
249 def active_tests(self, exists=True, disabled=True, **tags):
250
251 The manifests are passed to the `__init__` or `read` methods with
252 appropriate arguments. `active_tests` then allows you to select the
253 tests you want:
254
255 - exists : return only existing tests
256 - disabled : whether to return disabled tests; if not these will be
257 filtered out; if True (the default), the `disabled` key of a
258 test's metadata will be present and will be set to the reason that a
259 test is disabled
260 - tags : keys and values to filter on (e.g. `os='linux'`)
261
262 `active_tests` looks for tests with `skip-if`
263 `run-if`. If the condition is or is not fulfilled,
264 respectively, the test is marked as disabled. For instance, if you
265 pass `**dict(os='linux')` as `**tags`, if a test contains a line
266 `skip-if = os == 'linux'` this test will be disabled, or
267 `run-if = os = 'win'` in which case the test will also be disabled. It
268 is up to the harness to pass in tags appropriate to its usage.
269
270 Creating Manifests
271 ``````````````````
272
273 ManifestDestiny comes with a console script, `manifestparser create`, that
274 may be used to create a seed manifest structure from a directory of
275 files. Run `manifestparser help create` for usage information.
276
277 Copying Manifests
278 `````````````````
279
280 To copy tests and manifests from a source:
281
282 .. code-block:: text
283
284 manifestparser [options] copy from_manifest to_directory -tag1 -tag2 `key1=value1 key2=value2 ...
285
286 Updating Tests
287 ``````````````
288
289 To update the tests associated with with a manifest from a source
290 directory:
291
292 .. code-block:: text
293
294 manifestparser [options] update manifest from_directory -tag1 -tag2 `key1=value1 `key2=value2 ...
295
296 Usage example
297 `````````````
298
299 Here is an example of how to create manifests for a directory tree and
300 update the tests listed in the manifests from an external source.
301
302 Creating Manifests
303 ``````````````````
304
305 Let's say you want to make a series of manifests for a given directory structure containing `.js` test files:
306
307 .. code-block:: text
308
309 testing/mozmill/tests/firefox/
310 testing/mozmill/tests/firefox/testAwesomeBar/
311 testing/mozmill/tests/firefox/testPreferences/
312 testing/mozmill/tests/firefox/testPrivateBrowsing/
313 testing/mozmill/tests/firefox/testSessionStore/
314 testing/mozmill/tests/firefox/testTechnicalTools/
315 testing/mozmill/tests/firefox/testToolbar/
316 testing/mozmill/tests/firefox/restartTests
317
318 You can use `manifestparser create` to do this:
319
320 .. code-block:: text
321
322 $ manifestparser help create
323 Usage: manifestparser.py [options] create directory <directory> <...>
324
325 create a manifest from a list of directories
326
327 Options:
328 -p PATTERN, `pattern=PATTERN
329 glob pattern for files
330 -i IGNORE, `ignore=IGNORE
331 directories to ignore
332 -w IN_PLACE, --in-place=IN_PLACE
333 Write .ini files in place; filename to write to
334
335 We only want `.js` files and we want to skip the `restartTests` directory.
336 We also want to write a manifest per directory, so I use the `--in-place`
337 option to write the manifests:
338
339 .. code-block:: text
340
341 manifestparser create . -i restartTests -p '*.js' -w manifest.ini
342
343 This creates a manifest.ini per directory that we care about with the JS test files:
344
345 .. code-block:: text
346
347 testing/mozmill/tests/firefox/manifest.ini
348 testing/mozmill/tests/firefox/testAwesomeBar/manifest.ini
349 testing/mozmill/tests/firefox/testPreferences/manifest.ini
350 testing/mozmill/tests/firefox/testPrivateBrowsing/manifest.ini
351 testing/mozmill/tests/firefox/testSessionStore/manifest.ini
352 testing/mozmill/tests/firefox/testTechnicalTools/manifest.ini
353 testing/mozmill/tests/firefox/testToolbar/manifest.ini
354
355 The top-level `manifest.ini` merely has `[include:]` references to the sub manifests:
356
357 .. code-block:: text
358
359 [include:testAwesomeBar/manifest.ini]
360 [include:testPreferences/manifest.ini]
361 [include:testPrivateBrowsing/manifest.ini]
362 [include:testSessionStore/manifest.ini]
363 [include:testTechnicalTools/manifest.ini]
364 [include:testToolbar/manifest.ini]
365
366 Each sub-level manifest contains the (`.js`) test files relative to it.
367
368 Updating the tests from manifests
369 `````````````````````````````````
370
371 You may need to update tests as given in manifests from a different source directory.
372 `manifestparser update` was made for just this purpose:
373
374 .. code-block:: text
375
376 Usage: manifestparser [options] update manifest directory -tag1 -tag2 `key1=value1 --key2=value2 ...
377
378 update the tests as listed in a manifest from a directory
379
380 To update from a directory of tests in `~/mozmill/src/mozmill-tests/firefox/` run:
381
382 .. code-block:: text
383
384 manifestparser update manifest.ini ~/mozmill/src/mozmill-tests/firefox/
385
386 Tests
387 `````
388
389 ManifestDestiny includes a suite of tests:
390
391 https://github.com/mozilla/mozbase/tree/master/manifestdestiny/tests
392
393 `test_manifest.txt` is a doctest that may be helpful in figuring out
394 how to use the API. Tests are run via `python test.py`.
395
396 Bugs
397 ````
398
399 Please file any bugs or feature requests at
400
401 https://bugzilla.mozilla.org/enter_bug.cgi?product=Testing&component=ManifestParser
402
403 Or contact jhammel @mozilla.org or in #ateam on irc.mozilla.org
404
405 CLI
406 ```
407
408 Run `manifestparser help` for usage information.
409
410 To create a manifest from a set of directories:
411
412 .. code-block:: text
413
414 manifestparser [options] create directory <directory> <...> [create-options]
415
416 To output a manifest of tests:
417
418 .. code-block:: text
419
420 manifestparser [options] write manifest <manifest> <...> -tag1 -tag2 --key1=value1 --key2=value2 ...
421
422 To copy tests and manifests from a source:
423
424 .. code-block:: text
425
426 manifestparser [options] copy from_manifest to_manifest -tag1 -tag2 `key1=value1 key2=value2 ...
427
428 To update the tests associated with with a manifest from a source
429 directory:
430
431 .. code-block:: text
432
433 manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ...
434
435 Design Considerations
436 `````````````````````
437
438 Contrary to some opinion, manifestparser.py and the associated .ini
439 format were not magically plucked from the sky but were descended upon
440 through several design considerations.
441
442 * test manifests should be ordered. While python 2.6 and greater has
443 a ConfigParser that can use an ordered dictionary, it is a
444 requirement that we support python 2.4 for the build + testing
445 environment. To that end, a `read_ini` function was implemented
446 in manifestparser.py that should be the equivalent of the .ini
447 dialect used by ConfigParser.
448
449 * the manifest format should be easily human readable/writable. While
450 there was initially some thought of using JSON, there was pushback
451 that JSON was not easily editable. An ideal manifest format would
452 degenerate to a line-separated list of files. While .ini format
453 requires an additional `[]` per line, and while there have been
454 complaints about this, hopefully this is good enough.
455
456 * python does not have an in-built YAML parser. Since it was
457 undesirable for manifestparser.py to have any dependencies, YAML was
458 dismissed as a format.
459
460 * we could have used a proprietary format but decided against it.
461 Everyone knows .ini and there are good tools to deal with it.
462 However, since read_ini is the only function that transforms a
463 manifest to a list of key, value pairs, while the implications for
464 changing the format impacts downstream code, doing so should be
465 programmatically simple.
466
467 * there should be a single file that may easily be
468 transported. Traditionally, test harnesses have lived in
469 mozilla-central. This is less true these days and it is increasingly
470 likely that more tests will not live in mozilla-central going
471 forward. So `manifestparser.py` should be highly consumable. To
472 this end, it is a single file, as appropriate to mozilla-central,
473 which is also a working python package deployed to PyPI for easy
474 installation.
475
476 Historical Reference
477 ````````````````````
478
479 Date-ordered list of links about how manifests came to be where they are today::
480
481 * https://wiki.mozilla.org/Auto-tools/Projects/UniversalManifest
482 * http://alice.nodelman.net/blog/post/2010/05/
483 * http://alice.nodelman.net/blog/post/universal-manifest-for-unit-tests-a-proposal/
484 * https://elvis314.wordpress.com/2010/07/05/improving-personal-hygiene-by-adjusting-mochitests/
485 * https://elvis314.wordpress.com/2010/07/27/types-of-data-we-care-about-in-a-manifest/
486 * https://bugzilla.mozilla.org/show_bug.cgi?id=585106
487 * http://elvis314.wordpress.com/2011/05/20/converting-xpcshell-from-listing-directories-to-a-manifest/
488 * https://bugzilla.mozilla.org/show_bug.cgi?id=616999
489 * https://developer.mozilla.org/en/Writing_xpcshell-based_unit_tests#Adding_your_tests_to_the_xpcshell_manifest

mercurial