manifestparser.rst (24021B)
1 Managing lists of tests 2 ======================= 3 4 .. py:currentmodule:: manifestparser 5 6 We don't always want to run all tests, all the time. Sometimes a test 7 may be broken, in other cases we only want to run a test on a specific 8 platform or build of Mozilla. To handle these cases (and more), we 9 created a python library to create and use test "manifests", which 10 codify this information. 11 12 Update for August 2023: Transition to TOML for manifestparser 13 ````````````````````````````````````````````````````````````` 14 15 As of August 2023, manifestparser will be transitioning from INI format 16 configuration files to TOML. The new TOML format will better support 17 future continuous integration automation and has a much more 18 precise syntax (FFI see `Bug 1821199 <https://bugzilla.mozilla.org/show_bug.cgi?id=1821199>`_). 19 During the migration period both ``*.ini`` files and 20 ``*.toml`` files will be supported. If an INI config file is specified 21 (e.g. in ``moz.build``) and a TOML file is present, the TOML file will be 22 used. 23 24 :mod:`manifestparser` --- Create and manage test manifests 25 ----------------------------------------------------------- 26 27 manifestparser lets you easily create and use test manifests, to 28 control which tests are run under what circumstances. 29 30 What manifestparser gives you: 31 32 * manifests are ordered lists of tests 33 * tests may have an arbitrary number of key, value pairs 34 * the parser returns an ordered list of test data structures, which 35 are just dicts with some keys. For example, a test with no 36 user-specified metadata looks like this: 37 38 .. code-block:: text 39 40 [{'expected': 'pass', 41 'path': '/home/mozilla/mozmill/src/manifestparser/manifestparser/tests/testToolbar/testBackForwardButtons.js', 42 'relpath': 'testToolbar/testBackForwardButtons.js', 43 'name': 'testBackForwardButtons.js', 44 'here': '/home/mozilla/mozmill/src/manifestparser/manifestparser/tests', 45 'manifest': '/home/mozilla/mozmill/src/manifestparser/manifestparser/tests/manifest.toml',}] 46 47 The keys displayed here (path, relpath, name, here, and manifest) are 48 reserved keys for manifestparser and any consuming APIs. You can add 49 additional key, value metadata to each test. 50 51 Why have test manifests? 52 ```````````````````````` 53 54 It is desirable to have a unified format for test manifests for testing 55 `mozilla-central <http://hg.mozilla.org/mozilla-central>`_, etc. 56 57 * 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 58 * 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. 59 * 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. 60 61 62 Most Mozilla test harnesses work by crawling a directory structure. 63 While this is straight-forward, manifests offer several practical 64 advantages: 65 66 * ability to turn a test off easily: if a test is broken on m-c 67 currently, the only way to turn it off, generally speaking, is just 68 removing the test. Often this is undesirable, as if the test should 69 be dismissed because other people want to land and it can't be 70 investigated in real time (is it a failure? is the test bad? is no 71 one around that knows the test?), then backing out a test is at best 72 problematic. With a manifest, a test may be disabled without 73 removing it from the tree and a bug filed with the appropriate 74 reason: 75 76 .. code-block:: text 77 78 ["test_broken.js"] 79 disabled = "https://bugzilla.mozilla.org/show_bug.cgi?id=123456" 80 81 * ability to run different (subsets of) tests on different 82 platforms. Traditionally, we've done a bit of magic or had the test 83 know what platform it would or would not run on. With manifests, you 84 can mark what platforms a test will or will not run on and change 85 these without changing the test. 86 87 .. code-block:: text 88 89 ["test_works_on_windows_only.js"] 90 skip-if = ["os != 'win'"] 91 92 * ability to markup tests with metadata. We have a large, complicated, 93 and always changing infrastructure. key, value metadata may be used 94 as an annotation to a test and appropriately curated and mined. For 95 instance, we could mark certain tests as randomorange with a bug 96 number, if it were desirable. 97 98 * ability to have sane and well-defined test-runs. You can keep 99 different manifests for different test runs and ``["include:FILENAME.toml"]`` 100 (sub)manifests as appropriate to your needs. 101 102 Manifest Format 103 ``````````````` 104 105 Manifests are ``*.toml`` (formerly ``*.ini``) files with the section names denoting the path 106 relative to the manifest: 107 108 .. code-block:: text 109 110 ["foo.js"] 111 ["bar.js"] 112 ["fleem.js"] 113 114 The sections are read in order. In addition, tests may include 115 arbitrary key, value metadata to be used by the harness. You may also 116 have a `[DEFAULT]` section that will give key, value pairs that will 117 be inherited by each test unless overridden: 118 119 .. code-block:: text 120 121 [DEFAULT] 122 type = "restart" 123 124 ["lilies.js"] 125 color = "white" 126 127 ["daffodils.js"] 128 color = "yellow" 129 type = "other" 130 # override type from DEFAULT 131 132 ["roses.js"] 133 color = "red" 134 135 You can also include other manifests: 136 137 .. code-block:: text 138 139 ["include:subdir/anothermanifest.toml"] 140 141 And reference parent manifests to inherit keys and values from the DEFAULT 142 section, without adding possible included tests. 143 144 .. code-block:: text 145 146 ["parent:../manifest.toml"] 147 148 Manifests are included relative to the directory of the manifest with 149 the `[include:]` directive unless they are absolute paths. 150 151 By default you can use '#' as a comment character. Comments can start a 152 new line, or be inline. 153 154 .. code-block:: text 155 156 ["roses.js"] 157 # a valid comment 158 color = "red" # another valid comment 159 160 Because in TOML all values must be quoted there is no risk of an anchor in 161 an URL being interpreted as a comment. 162 163 .. code-block:: text 164 165 ["test1.js"] 166 url = "https://foo.com/bar#baz" # Bug 1234 167 168 169 Manifest Conditional Expressions 170 ```````````````````````````````` 171 The conditional expressions used in manifests are parsed using the *ExpressionParser* class. 172 173 .. autoclass:: manifestparser.ExpressionParser 174 175 Consumers of this module are expected to pass in a value dictionary 176 for evaluating conditional expressions. A common pattern is to pass 177 the dictionary from the :mod:`mozinfo` module. 178 179 Data 180 ```` 181 182 Manifest Destiny gives tests as a list of dictionaries (in python 183 terms). 184 185 * path: full path to the test 186 * relpath: relative path starting from the root directory. The root directory 187 is typically the location of the root manifest, or the source 188 repository. It can be specified at runtime by passing in `rootdir` 189 to `TestManifest`. Defaults to the directory containing the test's 190 ancestor manifest. 191 * name: file name of the test 192 * here: the parent directory of the manifest 193 * manifest: the path to the manifest containing the test 194 195 This data corresponds to a one-line manifest: 196 197 .. code-block:: text 198 199 ["testToolbar/testBackForwardButtons.js"] 200 201 If additional key, values were specified, they would be in this dict 202 as well. 203 204 Outside of the reserved keys, the remaining key, values 205 are up to convention to use. There is a (currently very minimal) 206 generic integration layer in manifestparser for use of all harnesses, 207 `manifestparser.TestManifest`. 208 For instance, if the 'disabled' key is present, you can get the set of 209 tests without disabled (various other queries are doable as well). 210 211 Since the system is convention-based, the harnesses may do whatever 212 they want with the data. They may ignore it completely, they may use 213 the provided integration layer, or they may provide their own 214 integration layer. This should allow whatever sort of logic is 215 desired. For instance, if in yourtestharness you wanted to run only on 216 mondays for a certain class of tests: 217 218 .. code-block:: text 219 220 tests = [] 221 for test in manifests.tests: 222 if 'runOnDay' in test: 223 if calendar.day_name[calendar.weekday(*datetime.datetime.now().timetuple()[:3])].lower() == test['runOnDay'].lower(): 224 tests.append(test) 225 else: 226 tests.append(test) 227 228 To recap: 229 230 * the manifests allow you to specify test data 231 * the parser gives you this data 232 * you can use it however you want or process it further as you need 233 234 Tests are denoted by sections in an ``*.toml`` file (see 235 https://searchfox.org/mozilla-central/source/testing/mozbase/manifestparser/tests/manifest.toml 236 ). 237 238 Additional manifest files may be included with an `[include:]` directive: 239 240 .. code-block:: text 241 242 ["include:path-to-additional-file-manifest.toml"] 243 244 The path to included files is relative to the current manifest. 245 246 The `[DEFAULT]` section contains variables that all tests inherit from. 247 248 Included files will inherit the top-level variables but may override 249 in their own `[DEFAULT]` section. 250 251 manifestparser Architecture 252 ```````````````````````````` 253 254 There is a two- or three-layered approach to the manifestparser 255 architecture, depending on your needs: 256 257 1. ManifestParser: this is a generic parser for ``*.toml`` manifests that 258 facilitates the `[include:]` logic and the inheritance of 259 metadata. Despite the internal variable being called `self.tests` 260 (an oversight), this layer has nothing in particular to do with tests. 261 262 2. TestManifest: this is a harness-agnostic integration layer that is 263 test-specific. TestManifest facilitates `skip-if` logic. 264 265 3. Optionally, a harness will have an integration layer than inherits 266 from TestManifest if more harness-specific customization is desired at 267 the manifest level. 268 269 See the source code at 270 https://searchfox.org/mozilla-central/source/testing/mozbase/manifestparser 271 . 272 273 Filtering Manifests 274 ``````````````````` 275 276 After creating a `TestManifest` object, all manifest files are read and a list 277 of test objects can be accessed via `TestManifest.tests`. However this list contains 278 all test objects, whether they should be run or not. Normally they need to be 279 filtered down only to the set of tests that should be run by the test harness. 280 281 To do this, a test harness can call `TestManifest.active_tests`: 282 283 .. code-block:: python 284 285 tests = manifest.active_tests(exists=True, disabled=True, **tags) 286 287 By default, `active_tests` runs the filters found in 288 :attr:`~.DEFAULT_FILTERS`. It also accepts two convenience arguments: 289 290 1. `exists`: if True (default), filter out tests that do not exist on the local file system. 291 2. `disabled`: if True (default), do not filter out tests containing the 'disabled' key 292 (which can be set by `skip-if` manually). 293 294 This works for simple cases, but there are other built-in filters, or even custom filters 295 that can be applied to the `TestManifest`. To do so, add the filter to `TestManifest.filters`: 296 297 .. code-block:: python 298 299 from manifestparser.filters import subsuite 300 import mozinfo 301 302 filters = [subsuite('devtools')] 303 tests = manifest.active_tests(filters=filters, **mozinfo.info) 304 305 .. automodule:: manifestparser.filters 306 :members: 307 :exclude-members: filterlist,InstanceFilter,DEFAULT_FILTERS 308 309 .. rstcheck: ignore-directives=autodata 310 .. autodata:: manifestparser.filters.DEFAULT_FILTERS 311 :annotation: 312 313 For example, suppose we want to introduce a new key called `timeout-if` that adds a 314 'timeout' property to a test if a certain condition is True. The syntax in the manifest 315 files will look like this: 316 317 .. code-block:: text 318 319 ["test_foo.py"] 320 timeout-if = ["300, os == 'win'"] 321 322 The value is <timeout>, <condition> where condition is the same format as the one in 323 `skip-if`. In the above case, if os == 'win', a timeout of 300 seconds will be 324 applied. Otherwise, no timeout will be applied. All we need to do is define the filter 325 and add it: 326 327 .. code-block:: python 328 329 from manifestparser.expression import parse 330 import mozinfo 331 332 def timeout_if(tests, values): 333 for test in tests: 334 if 'timeout-if' in test: 335 timeout, condition = test['timeout-if'].split(',', 1) 336 if parse(condition, **values): 337 test['timeout'] = timeout 338 yield test 339 340 tests = manifest.active_tests(filters=[timeout_if], **mozinfo.info) 341 342 343 CLI 344 ``` 345 346 **NOTE:** *The manifestparser CLI is currently being updated to support TOML.* 347 348 Run `manifestparser help` for usage information. 349 350 To create a manifest from a set of directories: 351 352 .. code-block:: text 353 354 manifestparser [options] create directory <directory> <...> [create-options] 355 356 To output a manifest of tests: 357 358 .. code-block:: text 359 360 manifestparser [options] write manifest <manifest> <...> -tag1 -tag2 --key1=value1 --key2=value2 ... 361 362 To copy tests and manifests from a source: 363 364 .. code-block:: text 365 366 manifestparser [options] copy from_manifest to_manifest -tag1 -tag2 `key1=value1 key2=value2 ... 367 368 To update the tests associated with a manifest from a source 369 directory: 370 371 .. code-block:: text 372 373 manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ... 374 375 Creating Manifests 376 `````````````````` 377 378 manifestparser comes with a console script, `manifestparser create`, that 379 may be used to create a seed manifest structure from a directory of 380 files. Run `manifestparser help create` for usage information. 381 382 Copying Manifests 383 ````````````````` 384 385 To copy tests and manifests from a source: 386 387 .. code-block:: text 388 389 manifestparser [options] copy from_manifest to_directory -tag1 -tag2 `key1=value1 key2=value2 ... 390 391 Updating Tests 392 `````````````` 393 394 To update the tests associated with a manifest from a source 395 directory: 396 397 .. code-block:: text 398 399 manifestparser [options] update manifest from_directory -tag1 -tag2 `key1=value1 `key2=value2 ... 400 401 Usage example 402 ````````````` 403 404 Here is an example of how to create manifests for a directory tree and 405 update the tests listed in the manifests from an external source. 406 407 Creating Manifests 408 `````````````````` 409 410 Let's say you want to make a series of manifests for a given directory structure containing `.js` test files: 411 412 .. code-block:: text 413 414 testing/mozmill/tests/firefox/ 415 testing/mozmill/tests/firefox/testAwesomeBar/ 416 testing/mozmill/tests/firefox/testPreferences/ 417 testing/mozmill/tests/firefox/testPrivateBrowsing/ 418 testing/mozmill/tests/firefox/testSessionStore/ 419 testing/mozmill/tests/firefox/testTechnicalTools/ 420 testing/mozmill/tests/firefox/testToolbar/ 421 testing/mozmill/tests/firefox/restartTests 422 423 You can use `manifestparser create` to do this: 424 425 .. code-block:: text 426 427 $ manifestparser help create 428 Usage: manifestparser.py [options] create directory <directory> <...> 429 430 create a manifest from a list of directories 431 432 Options: 433 -p PATTERN, `pattern=PATTERN 434 glob pattern for files 435 -i IGNORE, `ignore=IGNORE 436 directories to ignore 437 -w IN_PLACE, --in-place=IN_PLACE 438 Write .ini files in place; filename to write to 439 440 We only want `.js` files and we want to skip the `restartTests` directory. 441 We also want to write a manifest per directory, so I use the `--in-place` 442 option to write the manifests: 443 444 .. code-block:: text 445 446 manifestparser create . -i restartTests -p '*.js' -w manifest.ini 447 448 This creates a manifest.ini per directory that we care about with the JS test files: 449 450 .. code-block:: text 451 452 testing/mozmill/tests/firefox/manifest.ini 453 testing/mozmill/tests/firefox/testAwesomeBar/manifest.ini 454 testing/mozmill/tests/firefox/testPreferences/manifest.ini 455 testing/mozmill/tests/firefox/testPrivateBrowsing/manifest.ini 456 testing/mozmill/tests/firefox/testSessionStore/manifest.ini 457 testing/mozmill/tests/firefox/testTechnicalTools/manifest.ini 458 testing/mozmill/tests/firefox/testToolbar/manifest.ini 459 460 The top-level `manifest.ini` merely has `[include:]` references to the sub manifests: 461 462 .. code-block:: text 463 464 [include:testAwesomeBar/manifest.ini] 465 [include:testPreferences/manifest.ini] 466 [include:testPrivateBrowsing/manifest.ini] 467 [include:testSessionStore/manifest.ini] 468 [include:testTechnicalTools/manifest.ini] 469 [include:testToolbar/manifest.ini] 470 471 Each sub-level manifest contains the (`.js`) test files relative to it. 472 473 Updating the tests from manifests 474 ````````````````````````````````` 475 476 You may need to update tests as given in manifests from a different source directory. 477 `manifestparser update` was made for just this purpose: 478 479 .. code-block:: text 480 481 Usage: manifestparser [options] update manifest directory -tag1 -tag2 `key1=value1 --key2=value2 ... 482 483 update the tests as listed in a manifest from a directory 484 485 To update from a directory of tests in `~/mozmill/src/mozmill-tests/firefox/` run: 486 487 .. code-block:: text 488 489 manifestparser update manifest.ini ~/mozmill/src/mozmill-tests/firefox/ 490 491 Tests 492 ````` 493 494 manifestparser includes a suite of tests. 495 496 `test_manifest.txt` is a doctest that may be helpful in figuring out 497 how to use the API. Tests are run via `mach python-test testing/mozbase/manifestparser`. 498 499 Using mach manifest skip-fails 500 `````````````````````````````` 501 502 The first of the ``mach manifest`` subcommands is ``skip-fails``. This command 503 can be used to *automatically* edit manifests to skip tests that are failing 504 as well as file the corresponding bugs for the failures. This is particularly 505 useful when "greening up" a new platform. 506 507 You may verify the proposed changes from ``skip-fails`` output and examine 508 any local manifest changes with ``hg status``. 509 510 Be careful, this command will create bugs in Bugzilla. To avoid this, add 511 `--bugzilla disable` to your command. 512 513 Here is the usage: 514 515 .. code-block:: text 516 517 $ ./mach manifest skip-fails --help 518 usage: mach [global arguments] manifest skip-fails [command arguments] 519 520 Sub Command Arguments: 521 try_url Treeherder URL for try (please use quotes) 522 -b BUGZILLA, --bugzilla BUGZILLA 523 Bugzilla instance (or disable) 524 -m META_BUG_ID, --meta-bug-id META_BUG_ID 525 Meta Bug id 526 -s, --turbo Skip all secondary failures 527 -t SAVE_TASKS, --save-tasks SAVE_TASKS 528 Save tasks to file 529 -T USE_TASKS, --use-tasks USE_TASKS 530 Use tasks from file 531 -f SAVE_FAILURES, --save-failures SAVE_FAILURES 532 Save failures to file 533 -F USE_FAILURES, --use-failures USE_FAILURES 534 Use failures from file 535 -M MAX_FAILURES, --max-failures MAX_FAILURES 536 Maximum number of failures to skip (-1 == no limit) 537 -v, --verbose Verbose mode 538 -d, --dry-run Determine manifest changes, but do not write them 539 $ 540 541 ``try_url`` --- Treeherder URL 542 ------------------------------ 543 This is the url (usually in single quotes) from running tests in try, for example: 544 'https://treeherder.mozilla.org/jobs?repo=try&revision=babc28f495ee8af2e4f059e9cbd23e84efab7d0d' 545 546 ``--bugzilla BUGZILLA`` --- Bugzilla instance 547 --------------------------------------------- 548 549 By default the Bugzilla instance is ``bugzilla.allizom.org``, but you may set it on the command 550 line to another value such as ``bugzilla.mozilla.org`` (or by setting the environment variable 551 ``BUGZILLA``). 552 553 ``--meta-bug-id META_BUG_ID`` --- Meta Bug id 554 --------------------------------------------- 555 556 Any new bugs that are filed will block (be dependents of) this "meta" bug (optional). 557 558 ``--turbo`` --- Skip all secondary failures 559 ------------------------------------------- 560 561 The default ``skip-fails`` behavior is to skip only the first failure (for a given label) for each test. 562 In `turbo` mode, all failures for this manifest + label will skipped. 563 564 ``--save-tasks SAVE_TASKS`` --- Save tasks to file 565 -------------------------------------------------- 566 567 This feature is primarily for ``skip-fails`` development and debugging. 568 It will save the tasks (downloaded via mozci) to the specified JSON file 569 (which may be used in a future ``--use-tasks`` option) 570 571 ``--use-tasks USE_TASKS`` --- Use tasks from file 572 ------------------------------------------------- 573 This feature is primarily for ``skip-fails`` development and debugging. 574 It will uses the tasks from the specified JSON file (instead of downloading them via mozci). 575 See also ``--save-tasks``. 576 577 ``--save-failures SAVE_FAILURES`` --- Save failures to file 578 ----------------------------------------------------------- 579 580 This feature is primarily for ``skip-fails`` development and debugging. 581 It will save the failures (calculated from the tasks) to the specified JSON file 582 (which may be used in a future ``--use-failures`` option) 583 584 ``--use-failures USE_FAILURES`` --- Use failures from file 585 ---------------------------------------------------------- 586 This feature is primarily for ``skip-fails`` development and debugging. 587 It will uses the failures from the specified JSON file (instead of downloading them via mozci). 588 See also ``--save-failures``. 589 590 ``--max-failures MAX_FAILURES`` --- Maximum number of failures to skip 591 ---------------------------------------------------------------------- 592 This feature is primarily for ``skip-fails`` development and debugging. 593 It will limit the number of failures that are skipped (default is -1 == no limit). 594 595 ``--verbose`` --- Verbose mode 596 ------------------------------ 597 Increase verbosity of output. 598 599 ``--dry-run`` --- Dry run 600 ------------------------- 601 In dry run mode, the manifest changes (and bugs top be filed) are determined, but not written. 602 603 604 Bugs 605 ```` 606 607 Please file any bugs or feature requests at 608 609 https://bugzilla.mozilla.org/enter_bug.cgi?product=Testing&component=ManifestParser 610 611 Or contact in #cia on irc.mozilla.org 612 613 Design Considerations 614 ````````````````````` 615 616 Contrary to some opinion, manifestparser.py and the associated ``*.toml`` 617 format were not magically plucked from the sky but were descended upon 618 through several design considerations. 619 620 * test manifests should be ordered. The current ``*.toml`` format supports 621 this (as did the ``*.ini`` format) 622 623 * the manifest format should be easily human readable/writable And 624 programmatically editable. While the ``*.ini`` format worked for a long 625 time the underspecified syntax made it difficult to reliably parse. 626 The new ``*.toml`` format is widely accepted, as a formal syntax as well 627 as libraries to read and edit it (e.g. ``tomlkit``). 628 629 * there should be a single file that may easily be 630 transported. Traditionally, test harnesses have lived in 631 mozilla-central. This is less true these days and it is increasingly 632 likely that more tests will not live in mozilla-central going 633 forward. So `manifestparser.py` should be highly consumable. To 634 this end, it is a single file, as appropriate to mozilla-central, 635 which is also a working python package deployed to PyPI for easy 636 installation. 637 638 Historical Reference 639 ```````````````````` 640 641 Date-ordered list of links about how manifests came to be where they are today:: 642 643 * https://wiki.mozilla.org/Auto-tools/Projects/UniversalManifest 644 * http://alice.nodelman.net/blog/post/2010/05/ 645 * http://alice.nodelman.net/blog/post/universal-manifest-for-unit-tests-a-proposal/ 646 * https://elvis314.wordpress.com/2010/07/05/improving-personal-hygiene-by-adjusting-mochitests/ 647 * https://elvis314.wordpress.com/2010/07/27/types-of-data-we-care-about-in-a-manifest/ 648 * https://bugzilla.mozilla.org/show_bug.cgi?id=585106 649 * http://elvis314.wordpress.com/2011/05/20/converting-xpcshell-from-listing-directories-to-a-manifest/ 650 * https://bugzilla.mozilla.org/show_bug.cgi?id=616999 651 * https://developer.mozilla.org/en/Writing_xpcshell-based_unit_tests#Adding_your_tests_to_the_xpcshell_manifest 652 * https://bugzilla.mozilla.org/show_bug.cgi?id=1821199