|
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 |