testing/mozbase/docs/manifestdestiny.rst

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial