tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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